packages feed

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 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 @@+[![Hackage](https://img.shields.io/hackage/v/projectile.svg)](https://img.shields.io/hackage/v/projectile.svg)+[![Stackage LTS](http://stackage.org/package/projectile/badge/lts)](http://stackage.org/lts/package/projectile)+[![Stackage Nightly](http://stackage.org/package/projectile/badge/nightly)](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+[![Build Status](https://travis-ci.org/roman/Haskell-projectile}.svg?branch=master)](https://travis-ci.org/roman/Haskell-projectile)+[![Hackage Dependencies](https://img.shields.io/hackage-deps/v/projectile.svg)](https://http://packdeps.haskellers.com/feed?needle=projectile)+[![Github](https://img.shields.io/github/commits-since/roman/haskell-projectile/v0.0.0.1.svg)](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