packages feed

cabal-gild 1.3.0.1 → 1.3.1.0

raw patch · 5 files changed

+219/−40 lines, 5 filesdep +temporarydep ~directoryPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: temporary

Dependency ranges changed: directory

API changes (from Hackage documentation)

+ CabalGild.Unstable.Action.EvaluatePragmas: discover :: (MonadThrow m, MonadWalk m) => FilePath -> Name (p, [c]) -> [FieldLine (p, [c])] -> [String] -> MaybeT m (Field (p, [c]))
+ CabalGild.Unstable.Action.EvaluatePragmas: normalize :: FilePath -> FilePath
- CabalGild.Unstable.Action.EvaluatePragmas: field :: MonadWalk m => FilePath -> Field (p, [Comment q]) -> m (Field (p, [Comment q]))
+ CabalGild.Unstable.Action.EvaluatePragmas: field :: (MonadThrow m, MonadWalk m) => FilePath -> Field (p, [Comment q]) -> m (Field (p, [Comment q]))
- CabalGild.Unstable.Action.EvaluatePragmas: run :: MonadWalk m => FilePath -> ([Field (p, [Comment q])], cs) -> m ([Field (p, [Comment q])], cs)
+ CabalGild.Unstable.Action.EvaluatePragmas: run :: (MonadThrow m, MonadWalk m) => FilePath -> ([Field (p, [Comment q])], cs) -> m ([Field (p, [Comment q])], cs)
- CabalGild.Unstable.Type.Pragma: Discover :: NonEmpty FilePath -> Pragma
+ CabalGild.Unstable.Type.Pragma: Discover :: [String] -> Pragma

Files

README.md view
@@ -178,9 +178,10 @@ Each pragma starts with `-- cabal-gild:`. Pragmas must be the last comment before a field. -- `-- cabal-gild: discover DIRECTORY [DIRECTORY ...]`: This pragma will-  discover any Haskell files in any of the given directories and use those to-  populate the list of modules or signatures. For example, given this input:+- `-- cabal-gild: discover [DIRECTORY ...]`: This pragma will discover any+  Haskell files in any of the given directories and use those to populate the+  list of modules or signatures. If no directories are given, defaults to `.`+  (the current directory). For example, given this input:    ``` cabal   library@@ -207,3 +208,14 @@   This pragma searches for files with any of the following extensions: `*.chs`,   `*.cpphs`, `*.gc`, `*.hs`, `*.hsc`, `*.hsig`, `*.lhs`, `*.lhsig`, `*.ly`,   `*.x`, or `*.y`,++  Directories can be quoted if they contain spaces.++  Discovered modules can be ignored by using the `--exclude=FILE` option. For+  example:++  ``` cabal+  library+    -- cabal-gild: discover source/library --exclude=source/library/Foo/Bar.hs+    exposed-modules: ...+  ```
cabal-gild.cabal view
@@ -11,7 +11,7 @@ maintainer: Taylor Fausak name: cabal-gild synopsis: Formats package descriptions.-version: 1.3.0.1+version: 1.3.1.0  source-repository head   type: git@@ -146,9 +146,11 @@   build-depends:     bytestring,     containers,+    directory,     exceptions,     filepath,     hspec ^>=2.11.7,+    temporary ^>=1.3,     transformers,    hs-source-dirs: source/test-suite
source/library/CabalGild/Unstable/Action/EvaluatePragmas.hs view
@@ -1,6 +1,8 @@ module CabalGild.Unstable.Action.EvaluatePragmas where  import qualified CabalGild.Unstable.Class.MonadWalk as MonadWalk+import qualified CabalGild.Unstable.Exception.InvalidOption as InvalidOption+import qualified CabalGild.Unstable.Exception.UnknownOption as UnknownOption import qualified CabalGild.Unstable.Extra.FieldLine as FieldLine import qualified CabalGild.Unstable.Extra.ModuleName as ModuleName import qualified CabalGild.Unstable.Extra.Name as Name@@ -8,9 +10,9 @@ import qualified CabalGild.Unstable.Type.Comment as Comment import qualified CabalGild.Unstable.Type.Pragma as Pragma import qualified Control.Monad as Monad+import qualified Control.Monad.Catch as Exception import qualified Control.Monad.Trans.Class as Trans import qualified Control.Monad.Trans.Maybe as MaybeT-import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Maybe as Maybe import qualified Data.Set as Set import qualified Distribution.Compat.Lens as Lens@@ -18,12 +20,14 @@ import qualified Distribution.ModuleName as ModuleName import qualified Distribution.Parsec as Parsec import qualified Distribution.Utils.Generic as Utils+import qualified System.Console.GetOpt as GetOpt import qualified System.FilePath as FilePath+import qualified System.FilePath.Windows as FilePath.Windows  -- | High level wrapper around 'field' that makes this action easier to compose -- with other actions. run ::-  (MonadWalk.MonadWalk m) =>+  (Exception.MonadThrow m, MonadWalk.MonadWalk m) =>   FilePath ->   ([Fields.Field (p, [Comment.Comment q])], cs) ->   m ([Fields.Field (p, [Comment.Comment q])], cs)@@ -31,12 +35,8 @@  -- | Evaluates pragmas within the given field. Or, if the field is a section, -- evaluates pragmas recursively within the fields of the section.------ If modules are discovered for a field, that fields lines are completely--- replaced. If anything goes wrong while discovering modules, the original--- field is returned. field ::-  (MonadWalk.MonadWalk m) =>+  (Exception.MonadThrow m, MonadWalk.MonadWalk m) =>   FilePath ->   Fields.Field (p, [Comment.Comment q]) ->   m (Fields.Field (p, [Comment.Comment q]))@@ -46,29 +46,57 @@     comment <- hoistMaybe . Utils.safeLast . snd $ Name.annotation n     pragma <- hoistMaybe . Parsec.simpleParsecBS $ Comment.value comment     case pragma of-      Pragma.Discover ds -> do-        let root = FilePath.takeDirectory p-            directories =-              FilePath.dropTrailingPathSeparator-                . FilePath.normalise-                . FilePath.combine root-                <$> NonEmpty.toList ds-        files <- Trans.lift . fmap mconcat $ traverse MonadWalk.walk directories-        let comments = concatMap (snd . FieldLine.annotation) fls-            position =-              maybe (fst $ Name.annotation n) (fst . FieldLine.annotation) $-                Maybe.listToMaybe fls-            fieldLines =-              zipWith ModuleName.toFieldLine ((,) position <$> comments : repeat [])-                . Maybe.mapMaybe (toModuleName directories)-                $ Maybe.mapMaybe (stripAnyExtension extensions) files-            -- This isn't great, but the comments have to go /somewhere/.-            name =-              if null fieldLines-                then Lens.over (Name.annotationLens . Lens._2) (comments <>) n-                else n-        pure $ Fields.Field name fieldLines+      Pragma.Discover ds -> discover p n fls ds   Fields.Section n sas fs -> Fields.Section n sas <$> traverse (field p) fs++-- | If modules are discovered for a field, that fields lines are completely+-- replaced.+discover ::+  (Exception.MonadThrow m, MonadWalk.MonadWalk m) =>+  FilePath ->+  Fields.Name (p, [c]) ->+  [Fields.FieldLine (p, [c])] ->+  [String] ->+  MaybeT.MaybeT m (Fields.Field (p, [c]))+discover p n fls ds = do+  let (strs, args, opts, errs) =+        GetOpt.getOpt'+          GetOpt.Permute+          [ GetOpt.Option [] ["exclude"] (GetOpt.ReqArg id "FILE") ""+          ]+          ds+  mapM_ (Exception.throwM . UnknownOption.fromString) opts+  mapM_ (Exception.throwM . InvalidOption.fromString) errs+  let root = FilePath.takeDirectory p+      directories =+        FilePath.dropTrailingPathSeparator+          . normalize+          . FilePath.combine root+          <$> if null args then ["."] else args+  files <- Trans.lift . fmap mconcat $ traverse MonadWalk.walk directories+  let comments = concatMap (snd . FieldLine.annotation) fls+      position =+        maybe (fst $ Name.annotation n) (fst . FieldLine.annotation) $+          Maybe.listToMaybe fls+      excludedFiles = Set.fromList $ fmap normalize strs+      fieldLines =+        zipWith ModuleName.toFieldLine ((,) position <$> comments : repeat [])+          . Maybe.mapMaybe (toModuleName directories)+          . Maybe.mapMaybe (stripAnyExtension extensions)+          . filter (`Set.notMember` excludedFiles)+          $ fmap normalize files+      -- This isn't great, but the comments have to go /somewhere/.+      name =+        if null fieldLines+          then Lens.over (Name.annotationLens . Lens._2) (comments <>) n+          else n+  pure $ Fields.Field name fieldLines++normalize :: FilePath -> FilePath+normalize =+  FilePath.normalise+    . FilePath.joinPath+    . FilePath.Windows.splitDirectories  -- | These are the names of the fields that can have this action applied to -- them.
source/library/CabalGild/Unstable/Type/Pragma.hs view
@@ -1,15 +1,14 @@ module CabalGild.Unstable.Type.Pragma where  import qualified Control.Monad as Monad-import qualified Data.List.NonEmpty as NonEmpty import qualified Distribution.Compat.CharParsing as CharParsing import qualified Distribution.FieldGrammar.Newtypes as Newtypes import qualified Distribution.Parsec as Parsec  -- | A pragma, which is a special comment used to customize behavior. newtype Pragma-  = -- | Discover modules within the given directory.-    Discover (NonEmpty.NonEmpty FilePath)+  = -- | Discover modules using the given arguments.+    Discover [String]   deriving (Eq, Show)  instance Parsec.Parsec Pragma where@@ -18,7 +17,9 @@     Monad.void $ CharParsing.string "cabal-gild:"     CharParsing.spaces     Monad.void $ CharParsing.string "discover"-    CharParsing.skipSpaces1-    Discover-      . fmap Newtypes.getFilePathNT-      <$> CharParsing.sepByNonEmpty Parsec.parsec CharParsing.skipSpaces1+    arguments <-+      Monad.mplus+        (CharParsing.skipSpaces1 *> CharParsing.sepBy Parsec.parsec CharParsing.skipSpaces1)+        ([] <$ CharParsing.spaces)+    CharParsing.eof+    pure . Discover $ fmap Newtypes.getToken' arguments
source/test-suite/Main.hs view
@@ -1,3 +1,4 @@+{- hlint ignore "Redundant bracket" -} {-# LANGUAGE GeneralizedNewtypeDeriving #-}  import qualified CabalGild.Unstable.Class.MonadLog as MonadLog@@ -5,8 +6,11 @@ import qualified CabalGild.Unstable.Class.MonadWalk as MonadWalk import qualified CabalGild.Unstable.Class.MonadWrite as MonadWrite import qualified CabalGild.Unstable.Exception.CheckFailure as CheckFailure+import qualified CabalGild.Unstable.Exception.InvalidOption as InvalidOption import qualified CabalGild.Unstable.Exception.SpecifiedOutputWithCheckMode as SpecifiedOutputWithCheckMode import qualified CabalGild.Unstable.Exception.SpecifiedStdinWithFileInput as SpecifiedStdinWithFileInput+import qualified CabalGild.Unstable.Exception.UnexpectedArgument as UnexpectedArgument+import qualified CabalGild.Unstable.Exception.UnknownOption as UnknownOption import qualified CabalGild.Unstable.Extra.String as String import qualified CabalGild.Unstable.Main as Gild import qualified CabalGild.Unstable.Type.Input as Input@@ -21,8 +25,10 @@ import qualified Data.Map as Map import qualified Data.Maybe as Maybe import qualified GHC.Stack as Stack+import qualified System.Directory as Directory import qualified System.Exit as Exit import qualified System.FilePath as FilePath+import qualified System.IO.Temp as Temp import qualified Test.Hspec as Hspec  main :: IO ()@@ -39,6 +45,24 @@     w `Hspec.shouldNotBe` []     s `Hspec.shouldBe` Map.empty +  Hspec.it "fails with an unknown option" $ do+    let (a, s, w) = runGild ["--unknown"] [] []+    a `shouldBeFailure` UnknownOption.UnknownOption "--unknown"+    w `Hspec.shouldBe` []+    s `Hspec.shouldBe` Map.empty++  Hspec.it "fails with an invalid option" $ do+    let (a, s, w) = runGild ["--help=invalid"] [] []+    a `shouldBeFailure` InvalidOption.InvalidOption "option `--help' doesn't allow an argument"+    w `Hspec.shouldBe` []+    s `Hspec.shouldBe` Map.empty++  Hspec.it "fails with an unexpected argument" $ do+    let (a, s, w) = runGild ["unexpected"] [] []+    a `shouldBeFailure` UnexpectedArgument.UnexpectedArgument "unexpected"+    w `Hspec.shouldBe` []+    s `Hspec.shouldBe` Map.empty+   Hspec.it "reads from an input file" $ do     let (a, s, w) =           runGild@@ -1030,6 +1054,86 @@       "library\n -- cabal-gild: discover d e\n exposed-modules:"       "library\n  -- cabal-gild: discover d e\n  exposed-modules:\n    M\n    N\n" +  Hspec.it "discovers from a quoted directory" $ do+    expectDiscover+      [("d", ["M.hs"])]+      "library\n -- cabal-gild: discover \"d\"\n exposed-modules:"+      "library\n  -- cabal-gild: discover \"d\"\n  exposed-modules: M\n"++  Hspec.it "discovers from a directory with a space" $ do+    expectDiscover+      [("s p", ["M.hs"])]+      "library\n -- cabal-gild: discover \"s p\"\n exposed-modules:"+      "library\n  -- cabal-gild: discover \"s p\"\n  exposed-modules: M\n"++  Hspec.it "discovers from the current directory by default" $ do+    expectDiscover+      [(".", ["M.hs"])]+      "library\n -- cabal-gild: discover\n exposed-modules:"+      "library\n  -- cabal-gild: discover\n  exposed-modules: M\n"++  Hspec.it "allows excluding a path when discovering" $ do+    expectDiscover+      [(".", ["M.hs", "N.hs"])]+      "library\n -- cabal-gild: discover --exclude M.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude M.hs\n  exposed-modules: N\n"++  Hspec.it "allows excluding a nested POSIX path" $ do+    expectDiscover+      [(".", [FilePath.combine "A" "M.hs", FilePath.combine "B" "M.hs"])]+      "library\n -- cabal-gild: discover --exclude B/M.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude B/M.hs\n  exposed-modules: A.M\n"++  Hspec.it "allows excluding a nested Windows path" $ do+    expectDiscover+      [(".", [FilePath.combine "A" "M.hs", FilePath.combine "B" "M.hs"])]+      "library\n -- cabal-gild: discover --exclude B\\M.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude B\\M.hs\n  exposed-modules: A.M\n"++  Hspec.it "allows excluding a relative POSIX path" $ do+    expectDiscover+      [(".", ["M.hs", "N.hs"])]+      "library\n -- cabal-gild: discover --exclude ./M.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude ./M.hs\n  exposed-modules: N\n"++  Hspec.it "allows excluding a relative Windows path" $ do+    expectDiscover+      [(".", ["M.hs", "N.hs"])]+      "library\n -- cabal-gild: discover --exclude .\\M.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude .\\M.hs\n  exposed-modules: N\n"++  Hspec.it "allows excluding multiple paths" $ do+    expectDiscover+      [(".", ["M.hs", "N.hs", "O.hs"])]+      "library\n -- cabal-gild: discover --exclude M.hs --exclude O.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude M.hs --exclude O.hs\n  exposed-modules: N\n"++  Hspec.it "allows excluding paths that don't match anything" $ do+    expectDiscover+      [(".", ["M.hs"])]+      "library\n -- cabal-gild: discover --exclude N.hs\n exposed-modules:"+      "library\n  -- cabal-gild: discover --exclude N.hs\n  exposed-modules: M\n"++  Hspec.it "fails when discovering with an unknown option" $ do+    let (a, s, w) =+          runGild+            []+            [(Input.Stdin, String.toUtf8 "-- cabal-gild: discover --unknown\nsignatures:")]+            []+    a `shouldBeFailure` UnknownOption.UnknownOption "--unknown"+    w `Hspec.shouldBe` []+    s `Hspec.shouldBe` Map.empty++  Hspec.it "fails when discovering with an invalid option" $ do+    let (a, s, w) =+          runGild+            []+            [(Input.Stdin, String.toUtf8 "-- cabal-gild: discover --exclude\nsignatures:")]+            []+    a `shouldBeFailure` InvalidOption.InvalidOption "option `--exclude' requires an argument FILE"+    w `Hspec.shouldBe` []+    s `Hspec.shouldBe` Map.empty+   Hspec.it "retains comments when discovering" $ do     expectDiscover       [(".", ["M.hs"])]@@ -1128,6 +1232,38 @@     expectGilded       "f:\ng: a"       "f:\ng: a\n"++  Hspec.around_ withTemporaryDirectory+    . Hspec.it "discovers modules on the file system"+    $ do+      -- Although we already have pure tests for this behavior, it's important+      -- to ensure that both POSIX and Windows paths are supported on the real+      -- file system.+      writeFile "i.cabal" $+        unlines+          [ "library",+            "  -- cabal-gild: discover --exclude=.\\M2.hs --exclude=N/M1.hs",+            "  exposed-modules:"+          ]+      writeFile "M1.hs" ""+      writeFile "M2.hs" ""+      Directory.createDirectory "N"+      writeFile (FilePath.combine "N" "M1.hs") ""+      writeFile (FilePath.combine "N" "M2.hs") ""+      Gild.mainWith ["--input=i.cabal", "--output=o.cabal"]+      readFile "o.cabal"+        `Hspec.shouldReturn` unlines+          [ "library",+            "  -- cabal-gild: discover --exclude=.\\M2.hs --exclude=N/M1.hs",+            "  exposed-modules:",+            "    M1",+            "    N.M2"+          ]++withTemporaryDirectory :: IO () -> IO ()+withTemporaryDirectory =+  Temp.withSystemTempDirectory "cabal-gild"+    . flip Directory.withCurrentDirectory  shouldBeFailure ::   (Stack.HasCallStack, Eq e, Exception.Exception e, Show a) =>