projectile (empty) → 0.0.0.1
raw patch · 15 files changed
+420/−0 lines, 15 filesdep +basedep +deepseqdep +pathsetup-changed
Dependencies added: base, deepseq, path, path-io, projectile, protolude, safe-exceptions, tasty, tasty-hunit, tasty-rerun, text, vector
Files
- CHANGELOG.md +11/−0
- LICENSE +13/−0
- README.md +59/−0
- Setup.hs +2/−0
- projectile.cabal +77/−0
- src/Projectile.hs +183/−0
- test/DocTest.hs +7/−0
- test/TestSuite.hs +68/−0
- test/fixtures/recurring-mark-file/.svn/.keep +0/−0
- test/fixtures/recurring-mark-file/config/.svn/.keep +0/−0
- test/fixtures/recurring-mark-file/src/.svn/.keep +0/−0
- test/fixtures/recurring-mark-file/src/Foo/.svn/.keep +0/−0
- test/fixtures/recurring-mark-file/src/Foo/Bar/.svn/.keep +0/−0
- test/fixtures/recurring-mark-file/test/.svn/.keep +0/−0
- test/fixtures/top-mark-file/.projectile +0/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Change log++projectile uses [Semantic Versioning][1].+The change log is available [on GitHub][2].++[1]: http://semver.org/spec/v2.0.0.html+[2]: https://github.com/roman/projectile/releases++## v0.0.0.1++* Initially created.
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright (c) 2017, Roman Gonzalez++Permission to use, copy, modify, and/or distribute this software for any+purpose with or without fee is hereby granted, provided that the above+copyright notice and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ README.md view
@@ -0,0 +1,59 @@+[](https://img.shields.io/hackage/v/projectile.svg)+[](http://stackage.org/lts/package/projectile)+[](http://stackage.org/nightly/package/projectile)+# Projectile++> Utility functions to gather information about project directories++## Table Of Contents++* [Raison d'etre](#raison-detre)+* [Development](#development)+* [License](#license)++## Raison d'etre++Inspired by [bbatsov's](https://github.com/bbatsov/projectile) work, this library was created with+the purpose of reliably access to _project_ resources in a somewhat unified way when developing CLI+tools or [Turtle](https://github.com/Gabriel439/Haskell-Turtle-Library) scripts.++## Development+[](https://travis-ci.org/roman/Haskell-projectile)+[](https://http://packdeps.haskellers.com/feed?needle=projectile)+[](https://img.shields.io/github/commits-since/roman/haskell-projectile/v0.0.0.1.svg)++This library is intended to be minimal, providing a few functions that work+reliably among many different kind of projects. If you want to contribute,+Pull Request are very welcome! Please try to follow these simple rules:++* Please create a topic branch for every separate change you make.+* Update the README.md file if necessary.+* Please _do not_ change the version number.++### Open Commit Bit++projectile has an open commit bit policy: Anyone with an accepted pull request+gets added as a repository collaborator. Please try to follow these simple+rules:++* Commit directly onto the master branch only for typos, improvements to the+ README and documentation.+* Create a feature branch and open a pull-request early for any new features to+ get feedback.+* Make sure you adhere to the general pull request rules above.++## License++Copyright (c) 2017, Roman Gonzalez++Permission to use, copy, modify, and/or distribute this software for any+purpose with or without fee is hereby granted, provided that the above+copyright notice and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ projectile.cabal view
@@ -0,0 +1,77 @@+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack++name: projectile+version: 0.0.0.1+synopsis: Go to README.md+description: Utility functions to gather information about project directories+category: TODO+stability: alpha (experimental)+homepage: https://github.com/roman/Haskell-projectile#readme+bug-reports: https://github.com/roman/Haskell-projectile/issues+author: Roman Gonzalez+maintainer: romanandreg@gmail.com+copyright: © 2017 Roman Gonzalez+license: MIT+license-file: LICENSE+tested-with: GHC==7.10.3 GHC==8.0.1 GHC==8.0.2+build-type: Simple+cabal-version: >= 1.10++extra-source-files:+ CHANGELOG.md+ README.md+ test/fixtures/recurring-mark-file/.svn/.keep+ test/fixtures/recurring-mark-file/config/.svn/.keep+ test/fixtures/recurring-mark-file/src/.svn/.keep+ test/fixtures/recurring-mark-file/src/Foo/.svn/.keep+ test/fixtures/recurring-mark-file/src/Foo/Bar/.svn/.keep+ test/fixtures/recurring-mark-file/test/.svn/.keep+ test/fixtures/top-mark-file/.projectile++source-repository head+ type: git+ location: https://github.com/roman/Haskell-projectile++library+ hs-source-dirs:+ src+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates+ build-depends:+ base >=4.8 && <5.0+ , protolude >=0.1 && <0.2+ , text >=1.2 && <1.3+ , deepseq >=1.4 && <2.0+ , safe-exceptions >=0.1 && <0.2+ , path >=0.5 && <0.6+ , path-io >=1.1 && <1.3+ , vector >=0.11 && <1.0+ exposed-modules:+ Projectile+ other-modules:+ Paths_projectile+ default-language: Haskell2010++test-suite projectile-test+ type: exitcode-stdio-1.0+ main-is: TestSuite.hs+ hs-source-dirs:+ test+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.8 && <5.0+ , protolude >=0.1 && <0.2+ , text >=1.2 && <1.3+ , deepseq >=1.4 && <2.0+ , safe-exceptions >=0.1 && <0.2+ , path >=0.5 && <0.6+ , path-io >=1.1 && <1.3+ , vector >=0.11 && <1.0+ , tasty >=0.11 && <0.12+ , tasty-hunit >=0.9 && <0.10+ , tasty-rerun >=1.1 && <1.2+ , projectile+ other-modules:+ DocTest+ default-language: Haskell2010
+ src/Projectile.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-+Module: Projectile++This module provides utility functions to gather various paths of a project+-}+module Projectile (getProjectRootDir) where++import Protolude hiding (catch, (<>))++import Control.Exception.Safe (MonadCatch, MonadThrow, catch, throwM)+import Data.Monoid ((<>))+import Data.Vector (Vector)+import Path (Abs, Dir, Path, Rel, parent, parseRelFile, (</>))+import Path.IO (isLocationOccupied)++import qualified Data.Vector as V++--------------------------------------------------------------------------------++data ProjectileException+ -- | Error thrown when calling project function from+ -- a path that does not not belong to any project+ = ProjectRootNotFound+ deriving (Generic, NFData, Show, Eq)++instance Exception ProjectileException++data WalkAction+ = WalkFinish+ | WalkContinue+ | WalkInvalid+ deriving (Generic, NFData, Show, Eq)++--------------------------------------------------------------------------------++isRoot :: Path Abs Dir -> Bool+isRoot path =+ parent path == path++-- | A list of files considered to mark the root of a project. The top-most+-- match has precedence.+projectRootTopLangMarkFiles :: Vector FilePath+projectRootTopLangMarkFiles =+ V.fromList+ [+ "rebar.config" -- Rebar project file+ , "project.clj" -- Leiningen project file+ , "build.boot" -- Boot-clj project file+ , "SConstruct" -- Scons project file+ , "pom.xml" -- Maven project file+ , "build.sbt" -- SBT project file+ , "gradlew" -- Gradle wrapper script+ , "build.gradle" -- Gradle project file+ , ".ensime" -- Ensime configuration file+ , "Gemfile" -- Bundler file+ , "requirements.txt" -- Pip file+ , "setup.py" -- Setuptools file+ , "tox.ini" -- Tox file+ , "composer.json" -- Composer project file+ , "Cargo.toml" -- Cargo project file+ , "mix.exs" -- Elixir mix project file+ , "stack.yaml" -- Haskell's stack tool based project+ , "stack.yml" -- Haskell's stack tool based project+ , "info.rkt" -- Racket package description file+ , "DESCRIPTION" -- R package description file+ , "TAGS" -- etags/ctags are usually in the root of project+ , "GTAGS" -- GNU Global tags+ ]++-- | A list of files considered to mark the root of a project. The top-most+-- match has precedence.+projectRootTopMarkFiles :: Vector FilePath+projectRootTopMarkFiles =+ V.fromList+ [+ ".projectile" -- projectile project marker+ , ".git" -- Git VCS root dir+ , ".hg" -- Mercurial VCS root dir+ , ".fslckout" -- Fossil VCS root dir+ , "_FOSSIL_" -- Fossil VCS root DB on Windows+ , ".bzr" -- Bazaar VCS root dir+ , "_darcs" -- Darcs VCS root dir+ ]++-- | A list of files considered to mark the root of a project. This+-- file must be in all sub-directories of a project.+projectRecurringMarkFiles :: Vector FilePath+projectRecurringMarkFiles =+ V.fromList+ [+ ".svn" -- Svn VCS root dir+ , "CVS" -- Csv VCS root dir+ , "Makefile"+ ]++-- | Iterate from the current path to parent paths until the match function+-- returns a value for finishing the traversal+locateDominatingFile+ :: (MonadIO m, MonadThrow m)+ => Path Abs Dir -- ^ Directory to start from+ -> (Path Abs Dir -> m WalkAction) -- ^ Match function that will return what to do next on the iteration+ -> m (Path Abs Dir) -- ^ The path where the match function returns @WalkFinish@+locateDominatingFile dir continueP+ | isRoot dir =+ throwM ProjectRootNotFound++ | otherwise = do+ walkNext <- continueP dir+ case walkNext of+ WalkInvalid ->+ throwM ProjectRootNotFound+ WalkFinish ->+ return dir+ WalkContinue ->+ locateDominatingFile (parent dir) continueP++-- | Returns a @WalkAction@ that indicates a finish of iteration when any of the+-- given relative paths is contained on the given directory+doesContainAny+ :: MonadIO m+ => Vector (Path Rel t) -- ^ Relative path that should be contained in directory input+ -> Path b Dir -- ^ Directory path that should contain any of the relative paths+ -> m WalkAction+doesContainAny files dir = do+ matchesAnyFile <-+ (not . V.null . V.dropWhile not)+ <$> V.mapM (\file -> isLocationOccupied (dir </> file)) files++ if matchesAnyFile then+ return WalkFinish+ else+ return WalkContinue++getDirWithRootProjectFile+ :: (MonadIO m, MonadThrow m)+ => Path Abs Dir+ -> m (Path Abs Dir)+getDirWithRootProjectFile currentDir = do+ files <-+ mapM parseRelFile (projectRootTopMarkFiles+ <> projectRootTopLangMarkFiles)+ locateDominatingFile currentDir (doesContainAny files)++getDirWithRecurringProjectFile+ :: (MonadIO m, MonadThrow m)+ => Path Abs Dir+ -> m (Path Abs Dir)+getDirWithRecurringProjectFile currentDir =+ let+ parentDoesNotContainOneOf files dir = do+ fileLocated <- doesContainAny files dir+ if fileLocated == WalkFinish then do+ -- return check of parent directory not containing one of the+ -- recurring project files+ parentContains <- doesContainAny files (parent dir)+ if parentContains == WalkFinish then+ return WalkContinue+ else+ return WalkFinish+ -- if the path doesn't contain the recurring marking file, then+ -- this is not a project that can be recognizable+ else+ return WalkInvalid++ in do+ files <- mapM parseRelFile projectRecurringMarkFiles+ locateDominatingFile currentDir (parentDoesNotContainOneOf files)++-- | Retrieves the root of the current project if available.+-- A @ProjectRootNotFound@ error is returned otherwise.+--+getProjectRootDir+ :: (MonadCatch m, MonadIO m)+ => Path Abs Dir -- ^ Directory from where to look the root of the project+ -> m (Path Abs Dir) -- ^ Root of the project directory+getProjectRootDir dir =+ catch (getDirWithRecurringProjectFile dir)+ (\(_ :: ProjectileException) -> getDirWithRootProjectFile dir)
+ test/DocTest.hs view
@@ -0,0 +1,7 @@+module Main where++import System.FilePath.Glob+import Test.DocTest++main :: IO ()+main = glob "src/**/*.hs" >>= doctest
+ test/TestSuite.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module Main where++import Protolude++import qualified Data.Text as T++import Path (reldir, toFilePath, (</>))+import Path.IO (getCurrentDir, listDirRecur)++import Test.Tasty (TestTree, defaultMainWithIngredients, testGroup)+import Test.Tasty.HUnit (assertEqual, testCase)+import Test.Tasty.Ingredients.Rerun (rerunningTests)+import Test.Tasty.Runners (consoleTestReporter, listingTests)++import Projectile (getProjectRootDir)++main :: IO ()+main =+ defaultMainWithIngredients+ [ rerunningTests [listingTests, consoleTestReporter] ]+ (testGroup "all-tests" tests)++tests :: [TestTree]+tests =+ [+ testGroup "getProjectRootDir" getProjectRootDirTests+ ]++-- scTests :: [TestTree]+-- scTests =+-- [ testProperty "inc == succ" prop_succ+-- , testProperty "inc . negate == negate . pred" prop_pred+-- ]++getProjectRootDirTests :: [TestTree]+getProjectRootDirTests =+ [+ testCase "every sub-folder file marker" $ do+ root <- getCurrentDir+ let+ rootDirProject =+ root </> [reldir|test/fixtures/recurring-mark-file|]++ (dirs, _files) <- listDirRecur rootDirProject+ forM_ dirs $ \dir ->+ unless (".svn/" `T.isSuffixOf` T.pack (toFilePath dir)) $ do+ rootDirProject' <- getProjectRootDir dir+ assertEqual ("doesn't return the root of the project: " <> show dir)+ rootDirProject+ rootDirProject'++ , testCase "top level folder marker" $ do+ root <- getCurrentDir+ let+ rootDirProject =+ root </> [reldir|test/fixtures/top-mark-file|]++ (dirs, _files) <- listDirRecur rootDirProject+ forM_ dirs $ \dir -> do+ rootDirProject' <- getProjectRootDir dir+ assertEqual ("doesn't return the root of the project: " <> show dir)+ rootDirProject+ rootDirProject'++ ]
+ test/fixtures/recurring-mark-file/.svn/.keep view
+ test/fixtures/recurring-mark-file/config/.svn/.keep view
+ test/fixtures/recurring-mark-file/src/.svn/.keep view
+ test/fixtures/recurring-mark-file/src/Foo/.svn/.keep view
+ test/fixtures/recurring-mark-file/src/Foo/Bar/.svn/.keep view
+ test/fixtures/recurring-mark-file/test/.svn/.keep view
+ test/fixtures/top-mark-file/.projectile view