packages feed

module-management 0.9.3.1 → 0.10

raw patch · 14 files changed

+717/−259 lines, 14 filesdep +Extradep +ansi-wl-pprintdep +debiandep ~basedep ~set-extranew-component:exe:hmmnew-component:exe:testsbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: Extra, ansi-wl-pprint, debian, module-management, process-progress, regex-compat

Dependency ranges changed: base, set-extra

API changes (from Hackage documentation)

+ Language.Haskell.Modules.Fold: instance Show St
+ Language.Haskell.Modules.Fold: type ModuleInfo = (Module, String, [Comment])
+ Language.Haskell.Modules.Fold: type ModuleMap = Map ModuleName ModuleInfo
+ Language.Haskell.Modules.Util.SrcLoc: instance HasSpanInfo SrcSpan
+ Language.Haskell.Modules.Util.SrcLoc: srcPairTextPair :: SrcLoc -> SrcLoc -> String -> (String, String)
- Language.Haskell.Modules.Fold: foldDecls :: Show r => (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> Module -> String -> r -> r
+ Language.Haskell.Modules.Fold: foldDecls :: Show r => (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r
- Language.Haskell.Modules.Fold: foldExports :: Show r => (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> Module -> String -> r -> r
+ Language.Haskell.Modules.Fold: foldExports :: Show r => (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r
- Language.Haskell.Modules.Fold: foldHeader :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> Module -> String -> r -> r
+ Language.Haskell.Modules.Fold: foldHeader :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> ModuleInfo -> r -> r
- Language.Haskell.Modules.Fold: foldImports :: Show r => (ImportDecl -> String -> String -> String -> r -> r) -> Module -> String -> r -> r
+ Language.Haskell.Modules.Fold: foldImports :: Show r => (ImportDecl -> String -> String -> String -> r -> r) -> ModuleInfo -> r -> r
- Language.Haskell.Modules.Fold: foldModule :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ImportDecl -> String -> String -> String -> r -> r) -> (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> Module -> String -> r -> r
+ Language.Haskell.Modules.Fold: foldModule :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ImportDecl -> String -> String -> String -> r -> r) -> (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r

Files

Language/Haskell/Modules.hs view
@@ -34,15 +34,19 @@ -- -- * 'mergeModules' ----- * 'runMonadClean'+-- * 'runMonadClean' - Sets up the environment for splitting and merging --+-- * 'Language.Haskell.Modules.Params' - Functions to control modes of operation+-- -- Examples: ----- * Clean up the import lists of all the modules under @./Language@:+-- * Use @cleanImports@ to clean up the import lists of all the modules under @./Language@: -- --    @findPaths \"Language\" >>= runMonadClean . mapM cleanImports . toList@ ----- * Split up the module Common and then merge two of the pieces back in:+-- * Use @splitModule@ to split up module+--   @Language.Haskell.Modules.Common@, and then merge two of the pieces+--   back in. -- --   @findModules \"Language\" >>= \\ modules -> runMonadClean $ --      let mn = Language.Haskell.Exts.Syntax.ModuleName in@@ -65,7 +69,8 @@ import Language.Haskell.Modules.Fold (echo, echo2, foldDecls, foldExports, foldHeader, foldImports, foldModule, ignore, ignore2) import Language.Haskell.Modules.Imports (cleanImports) import Language.Haskell.Modules.Merge (mergeModules)-import Language.Haskell.Modules.Params (modifyDryRun, modifyExtensions, modifyHsFlags, modifyModuVerse, modifyRemoveEmptyImports, modifySourceDirs, modifyTestMode, runMonadClean)+import Language.Haskell.Modules.Params (runMonadClean, modifyDryRun, modifyExtensions, modifyHsFlags, modifyModuVerse,+                                        modifyRemoveEmptyImports, modifySourceDirs, modifyTestMode) import Language.Haskell.Modules.Split (splitModule) import Language.Haskell.Modules.Util.QIO (noisily, quietly) import Language.Haskell.Modules.Util.Test (findModules, findPaths)
Language/Haskell/Modules/Fold.hs view
@@ -2,7 +2,9 @@ {-# LANGUAGE BangPatterns, CPP, FlexibleInstances, ScopedTypeVariables #-} {-# OPTIONS_GHC -Wall #-} module Language.Haskell.Modules.Fold-    ( foldModule+    ( ModuleInfo+    , ModuleMap+    , foldModule     , foldHeader     , foldExports     , foldImports@@ -14,20 +16,26 @@     , tests     ) where +import Control.Applicative ((<$>))+import Control.Monad (when) import Control.Monad.State (get, put, runState, State) import Control.Monad.Trans (liftIO) import Data.Char (isSpace) import Data.Default (Default(def)) import Data.List (tails)+import Data.Map (Map)+import Data.Maybe (mapMaybe) import Data.Monoid ((<>), Monoid) import Data.Set.Extra as Set (fromList) import Data.Tree (Tree(..)) import Language.Haskell.Exts.Annotated (ParseResult(..)) import qualified Language.Haskell.Exts.Annotated.Syntax as A (Decl, ExportSpec, ExportSpec(..), ExportSpecList(ExportSpecList), ImportDecl, Module(..), ModuleHead(..), ModuleName, ModulePragma, WarningText)+import Language.Haskell.Exts.Comments (Comment(..)) import Language.Haskell.Exts.SrcLoc (SrcLoc(..), SrcSpan(..), SrcSpanInfo(..))+import qualified Language.Haskell.Exts.Syntax as S (ModuleName) import Language.Haskell.Modules.Common (withCurrentDirectory)-import Language.Haskell.Modules.Internal (parseFile, runMonadClean)-import Language.Haskell.Modules.Util.SrcLoc (endLoc, HasSpanInfo(..), increaseSrcLoc, makeTree, srcLoc, srcPairTextHead, srcPairTextTail)+import Language.Haskell.Modules.Internal (parseFileWithComments, runMonadClean)+import Language.Haskell.Modules.Util.SrcLoc (endLoc, HasSpanInfo(..), increaseSrcLoc, makeTree, srcLoc, srcPairTextHead, srcPairTextTail, srcPairTextPair) import Test.HUnit (assertEqual, Test(TestList, TestCase, TestLabel))  type Module = A.Module SrcSpanInfo@@ -60,13 +68,97 @@ instance Spans (A.ModuleName SrcSpanInfo) where spans x = [fixSpan $ spanInfo x] instance Spans (A.WarningText SrcSpanInfo) where spans x = [fixSpan $ spanInfo x] --- This happens.  Is it a bug in haskell-src-exts?+type ModuleInfo = (Module, String, [Comment])+type ModuleMap = Map S.ModuleName ModuleInfo++-- This happens, a span with end column 0, even though column+-- numbering begins at 1.  Is it a bug in haskell-src-exts? fixSpan :: SrcSpanInfo -> SrcSpanInfo fixSpan sp =     if srcSpanEndColumn (srcInfoSpan sp) == 0     then sp {srcInfoSpan = (srcInfoSpan sp) {srcSpanEndColumn = 1}}     else sp +data St+    = St { loc_ :: SrcLoc+         , text_ :: String+         , comms_ :: [Comment]+         , sps_ :: [SrcSpanInfo] }+      deriving (Show)++setSpanEnd :: SrcLoc -> SrcSpan -> SrcSpan+setSpanEnd loc sp = sp {srcSpanEndLine = srcLine loc, srcSpanEndColumn = srcColumn loc}+setSpanStart :: SrcLoc -> SrcSpan -> SrcSpan+setSpanStart loc sp = sp {srcSpanStartLine = srcLine loc, srcSpanStartColumn = srcColumn loc}++-- | The spans returned by haskell-src-exts may put comments and+-- whitespace in the suffix string of a declaration, we want them in+-- the prefix string of the following declaration where possible.+adjustSpans :: String -> [Comment] -> [SrcSpanInfo] -> [SrcSpanInfo]+adjustSpans _ _ [] = []+adjustSpans _ _ [x] = [x]+adjustSpans text comments sps =+    fst $ runState f (St def text comments sps)+    where+      f = do st <- get+             let b = loc_ st+             case sps_ st of+               (ss1 : ssis) ->+                   do skip+                      st' <- get+                      let e = loc_ st'+                      case e >= endLoc ss1 of+                        True ->+                            -- We reached the end of ss1, so the segment from b to e is+                            -- trailing comments and space, some of which may belong in+                            -- the following span.+                            do put (st' {sps_ = ssis})+                               sps' <- f+                               let ss1' = ss1 {srcInfoSpan = setSpanEnd b (srcInfoSpan ss1)}+                               return (ss1' : sps')+                        False ->+                           -- If we weren't able to skip to the end of+                           -- the span, we encountered real text.+                           -- Move past one char and try again.+                           do case text_ st' of+                                "" -> return (sps_ st') -- error $ "Ran out of text\n st=" ++ show st ++ "\n st'=" ++ show st'+                                (c : t') -> do put (st' {text_ = t', loc_ = increaseSrcLoc [c] e})+                                               f+               sss -> return sss++      -- loc is the current position in the input file, text+      -- is the text starting at that location.+      skip :: State St ()+      skip = do loc1 <- loc_ <$> get+                skipWhite+                skipComment+                loc2 <- loc_ <$> get+                -- Repeat until failure+                when (loc1 /= loc2) skip++      skipWhite :: State St ()+      skipWhite = do st <- get+                     case span isSpace (text_ st) of+                       ("", _) -> return ()+                       (space, t') ->+                           let loc' = increaseSrcLoc space (loc_ st) in+                           put (st {loc_ = loc', text_ = t'})++      skipComment :: State St ()+      skipComment = do st <- get+                       case comms_ st of+                         (Comment _ csp _ : cs)+                             | srcLoc csp <= loc_ st ->+                                 -- We reached the comment, skip past it and discard+                                 case srcPairTextPair (loc_ st) (endLoc csp) (text_ st) of+                                   ("", _) -> return ()+                                   (comm, t') ->+                                       let loc' = increaseSrcLoc comm (loc_ st) in+                                       put (st {loc_ = loc',+                                                text_ = t',+                                                comms_ = cs})+                         _ -> return () -- No comments, or we didn't reach it+ -- | Given the result of parseModuleWithComments and the original -- module text, this does a fold over the parsed module contents, -- calling the seven argument functions in order.  Each function is@@ -87,92 +179,96 @@            -> (ImportDecl -> String -> String -> String -> r -> r) -- ^ Called with each import declarator            -> (Decl -> String -> String -> String -> r -> r) -- ^ Called with each top level declaration            -> (String -> r -> r) -- ^ Called with comments following the last declaration-           -> Module -- ^ Parsed module-           -> String -- ^ Original text file+           -> ModuleInfo -- ^ Parsed module            -> r -- ^ Fold initialization value            -> r -- ^ Result-foldModule _ _ _ _ _ _ _ _ _ _ (A.XmlPage _ _ _ _ _ _ _) _ _ = error "XmlPage: unsupported"-foldModule _ _ _ _ _ _ _ _ _ _ (A.XmlHybrid _ _ _ _ _ _ _ _ _) _ _ = error "XmlHybrid: unsupported"-foldModule topf pragmaf namef warnf pref exportf postf importf declf sepf m@(A.Module _ mh ps is ds) text r0 =-    fst $ runState (doModule r0) (text, def, spans m)+foldModule _ _ _ _ _ _ _ _ _ _ (A.XmlPage _ _ _ _ _ _ _, _, _) _ = error "XmlPage: unsupported"+foldModule _ _ _ _ _ _ _ _ _ _ (A.XmlHybrid _ _ _ _ _ _ _ _ _, _, _) _ = error "XmlHybrid: unsupported"+foldModule topf pragmaf namef warnf pref exportf postf importf declf sepf (m@(A.Module _ mh ps is ds), text, comments) r0 =+    (\ (_, (_, _, _, r)) -> r) $ runState doModule (text, def, spans m, r0)     where-      doModule r =-          doSep topf r >>=-          doList pragmaf ps >>=-          maybe return doHeader mh >>=-          doList importf is >>=-          doList declf ds >>=-          doTail sepf-      doHeader (A.ModuleHead sp n mw me) r =-          doItem namef n r >>=-          maybe return (doItem warnf) mw >>=-          doSep pref >>=-          maybe return (\ (A.ExportSpecList _ es) -> doList exportf es) me >>=-          doClose postf sp-      doClose f sp r =-          do (tl, l, sps) <- get+      doModule =+          do doSep topf+             doList pragmaf ps+             maybe (return ()) doHeader mh+             doList importf is+             (tl, l, sps, r) <- get+             put (tl, l, adjustSpans text comments sps, r)+             doList declf ds+             doTail sepf+      doHeader (A.ModuleHead sp n mw me) =+          do doItem namef n+             maybe (return ()) (doItem warnf) mw+             doSep pref+             maybe (return ()) (\ (A.ExportSpecList _ es) -> doList exportf es) me+             doClose postf sp+      doClose f sp =+          do (tl, l, sps, r) <- get              case l < endLoc sp of-               True -> do put (srcPairTextTail l (endLoc sp) tl, endLoc sp, sps)-                          return (f (srcPairTextHead l (endLoc sp) tl) r)-               False -> return r-      doTail f r =-          do (tl, _, _) <- get-             return $ f tl r-      doSep :: (String -> r -> r) -> r -> State (String, SrcLoc, [SrcSpanInfo]) r-      doSep f r =+               True -> put (srcPairTextTail l (endLoc sp) tl,+                            endLoc sp,+                            sps,+                            f (srcPairTextHead l (endLoc sp) tl) r)+               False -> return ()+      doTail f =+          do (tl, l, sps, r) <- get+             put (tl, l, sps, f tl r)+      doSep :: (String -> r -> r) -> State (String, SrcLoc, [SrcSpanInfo], r) ()+      doSep f =           do p <- get              case p of-               (tl, l, sps@(sp : _)) ->+               (tl, l, sps@(sp : _), r) ->                    do let l' = srcLoc sp                       case l <= l' of                         True ->-                            do put (srcPairTextTail l l' tl, l', sps)-                               return $ f (srcPairTextHead l l' tl) r-                        False -> return r+                            do put (srcPairTextTail l l' tl,+                                    l',+                                    sps,+                                    f (srcPairTextHead l l' tl) r)+                        False -> return ()                _ -> error $ "foldModule - out of spans: " ++ show p-      doList :: (HasSpanInfo a, Show a) => (a -> String -> String -> String -> r -> r) -> [a] -> r -> State (String, SrcLoc, [SrcSpanInfo]) r-      doList _ [] r = return r-      doList f (x : xs) r = doItem f x r >>= doList f xs+      doList :: (HasSpanInfo a, Show a) => (a -> String -> String -> String -> r -> r) -> [a] -> State (String, SrcLoc, [SrcSpanInfo], r) ()+      doList _ [] = return ()+      doList f (x : xs) = doItem f x >> doList f xs -      doItem :: (HasSpanInfo a, Show a) => (a -> String -> String -> String -> r -> r) -> a -> r -> State (String, SrcLoc, [SrcSpanInfo]) r-      doItem f x r =-          do (tl, l, (sp : sps')) <- get+      doItem :: (HasSpanInfo a, Show a) => (a -> String -> String -> String -> r -> r) -> a -> State (String, SrcLoc, [SrcSpanInfo], r) ()+      doItem f x =+          do (tl, l, (sp : sps'), r) <- get              let -- Another haskell-src-exts bug?  If a module ends                  -- with no newline, endLoc will be at the beginning                  -- of the following (nonexistant) line.-                 l' = endLoc sp                  pre = srcPairTextHead l (srcLoc sp) tl                  tl' = srcPairTextTail l (srcLoc sp) tl+                 l' = endLoc sp                  s = srcPairTextHead (srcLoc sp) l' tl'                  tl'' = srcPairTextTail (srcLoc sp) l' tl'-                 l'' = adjust tl'' l'+                 l'' = adjust1 tl'' l'                  post = srcPairTextHead l' l'' tl''                  tl''' = srcPairTextTail l' l'' tl''-             put (tl''', l'', sps')-             return $ f x pre s post r+             put (tl''', l'', sps', f x pre s post r) -      -- Move to just past the first newline in the leading whitespace+      -- Move to just past the last newline in the leading whitespace       -- adjust "\n  \n  hello\n" (SrcLoc "<unknown>.hs" 5 5) ->-      --   (SrcLoc "<unknown>.hs" 6 1)-      _adjust2 :: String -> SrcLoc -> SrcLoc-      _adjust2 a l =+      --   (SrcLoc "<unknown>.hs" 7 1)+      _adjust :: String -> SrcLoc -> SrcLoc+      _adjust a l =           l'           where             w = takeWhile isSpace a-            w' = case span (/= '\n') w of-                   (w'', '\n' : _) -> w'' ++ ['\n']-                   (w'', "") -> w''+            w' = take (length (takeWhile (elem '\n') (tails w))) w             l' = increaseSrcLoc w' l -      -- Move to just past the last newline in the leading whitespace+      -- Move to just past the first newline in the leading whitespace       -- adjust "\n  \n  hello\n" (SrcLoc "<unknown>.hs" 5 5) ->-      --   (SrcLoc "<unknown>.hs" 7 1)-      adjust :: String -> SrcLoc -> SrcLoc-      adjust a l =+      --   (SrcLoc "<unknown>.hs" 6 1)+      adjust1 :: String -> SrcLoc -> SrcLoc+      adjust1 a l =           l'           where             w = takeWhile isSpace a-            w' = take (length (takeWhile (elem '\n') (tails w))) w+            w' = case span (/= '\n') w of+                   (w'', '\n' : _) -> w'' ++ ['\n']+                   (w'', "") -> w''             l' = increaseSrcLoc w' l  -- | Do just the header portion of 'foldModule'.@@ -181,33 +277,33 @@            -> (ModulePragma -> String -> String -> String -> r -> r)            -> (ModuleName -> String -> String -> String -> r -> r)            -> (WarningText -> String -> String -> String -> r -> r)-           -> Module -> String -> r -> r-foldHeader topf pragmaf namef warnf m text r0 =-    foldModule topf pragmaf namef warnf ignore2 ignore ignore2 ignore ignore ignore2 m text r0+           -> ModuleInfo -> r -> r+foldHeader topf pragmaf namef warnf m r0 =+    foldModule topf pragmaf namef warnf ignore2 ignore ignore2 ignore ignore ignore2 m r0  -- | Do just the exports portion of 'foldModule'. foldExports :: forall r. (Show r) =>                (String -> r -> r)             -> (ExportSpec -> String -> String -> String -> r -> r)             -> (String -> r -> r)-            -> Module -> String -> r -> r-foldExports pref exportf postf m text r0 =-    foldModule ignore2 ignore ignore ignore pref exportf postf ignore ignore ignore2 m text r0+            -> ModuleInfo -> r -> r+foldExports pref exportf postf m r0 =+    foldModule ignore2 ignore ignore ignore pref exportf postf ignore ignore ignore2 m r0  -- | Do just the imports portion of 'foldModule'. foldImports :: forall r. (Show r) =>                (ImportDecl -> String -> String -> String -> r -> r)-            -> Module -> String -> r -> r-foldImports importf m text r0 =-    foldModule ignore2 ignore ignore ignore ignore2 ignore ignore2 importf ignore ignore2 m text r0+            -> ModuleInfo -> r -> r+foldImports importf m r0 =+    foldModule ignore2 ignore ignore ignore ignore2 ignore ignore2 importf ignore ignore2 m r0  -- | Do just the declarations portion of 'foldModule'. foldDecls :: forall r. (Show r) =>              (Decl -> String -> String -> String -> r -> r)           -> (String -> r -> r)-          -> Module -> String -> r -> r-foldDecls declf sepf m text r0 =-    foldModule ignore2 ignore ignore ignore ignore2 ignore ignore2 ignore declf sepf m text r0+          -> ModuleInfo -> r -> r+foldDecls declf sepf m r0 =+    foldModule ignore2 ignore ignore ignore ignore2 ignore ignore2 ignore declf sepf m r0  -- | This can be passed to foldModule to include the original text in the result echo :: Monoid m => t -> m -> m -> m -> m -> m@@ -226,31 +322,32 @@ ignore2 _ r = r  tests :: Test-tests = TestLabel "Clean" (TestList [test1, test1b, test3, test4, test6])+tests = TestLabel "Clean" (TestList [test1, test1b, test3, test4, test5, test5b, test6])  test1 :: Test test1 =     TestLabel "test1" $ TestCase $ withCurrentDirectory "testdata/original" $     do let path = "Debian/Repo/Orphans.hs"        text <- liftIO $ readFile path-       ParseOk m <- runMonadClean $ parseFile path-       let (output, original) = test m text+       ParseOk (m, comments) <- runMonadClean $ parseFileWithComments path+       let (output, original) = test (m, text, comments)        assertEqual "echo" original output     where-      test :: Module -> String -> (String, String)-      test m text = (foldModule echo2 echo echo echo echo2 echo echo2 echo echo echo2 m text "", text)+      test :: ModuleInfo -> (String, String)+      test m@(_, text, _) = (foldModule echo2 echo echo echo echo2 echo echo2 echo echo echo2 m "", text)  test1b :: Test test1b =     TestLabel "test1b" $ TestCase $ withCurrentDirectory "testdata/original" $     do let path = "Debian/Repo/Sync.hs"        text <- liftIO $ readFile path-       ParseOk m <- runMonadClean $ parseFile path-       let output = test m text-       assertEqual "echo"-                   [("-- Comment above module head\nmodule ","","",""),-                    ("","Debian.Repo.Sync","","[2.8:2.24]"),-                    (" ","{-# WARNING \"this is a warning\" #-}","\n","[2.25:2.60]"),+       ParseOk (m, comments) <- runMonadClean $ parseFileWithComments path+       let output = test (m, text, comments)+       assertEqual "echo" [] (mapMaybe (\ (a, b) -> if a == b then Nothing else Just (a, b)) (zip expected output))+    where+      expected =   [("-- Comment above module head\nmodule ","","",""),+                    ("","Debian.Repo.Sync"," ","[2.8:2.24]"),+                    ("","{-# WARNING \"this is a warning\" #-}","\n","[2.25:2.60]"),                     ("    ( ","","",""),                     ("","rsync","\n","[3.7:3.12]"),                     ("    , ","foo","\n","[4.7:4.10]"),@@ -260,17 +357,15 @@                     ("","import System.Exit (ExitCode)","\n","[12.1:12.30]"),                     ("","import System.FilePath (dropTrailingPathSeparator)","\n","[13.1:13.51]"),                     ("-- Comment between two imporrts\n","import System.Process (proc)","\n","[15.1:15.29]"),-                    ("","import System.Process.Progress (keepResult, runProcessF)","\n\n","[16.1:16.57]"),-                    ("-- Comment before first decl\n","rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode","\n","[19.1:19.82]"),-                    ("","rsync extra source dest =\n    do result <- runProcessF (proc \"rsync\" ([\"-aHxSpDt\", \"--delete\"] ++ extra ++\n                                            [dropTrailingPathSeparator source ++ \"/\",\n                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult\n       case result of\n         [x] -> return x\n         _ -> error \"Missing or multiple exit codes\"\n\n-- Comment between two decls\n","","[20.1:29.0]"),-                    ("","foo :: Int","\n","[29.1:29.11]"),-                    ("","foo = 1","\n\n","[30.1:30.8]"),-                    ("{-\nhandleExit 1 = \"Syntax or usage error\"\nhandleExit 2 = \"Protocol incompatibility\"\nhandleExit 3 = \"Errors selecting input/output files, dirs\"\nhandleExit 4 = \"Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server.\"\nhandleExit 5 = \"Error starting client-server protocol\"\nhandleExit 6 = \"Daemon unable to append to log-file\"\nhandleExit 10 = \"Error in socket I/O\"\nhandleExit 11 = \"Error in file I/O\"\nhandleExit 12 = \"Error in rsync protocol data stream\"\nhandleExit 13 = \"Errors with program diagnostics\"\nhandleExit 14 = \"Error in IPC code\"\nhandleExit 20 = \"Received SIGUSR1 or SIGINT\"\nhandleExit 21 = \"Some error returned by waitpid()\"\nhandleExit 22 = \"Error allocating core memory buffers\"\nhandleExit 23 = \"Partial transfer due to error\"\nhandleExit 24 = \"Partial transfer due to vanished source files\"\nhandleExit 25 = \"The --max-delete limit stopped deletions\"\nhandleExit 30 = \"Timeout in data send/receive\"\nhandleExit 35 = \"Timeout waiting for daemon connection\"\n-}\n","","","")]-                   output-    where-      test :: Module -> String -> [(String, String, String, String)]-      test m text =-          foldModule tailf pragmaf namef warningf tailf exportf tailf importf declf tailf m text []+                    ("","import System.Process.Progress (keepResult, runProcessF)","\n","[16.1:16.57]"),+                    ("\n-- Comment before first decl\n","rsync :: (Functor m, MonadIO m) => [String] -> FilePath -> FilePath -> m ExitCode","\n","[19.1:19.82]"),+                    ("","rsync extra source dest =\n    do result <- runProcessF (proc \"rsync\" ([\"-aHxSpDt\", \"--delete\"] ++ extra ++\n                                            [dropTrailingPathSeparator source ++ \"/\",\n                                             dropTrailingPathSeparator dest])) B.empty >>= return . keepResult\n       case result of\n         [x] -> return x\n         _ -> error \"Missing or multiple exit codes\"","\n","[20.1:29.0]"),+                    ("\n-- Comment between two decls\n","foo :: Int","\n","[29.1:29.11]"),+                    ("","foo = 1","\n","[30.1:30.8]"),+                    ("\n{-\nhandleExit 1 = \"Syntax or usage error\"\nhandleExit 2 = \"Protocol incompatibility\"\nhandleExit 3 = \"Errors selecting input/output files, dirs\"\nhandleExit 4 = \"Requested action not supported: an attempt was made to manipulate 64-bit files on a platform that cannot support them; or an option was specified that is supported by the client and not by the server.\"\nhandleExit 5 = \"Error starting client-server protocol\"\nhandleExit 6 = \"Daemon unable to append to log-file\"\nhandleExit 10 = \"Error in socket I/O\"\nhandleExit 11 = \"Error in file I/O\"\nhandleExit 12 = \"Error in rsync protocol data stream\"\nhandleExit 13 = \"Errors with program diagnostics\"\nhandleExit 14 = \"Error in IPC code\"\nhandleExit 20 = \"Received SIGUSR1 or SIGINT\"\nhandleExit 21 = \"Some error returned by waitpid()\"\nhandleExit 22 = \"Error allocating core memory buffers\"\nhandleExit 23 = \"Partial transfer due to error\"\nhandleExit 24 = \"Partial transfer due to vanished source files\"\nhandleExit 25 = \"The --max-delete limit stopped deletions\"\nhandleExit 30 = \"Timeout in data send/receive\"\nhandleExit 35 = \"Timeout waiting for daemon connection\"\n-}\n","","","")]+      test :: ModuleInfo -> [(String, String, String, String)]+      test m =+          foldModule tailf pragmaf namef warningf tailf exportf tailf importf declf tailf m []           where             pragmaf :: ModulePragma -> String -> String -> String -> [(String, String, String, String)] -> [(String, String, String, String)]             pragmaf x pref s suff r = r ++ [(pref, s, suff,int (spanInfo x))]@@ -295,12 +390,61 @@     TestLabel "test3" $ TestCase $ withCurrentDirectory "testdata" $     do let path = "Equal.hs"        text <- liftIO $ readFile path-       ParseOk m <- runMonadClean $ parseFile path-       let (output, original) = test m text+       ParseOk (m, comments) <- runMonadClean $ parseFileWithComments path+       let (output, original) = test (m, text, comments)        assertEqual "echo" original output     where-      test :: Module -> String -> (String, String)-      test m text = (foldModule echo2 echo echo echo echo2 echo echo2 echo echo echo2 m text "", text)+      test :: ModuleInfo -> (String, String)+      test m@(_, text, _) = (foldModule echo2 echo echo echo echo2 echo echo2 echo echo echo2 m "", text)++test5 :: Test+test5 =+    TestLabel "test5" $ TestCase $+    do let path = "testdata/test5.hs" -- "testdata/logic/Data/Logic/Classes/Literal.hs"+       text <- liftIO $ readFile path+       ParseOk (m, comments) <- runMonadClean $ parseFileWithComments path+       -- let actual = map f (adjustSpans text comments (spans m))+       -- assertEqual "spans" original actual+       let actual = foldDecls (\ _ a b c r -> r ++ [(a, b, c)]) (\ s r -> r ++ [("", s, "")]) (m, text, comments) []+       assertEqual "spans" expected actual+    where+      expected = [("","a = 1 where","\n"),+                  ("\n{- This makes bad things happen. -}\n\n","b = ()","\n"),+                  ("","","")]++test5b :: Test+test5b =+    TestLabel "test5b" $ TestCase $+    do let path = "testdata/logic/Data/Logic/Classes/Literal.hs"+       text <- liftIO $ readFile path+       ParseOk (m, comments) <- runMonadClean $ parseFileWithComments path+       let actual = foldDecls (\ _ a b c r -> r ++ [(a, b, c)]) (\ s r -> r ++ [("", s, "")]) (m, text, comments) []+       assertEqual "spans" expected actual+    where+      expected = [("\n-- |Literals are the building blocks of the clause and implicative normal\n-- |forms.  They support negation and must include True and False elements.\n",+                   "class (Negatable lit, Constants lit, HasFixity atom, Formula lit atom, Ord lit) => Literal lit atom | lit -> atom where\n    foldLiteral :: (lit -> r) -> (Bool -> r) -> (atom -> r) -> lit -> r",+                   "\n"),+                  ("\n","zipLiterals :: Literal lit atom =>\n               (lit -> lit -> Maybe r)\n            -> (Bool -> Bool -> Maybe r)\n            -> (atom -> atom -> Maybe r)\n            -> lit -> lit -> Maybe r","\n"),+                  ("",+                   "zipLiterals neg tf at fm1 fm2 =\n    foldLiteral neg' tf' at' fm1\n    where\n      neg' p1 = foldLiteral (neg p1) (\\ _ -> Nothing) (\\ _ -> Nothing) fm2\n      tf' x1 = foldLiteral (\\ _ -> Nothing) (tf x1) (\\ _ -> Nothing) fm2\n      at' a1 = foldLiteral (\\ _ -> Nothing) (\\ _ -> Nothing) (at a1) fm2",+                   "\n"),+                  ("\n{- This makes bad things happen.\n-- | We can use an fof type as a lit, but it must not use some constructs.\ninstance FirstOrderFormula fof atom v => Literal fof atom v where\n    foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm\n        where qu = error \"instance Literal FirstOrderFormula\"\n              co ((:~:) x) = neg x\n              co _ = error \"instance Literal FirstOrderFormula\"\n    atomic = Data.Logic.Classes.FirstOrder.atomic\n-}\n\n-- |Just like Logic.FirstOrder.convertFOF except it rejects anything\n-- with a construct unsupported in a normal logic formula,\n-- i.e. quantifiers and formula combinators other than negation.\n",+                   "fromFirstOrder :: forall formula atom v lit atom2.\n                  (Formula lit atom2, FOF.FirstOrderFormula formula atom v, Literal lit atom2) =>\n                  (atom -> atom2) -> formula -> Failing lit",+                   "\n"),+                  ("",+                   "fromFirstOrder ca formula =\n    FOF.foldFirstOrder (\\ _ _ _ -> Failure [\"fromFirstOrder\"]) co (Success . fromBool) (Success . atomic . ca) formula\n    where\n      co :: Combination formula -> Failing lit\n      co ((:~:) f) =  fromFirstOrder ca f >>= return . (.~.)\n      co _ = Failure [\"fromFirstOrder\"]",+                   "\n"),+                  ("\n","fromLiteral :: forall lit atom v fof atom2. (Literal lit atom, FOF.FirstOrderFormula fof atom2 v) =>\n               (atom -> atom2) -> lit -> fof","\n"),+                  ("","fromLiteral ca lit = foldLiteral (\\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit","\n"),+                  ("\n","toPropositional :: forall lit atom pf atom2. (Literal lit atom, P.PropositionalFormula pf atom2) =>\n                   (atom -> atom2) -> lit -> pf","\n"),+                  ("","toPropositional ca lit = foldLiteral (\\ p -> (.~.) (toPropositional ca p)) fromBool (atomic . ca) lit","\n"),+                  ("\n{-\nprettyLit :: forall lit atom term v p f. (Literal lit atom v, Apply atom p term, Term term v f) =>\n              (v -> Doc)\n           -> (p -> Doc)\n           -> (f -> Doc)\n           -> Int\n           -> lit\n           -> Doc\nprettyLit pv pp pf _prec lit =\n    foldLiteral neg tf at lit\n    where\n      neg :: lit -> Doc\n      neg x = if negated x then text {-\"\172\"-} \"~\" <> prettyLit pv pp pf 5 x else prettyLit pv pp pf 5 x\n      tf = text . ifElse \"true\" \"false\"\n      at = foldApply (\\ pr ts -> \n                        pp pr <> case ts of\n                                   [] -> empty\n                                   _ -> parens (hcat (intersperse (text \",\") (map (prettyTerm pv pf) ts))))\n                   (\\ x -> text $ if x then \"true\" else \"false\")\n      -- parensIf False = id\n      -- parensIf _ = parens . nest 1\n-}\n\n","prettyLit :: forall lit atom v. (Literal lit atom) =>\n              (Int -> atom -> Doc)\n           -> (v -> Doc)\n           -> Int\n           -> lit\n           -> Doc","\n"),+                  ("","prettyLit pa pv pprec lit =\n    parensIf (pprec > prec) $ foldLiteral co tf at lit\n    where\n      co :: lit -> Doc\n      co x = if negated x then text {-\"\172\"-} \"~\" <> prettyLit pa pv 5 x else prettyLit pa pv 5 x\n      tf x = text (if x then \"true\" else \"false\")\n      at = pa 6\n      parensIf False = id\n      parensIf _ = parens . nest 1\n      Fixity prec _ = fixityLiteral lit","\n"),+                  ("\n","fixityLiteral :: (Literal formula atom) => formula -> Fixity","\n"),+                  ("","fixityLiteral formula =\n    foldLiteral neg tf at formula\n    where\n      neg _ = Fixity 5 InfixN\n      tf _ = Fixity 10 InfixN\n      at = fixity","\n"),+                  ("\n","foldAtomsLiteral :: Literal lit atom => (r -> atom -> r) -> r -> lit -> r","\n"),+                  ("","foldAtomsLiteral f i lit = foldLiteral (foldAtomsLiteral f i) (const i) (f i) lit","\n"),+                  ("","","")]  test6 :: Test test6 = TestCase (assertEqual "tree1"
Language/Haskell/Modules/Imports.hs view
@@ -24,8 +24,9 @@ import Language.Haskell.Exts.SrcLoc (SrcSpanInfo) import qualified Language.Haskell.Exts.Syntax as S (ImportDecl(importLoc, importModule, importSpecs), ModuleName(..), Name(..)) import Language.Haskell.Modules.Common (modulePathBase, withCurrentDirectory)-import Language.Haskell.Modules.Fold (foldDecls, foldExports, foldHeader, foldImports)-import Language.Haskell.Modules.Internal (getParams, markForDelete, modifyParams, ModuleResult(..), MonadClean, Params(..), parseFile, runMonadClean, scratchDir)+import Language.Haskell.Modules.Fold (ModuleInfo, foldDecls, foldExports, foldHeader, foldImports)+import Language.Haskell.Modules.Internal (getParams, markForDelete, modifyParams, ModuleResult(..), MonadClean, Params(..), parseFile, parseFileWithComments, runMonadClean, scratchDir)+import Language.Haskell.Modules.Params (modifyTestMode) import Language.Haskell.Modules.Util.DryIO (replaceFile, tildeBackup) import Language.Haskell.Modules.Util.QIO (qPutStrLn, quietly) import Language.Haskell.Modules.Util.Symbols (symbols)@@ -62,16 +63,17 @@ -- | Clean up the imports of a source file. cleanImports :: MonadClean m => FilePath -> m ModuleResult cleanImports path =-    do source <- parseFile path+    do text <- liftIO $ readFile path+       source <- parseFileWithComments path        case source of-         ParseOk (m@(A.Module _ h _ imports _decls)) ->+         ParseOk (m@(A.Module _ h _ imports _decls), comments) ->              do let name = case h of                              Just (A.ModuleHead _ x _ _) -> sModuleName x                              _ -> S.ModuleName "Main"                     hiddenImports = filter isHiddenImport imports-                dumpImports path >> checkImports path name m hiddenImports-         ParseOk (A.XmlPage {}) -> error "cleanImports: XmlPage"-         ParseOk (A.XmlHybrid {}) -> error "cleanImports: XmlHybrid"+                dumpImports path >> checkImports path name (m, text, comments) hiddenImports+         ParseOk (A.XmlPage {}, _) -> error "cleanImports: XmlPage"+         ParseOk (A.XmlHybrid {}, _) -> error "cleanImports: XmlHybrid"          ParseFailed _loc msg -> error ("cleanImports: - parse of " ++ path ++ " failed: " ++ msg)     where       isHiddenImport (A.ImportDecl {A.importSpecs = Just (A.ImportSpecList _ True _)}) = True@@ -97,7 +99,7 @@ -- source file.  We also need to modify the imports of any names -- that are types that appear in standalone instance derivations so -- their members are imported too.-checkImports :: MonadClean m => FilePath -> S.ModuleName -> A.Module SrcSpanInfo -> [A.ImportDecl SrcSpanInfo] -> m ModuleResult+checkImports :: MonadClean m => FilePath -> S.ModuleName -> ModuleInfo -> [A.ImportDecl SrcSpanInfo] -> m ModuleResult checkImports path name@(S.ModuleName name') m extraImports =     do let importsPath = name' <.> ".imports"        markForDelete importsPath@@ -112,37 +114,36 @@  -- | If all the parsing went well and the new imports differ from the -- old, update the source file with the new imports.-updateSource :: MonadClean m => FilePath -> A.Module SrcSpanInfo -> A.Module SrcSpanInfo -> S.ModuleName -> [A.ImportDecl SrcSpanInfo] -> m ModuleResult-updateSource path (m@(A.Module _ _ _ oldImports _)) (A.Module _ _ _ newImports _) name extraImports =+updateSource :: MonadClean m => FilePath -> ModuleInfo -> A.Module SrcSpanInfo -> S.ModuleName -> [A.ImportDecl SrcSpanInfo] -> m ModuleResult+updateSource path m@(A.Module _ _ _ oldImports _, _, _) (A.Module _ _ _ newImports _) name extraImports =     do remove <- removeEmptyImports <$> getParams-       text <- liftIO $ readFile path        maybe (qPutStrLn ("cleanImports: no changes to " ++ path) >> return (Unchanged name))              (\ text' ->                   qPutStrLn ("cleanImports: modifying " ++ path) >>                   replaceFile tildeBackup path text' >>                   return (Modified name text'))-             (replaceImports (fixNewImports remove m oldImports (newImports ++ extraImports)) m text)+             (replaceImports (fixNewImports remove m oldImports (newImports ++ extraImports)) m) updateSource _ _ _ _ _ = error "updateSource"  -- | Compare the old and new import sets and if they differ clip out -- the imports from the sourceText and insert the new ones.-replaceImports :: [A.ImportDecl SrcSpanInfo] -> A.Module SrcSpanInfo -> String -> Maybe String-replaceImports newImports m sourceText =-    let oldPretty = foldImports (\ _ pref s suff r -> r <> pref <> s <> suff) m sourceText ""+replaceImports :: [A.ImportDecl SrcSpanInfo] -> ModuleInfo -> Maybe String+replaceImports newImports m =+    let oldPretty = foldImports (\ _ pref s suff r -> r <> pref <> s <> suff) m ""         -- Surround newPretty with the same prefix and suffix as oldPretty-        newPretty = fromMaybe "" (foldImports (\ _ pref _ _ r -> maybe (Just pref) Just r) m sourceText Nothing) <>+        newPretty = fromMaybe "" (foldImports (\ _ pref _ _ r -> maybe (Just pref) Just r) m Nothing) <>                     intercalate "\n" (map (prettyPrintWithMode (defaultMode {layout = PPInLine})) newImports) <>-                    foldImports (\ _ _ _ suff _ -> suff) m sourceText "" in+                    foldImports (\ _ _ _ suff _ -> suff) m "" in     if oldPretty == newPretty     then Nothing-    else Just (foldHeader (\ s r -> r <> s) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ _ pref s suff r -> r <> pref <> s <> suff) m sourceText "" ++-               foldExports (\ s r -> r <> s) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ s r -> r <> s) m sourceText "" +++    else Just (foldHeader (\ s r -> r <> s) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ _ pref s suff r -> r <> pref <> s <> suff) m "" +++               foldExports (\ s r -> r <> s) (\ _ pref s suff r -> r <> pref <> s <> suff) (\ s r -> r <> s) m "" ++                newPretty <>-               foldDecls  (\ _ pref s suff r -> r <> pref <> s <> suff) (\ r s -> r <> s) m sourceText "")+               foldDecls  (\ _ pref s suff r -> r <> pref <> s <> suff) (\ r s -> s <> r) m "")  -- | Final touch-ups - sort and merge similar imports. fixNewImports :: Bool         -- ^ If true, imports that turn into empty lists will be removed-              -> A.Module SrcSpanInfo+              -> ModuleInfo               -> [A.ImportDecl SrcSpanInfo]               -> [A.ImportDecl SrcSpanInfo]               -> [A.ImportDecl SrcSpanInfo]@@ -194,10 +195,10 @@       sdTypes :: Set (Maybe S.ModuleName, S.Name)       sdTypes = standaloneDerivingTypes m -standaloneDerivingTypes :: A.Module SrcSpanInfo -> Set (Maybe S.ModuleName, S.Name)-standaloneDerivingTypes (A.XmlPage _ _ _ _ _ _ _) = error "standaloneDerivingTypes A.XmlPage"-standaloneDerivingTypes (A.XmlHybrid _ _ _ _ _ _ _ _ _) = error "standaloneDerivingTypes A.XmlHybrid"-standaloneDerivingTypes (A.Module _ _ _ _ decls) =+standaloneDerivingTypes :: ModuleInfo -> Set (Maybe S.ModuleName, S.Name)+standaloneDerivingTypes (A.XmlPage _ _ _ _ _ _ _, _, _) = error "standaloneDerivingTypes A.XmlPage"+standaloneDerivingTypes (A.XmlHybrid _ _ _ _ _ _ _ _ _, _, _) = error "standaloneDerivingTypes A.XmlHybrid"+standaloneDerivingTypes (A.Module _ _ _ _ decls, _, _) =     unions (map derivDeclTypes decls)     where       -- derivDeclTypes :: Decl -> Set (Maybe S.ModuleName, S.Name)@@ -270,7 +271,7 @@ nameString (S.Symbol s) = s  tests :: Test-tests = TestLabel "Clean" (TestList [test1, test2, test3, test4, test5])+tests = TestLabel "Clean" (TestList [test1, test2, test3, test4, test5, test6])  test1 :: Test test1 =@@ -350,3 +351,14 @@                          " deriving instance Show Paragraph"]),                        "")                       (code, unlines (drop 2 (lines diff)), err))++-- | Comment at EOF+test6 :: Test+test6 =+    TestLabel "Imports.test6" $ TestCase+      (do -- _ <- system "rsync -aHxS --delete testdata/logic/ testdata/copy"+          _ <- system "cp testdata/EndCommentOrig.hs testdata/EndComment.hs"+          let path = "EndComment.hs" -- "Data/Logic/Harrison/Tableaux.hs"+          _ <- withCurrentDirectory "testdata" (runMonadClean (modifyTestMode (const True) >> cleanImports "EndComment.hs"))+          (code, diff, err) <- readProcessWithExitCode "diff" ["-ru", "testdata/EndCommentClean.hs", "testdata" </> "EndComment.hs"] ""+          assertEqual "comment at end" "" diff)
Language/Haskell/Modules/Internal.hs view
@@ -99,7 +99,7 @@     withTempDirectory "." "scratch" $ \ scratch ->     do (result, params) <- runStateT action (Params {scratchDir = scratch,                                                      dryRun = False,-                                                     verbosity = 0,+                                                     verbosity = 1,                                                      hsFlags = [],                                                      extensions = Exts.extensions Exts.defaultParseMode,                                                      sourceDirs = ["."],@@ -175,7 +175,7 @@  doResult x@(Modified name text) =     do path <- modulePath name-       qPutStr ("catModules: modifying " ++ show path)+       qPutStr ("modifying " ++ show path)        quietly (qPutStr (" new text: " ++ show text))        qPutStr "\n"        replaceFile tildeBackup path text@@ -183,7 +183,7 @@  doResult x@(Created name text) =     do path <- modulePath name-       qPutStr ("catModules: creating " ++ show path)+       qPutStr ("creating " ++ show path)        quietly (qPutStr (" containing " ++ show text))        qPutStr "\n"        createDirectoryIfMissing True (takeDirectory . dropExtension $ path)
Language/Haskell/Modules/Merge.hs view
@@ -19,14 +19,15 @@ import Data.Set.Extra as Set (mapM) import Language.Haskell.Exts.Annotated.Simplify (sDecl, sExportSpec, sImportDecl, sModuleName) import qualified Language.Haskell.Exts.Annotated.Syntax as A (ExportSpecList(ExportSpecList), ImportDecl(..), Module(Module), ModuleHead(ModuleHead))+import Language.Haskell.Exts.Comments (Comment) import Language.Haskell.Exts.Parser (fromParseResult) import Language.Haskell.Exts.Pretty (prettyPrint) import Language.Haskell.Exts.SrcLoc (SrcSpanInfo) import qualified Language.Haskell.Exts.Syntax as S (ExportSpec(EModuleContents), ImportDecl(..), ModuleName(..)) import Language.Haskell.Modules.Common (withCurrentDirectory)-import Language.Haskell.Modules.Fold (echo, echo2, foldDecls, foldExports, foldHeader, foldImports, ignore, ignore2)+import Language.Haskell.Modules.Fold (ModuleInfo, echo, echo2, foldDecls, foldExports, foldHeader, foldImports, ignore, ignore2) import Language.Haskell.Modules.Imports (cleanImports)-import Language.Haskell.Modules.Internal (doResult, getParams, modifyParams, modulePath, ModuleResult(..), MonadClean, Params(sourceDirs, moduVerse, testMode), parseFile, runMonadClean)+import Language.Haskell.Modules.Internal (doResult, getParams, modifyParams, modulePath, ModuleResult(..), MonadClean, Params(sourceDirs, moduVerse, testMode), parseFileWithComments, runMonadClean) import Language.Haskell.Modules.Util.Test (diff, repoModules) import System.Cmd (system) import System.Exit (ExitCode(ExitSuccess))@@ -58,42 +59,42 @@ -- The output module may not (yet) be an element of the moduVerse, in -- that case choose the first input modules to convert into the output -- module.-doModule :: MonadClean m => Map S.ModuleName (A.Module SrcSpanInfo, String) -> [S.ModuleName] -> S.ModuleName -> S.ModuleName -> m ModuleResult+doModule :: MonadClean m => Map S.ModuleName (A.Module SrcSpanInfo, String, [Comment]) -> [S.ModuleName] -> S.ModuleName -> S.ModuleName -> m ModuleResult doModule inputInfo inputs@(first : _) output name =     do -- The new module will be based on the existing module, unless        -- name equals output and output does not exist        let oldName = if name == output && not (Map.member name inputInfo) then first else name-       (m, text) <- maybe (loadModule oldName) return (Map.lookup name inputInfo)+       (m, text, comments) <- maybe (loadModule oldName) return (Map.lookup name inputInfo)        return $ case () of-         _ | name == output -> Modified name (doOutput inputInfo inputs output (m, text))+         _ | name == output -> Modified name (doOutput inputInfo inputs output (m, text, comments))            | elem name inputs -> Removed name-         _ -> let text' = doOther inputs output (m, text) in+         _ -> let text' = doOther inputs output (m, text, comments) in               if text /= text' then Modified name text' else Unchanged name doModule _ [] _ _ = error "doModule: no inputs"  -- | Create the output module, the destination of the merge.-doOutput :: Map S.ModuleName (A.Module SrcSpanInfo, String) -> [S.ModuleName] -> S.ModuleName -> (A.Module SrcSpanInfo, String) -> String-doOutput inputInfo inNames outName (m, text) =+doOutput :: Map S.ModuleName ModuleInfo -> [S.ModuleName] -> S.ModuleName -> ModuleInfo -> String+doOutput inputInfo inNames outName m =     header ++ exports ++ imports ++ decls     where-      header = foldHeader echo2 echo (\ _ pref _ suff r -> r <> pref <> prettyPrint outName <> suff) echo m text "" <>-               foldExports (\ s r -> r <> s <> maybe "" (intercalate ", " . List.map (prettyPrint)) (mergeExports inputInfo outName) <> "\n") ignore ignore2 m text ""-      exports = fromMaybe "" (foldExports ignore2 (\ _e pref _ _ r -> maybe (Just pref) Just r) ignore2 m text Nothing)-      imports = foldExports ignore2 ignore (\ s r -> r <> s {-where-}) m text "" <>+      header = foldHeader echo2 echo (\ _ pref _ suff r -> r <> pref <> prettyPrint outName <> suff) echo m "" <>+               foldExports (\ s r -> r <> s <> maybe "" (intercalate ", " . List.map (prettyPrint)) (mergeExports inputInfo outName) <> "\n") ignore ignore2 m ""+      exports = fromMaybe "" (foldExports ignore2 (\ _e pref _ _ r -> maybe (Just pref) Just r) ignore2 m Nothing)+      imports = foldExports ignore2 ignore (\ s r -> r <> s {-where-}) m "" <>                 -- Insert the new imports just after the first "pre" string of the imports-                (fromMaybe "" (foldImports (\ _ pref _ _ r -> maybe (Just (pref <> unlines (List.map (moduleImports inputInfo) inNames))) Just r) m text Nothing)) <>-                (foldImports (\ _i pref s suff r -> r <> pref <> s <> suff) m text "")-      decls = fromMaybe "" (foldDecls (\ _d _ _ _ r -> Just (fromMaybe (unlines (List.map (moduleDecls inputInfo outName) inNames)) r)) (\ s r -> Just (maybe s (<> s) r)) m text Nothing)+                (fromMaybe "" (foldImports (\ _ pref _ _ r -> maybe (Just (pref <> unlines (List.map (moduleImports inputInfo) inNames))) Just r) m Nothing)) <>+                (foldImports (\ _i pref s suff r -> r <> pref <> s <> suff) m "")+      decls = fromMaybe "" (foldDecls (\ _d _ _ _ r -> Just (fromMaybe (unlines (List.map (moduleDecls inputInfo outName) inNames)) r)) (\ s r -> Just (maybe s (<> s) r)) m Nothing)  -- | Update a module that does not participate in the merge - this -- involves changing imports and exports of merged modules. -- (Shouldn't this also fix qualified symbols?)-doOther :: [S.ModuleName] -> S.ModuleName -> (A.Module SrcSpanInfo, String) -> String-doOther inputs output (m, text) =-    foldHeader echo2 echo echo echo m text "" <>-    foldExports echo2 (\ x pref s suff r -> r <> pref <> fromMaybe s (fixModuleExport inputs output (sExportSpec x)) <> suff) echo2 m text "" <>-    foldImports (\ x pref s suff r -> r <> pref <> fromMaybe s (fixModuleImport inputs output (sImportDecl x)) <> suff) m text "" <>-    foldDecls echo echo2 m text ""+doOther :: [S.ModuleName] -> S.ModuleName -> ModuleInfo -> String+doOther inputs output m =+    foldHeader echo2 echo echo echo m "" <>+    foldExports echo2 (\ x pref s suff r -> r <> pref <> fromMaybe s (fixModuleExport inputs output (sExportSpec x)) <> suff) echo2 m "" <>+    foldImports (\ x pref s suff r -> r <> pref <> fromMaybe s (fixModuleImport inputs output (sImportDecl x)) <> suff) m "" <>+    foldDecls echo echo2 m ""  fixModuleExport :: [S.ModuleName] -> S.ModuleName -> S.ExportSpec -> Maybe String fixModuleExport inputs output x =@@ -109,16 +110,16 @@                 | elem y inputs -> Just (prettyPrint (x {S.importModule = output}))             _ -> Nothing -mergeExports :: Map S.ModuleName (A.Module SrcSpanInfo, String) -> S.ModuleName -> Maybe [S.ExportSpec]+mergeExports :: Map S.ModuleName (A.Module SrcSpanInfo, String, [Comment]) -> S.ModuleName -> Maybe [S.ExportSpec] mergeExports old new =     Just (concatMap mergeExports' (Map.toAscList old))     where-      mergeExports' (_, (A.Module _ Nothing _ _ _, _)) = error "mergeModules: no explicit export list"-      mergeExports' (_, (A.Module _ (Just (A.ModuleHead _ _ _ Nothing)) _ _ _, _)) = error "mergeModules: no explicit export list"-      mergeExports' (_, (A.Module _ (Just (A.ModuleHead _ _ _ (Just (A.ExportSpecList _ e)))) _ _ _, _)) = updateModuleContentsExports old new (List.map sExportSpec e)+      mergeExports' (_, (A.Module _ Nothing _ _ _, _, _)) = error "mergeModules: no explicit export list"+      mergeExports' (_, (A.Module _ (Just (A.ModuleHead _ _ _ Nothing)) _ _ _, _, _)) = error "mergeModules: no explicit export list"+      mergeExports' (_, (A.Module _ (Just (A.ModuleHead _ _ _ (Just (A.ExportSpecList _ e)))) _ _ _, _, _)) = updateModuleContentsExports old new (List.map sExportSpec e)       mergeExports' (_, _) = error "mergeExports'" -updateModuleContentsExports :: Map S.ModuleName (A.Module SrcSpanInfo, String) -> S.ModuleName -> [S.ExportSpec] -> [S.ExportSpec]+updateModuleContentsExports :: Map S.ModuleName (A.Module SrcSpanInfo, String, [Comment]) -> S.ModuleName -> [S.ExportSpec] -> [S.ExportSpec] updateModuleContentsExports old new es =     foldl f [] es     where@@ -128,15 +129,15 @@           ys ++ if elem e' ys then [] else [e']       f ys e = ys ++ [e] -moduleImports :: Map S.ModuleName (A.Module SrcSpanInfo, String) -> S.ModuleName -> String+moduleImports :: Map S.ModuleName ModuleInfo -> S.ModuleName -> String moduleImports old name =-    let (Just (m, text)) = Map.lookup name old in+    let (Just m) = Map.lookup name old in     foldImports (\ x pref s suff r ->                     r                     -- If this is the first import, omit the prefix, it includes the ") where" text.                     <> (if r == "" then "" else pref)                     <> if Map.member (sModuleName (A.importModule x)) old then "" else (s <> suff))-                m text "" <> "\n"+                m "" <> "\n"  -- | Grab the declarations out of the old modules, fix any -- qualified symbol references, prettyprint and return.@@ -148,17 +149,17 @@ -- In terms of what is going on right here, if m imports any of the -- modules in oldmap with an "as" qualifier, identifiers using the -- module name in the "as" qualifier must use new instead.-moduleDecls :: Map S.ModuleName (A.Module SrcSpanInfo, String) -> S.ModuleName -> S.ModuleName -> String+moduleDecls :: Map S.ModuleName ModuleInfo -> S.ModuleName -> S.ModuleName -> String moduleDecls oldmap new name =-    let (Just (m@(A.Module _ _ _ imports _), text)) = Map.lookup name oldmap in+    let (Just m@(A.Module _ _ _ imports _, _, _)) = Map.lookup name oldmap in     let oldmap' = foldr f oldmap imports in     foldDecls (\ d pref s suff r ->                     let d' = sDecl d                         d'' = fixReferences oldmap' new d' in                     r <> pref <>-                    (if d'' /= d' then "-- Declaration reformatted because module qualifiers changed\n" <> prettyPrint d'' <> "\n\n" else (s <> suff)))+                    (if d'' /= d' then prettyPrint d'' else s) <> suff)               echo2-              m text "" <> "\n"+              m "" <> "\n"     where       f (A.ImportDecl _ m _ _ _ (Just a) _specs) mp =           case Map.lookup (sModuleName m) oldmap of@@ -170,7 +171,7 @@ -- probably mess up the location information, so the result (if -- different from the original) should be prettyprinted, not -- exactPrinted.-fixReferences :: (Data a, Typeable a) => Map S.ModuleName (A.Module SrcSpanInfo, String) -> S.ModuleName -> a -> a+fixReferences :: (Data a, Typeable a) => Map S.ModuleName ModuleInfo -> S.ModuleName -> a -> a fixReferences oldmap new x =     everywhere (mkT moveModuleName) x     where@@ -225,12 +226,12 @@ -- junk :: String -> Bool -- junk s = isSuffixOf ".imports" s || isSuffixOf "~" s -loadModules :: MonadClean m => [S.ModuleName] -> m (Map S.ModuleName (A.Module SrcSpanInfo, String))+loadModules :: MonadClean m => [S.ModuleName] -> m (Map S.ModuleName ModuleInfo) loadModules names = List.mapM loadModule names >>= return . Map.fromList . zip names -loadModule :: MonadClean m => S.ModuleName -> m (A.Module SrcSpanInfo, String)+loadModule :: MonadClean m => S.ModuleName -> m ModuleInfo loadModule name =     do path <- modulePath name        text <- liftIO $ readFile path-       m <- parseFile path >>= return . fromParseResult-       return (m, text)+       (m, comments) <- parseFileWithComments path >>= return . fromParseResult+       return (m, text, comments)
Language/Haskell/Modules/Params.hs view
@@ -1,14 +1,15 @@+-- | Functions to control the state variables of 'MonadClean'. {-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, ScopedTypeVariables, UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Haskell.Modules.Params     ( MonadClean     , runMonadClean-    , modifyDryRun-    , modifyExtensions     , modifyModuVerse-    , modifyHsFlags-    , modifySourceDirs     , modifyRemoveEmptyImports+    , modifySourceDirs+    , modifyExtensions+    , modifyHsFlags+    , modifyDryRun     , modifyTestMode     ) where @@ -16,39 +17,47 @@ import Data.Set (empty, Set) import Language.Haskell.Exts.Extension (Extension) import qualified Language.Haskell.Exts.Syntax as S (ModuleName)-import Language.Haskell.Modules.Internal (modifyParams, MonadClean, Params(dryRun, extensions, hsFlags, moduVerse, removeEmptyImports, sourceDirs, testMode), runMonadClean)+import Language.Haskell.Modules.Internal (MonadClean, modifyParams, Params(dryRun, extensions, hsFlags, moduVerse, removeEmptyImports, sourceDirs, testMode), runMonadClean) import Prelude hiding (writeFile) --- | Controls whether file updates will actually be performed.--- Default is False.  (I recommend running in a directory controlled--- by a version control system so you don't have to worry about this.)-modifyDryRun :: MonadClean m => (Bool -> Bool) -> m ()-modifyDryRun f = modifyParams (\ p -> p {dryRun = f (dryRun p)})---- | Modify the extra extensions passed to the compiler and the--- parser.  Default value is the list in--- 'Language.Haskell.Exts.Parser.defaultParseMode'.-modifyExtensions :: MonadClean m => ([Extension] -> [Extension]) -> m ()-modifyExtensions f = modifyParams (\ p -> p {extensions = f (extensions p)})- -- | Modify the set of modules whose imports will be updated when -- modules are split or merged.  No default, it is an error to run -- splitModules or catModules without first setting this. modifyModuVerse :: MonadClean m => (Set S.ModuleName -> Set S.ModuleName) -> m () modifyModuVerse f = modifyParams (\ p -> p {moduVerse = Just (f (fromMaybe empty (moduVerse p)))}) --- | Modify the list of extra flags passed to GHC.-modifyHsFlags :: MonadClean m => ([String] -> [String]) -> m ()-modifyHsFlags f = modifyParams (\ p -> p {hsFlags = f (hsFlags p)})+-- | If this flag is set, imports that become empty are removed.+-- Sometimes this will lead to errors, specifically when an instance+-- in the removed import that was required is no longer be available.+-- (Note that this reflects a limitation of the+-- @-ddump-minimal-imports@ option of GHC.)  If this happens this flag+-- should be set.  Note that an import that is already empty when+-- @cleanImports@ runs will never be removed, on the assumption that+-- it was placed there only to import instances.  Default is True.+modifyRemoveEmptyImports :: MonadClean m => (Bool -> Bool) -> m ()+modifyRemoveEmptyImports f = modifyParams (\ p -> p {removeEmptyImports = f (removeEmptyImports p)}) --- | Modify the list of directories that will be searched for source files.  Default is [\".\"].+-- | Modify the list of directories that will be searched for source+-- files, in a similar way to the Hs-Source-Dirs field in a cabal+-- file.  Default is @[\".\"]@. modifySourceDirs :: MonadClean m => ([FilePath] -> [FilePath]) -> m () modifySourceDirs f = modifyParams (\ p -> p {sourceDirs = f (sourceDirs p)}) --- | Change the flag controlling whether imports that become empty will be--- removed.  Default is True.-modifyRemoveEmptyImports :: MonadClean m => (Bool -> Bool) -> m ()-modifyRemoveEmptyImports f = modifyParams (\ p -> p {removeEmptyImports = f (removeEmptyImports p)})+-- | Modify the extra extensions passed to the compiler and the+-- parser.  Default value is the list in+-- 'Language.Haskell.Exts.Parser.defaultParseMode'.+modifyExtensions :: MonadClean m => ([Extension] -> [Extension]) -> m ()+modifyExtensions f = modifyParams (\ p -> p {extensions = f (extensions p)})++-- | Modify the list of extra flags passed to GHC.  Default is @[]@.+modifyHsFlags :: MonadClean m => ([String] -> [String]) -> m ()+modifyHsFlags f = modifyParams (\ p -> p {hsFlags = f (hsFlags p)})++-- | Controls whether file updates will actually be performed.+-- Default is False.  (I recommend running in a directory controlled+-- by a version control system so you don't have to worry about this.)+modifyDryRun :: MonadClean m => (Bool -> Bool) -> m ()+modifyDryRun f = modifyParams (\ p -> p {dryRun = f (dryRun p)})  -- | If TestMode is turned on no import cleaning will occur after a -- split or cat.  Default is False.
Language/Haskell/Modules/Split.hs view
@@ -22,10 +22,9 @@ import Language.Haskell.Exts.Pretty (defaultMode, prettyPrint, prettyPrintWithMode) import Language.Haskell.Exts.SrcLoc (SrcSpanInfo(..)) import qualified Language.Haskell.Exts.Syntax as S (ImportDecl(..), ModuleName(..), Name(..))-import Language.Haskell.Modules.Fold (echo, echo2, foldDecls, foldExports, foldHeader, foldImports, foldModule, ignore, ignore2)+import Language.Haskell.Modules.Fold (ModuleInfo, echo, echo2, foldDecls, foldExports, foldHeader, foldImports, foldModule, ignore, ignore2) import Language.Haskell.Modules.Imports (cleanImports)-import Language.Haskell.Modules.Internal (doResult, modifyParams, modulePath, ModuleResult(..), MonadClean(getParams), Params(moduVerse, sourceDirs, testMode), parseFile, runMonadClean)-import Language.Haskell.Modules.Util.QIO (noisily)+import Language.Haskell.Modules.Internal (doResult, modifyParams, modulePath, ModuleResult(..), MonadClean(getParams), Params(moduVerse, sourceDirs, testMode), parseFileWithComments, runMonadClean) import Language.Haskell.Modules.Util.Symbols (exports, imports, symbols) import Language.Haskell.Modules.Util.Test (diff, repoModules) import Prelude hiding (writeFile)@@ -47,13 +46,36 @@  -- | Split each of a module's declarations into a new module.  Update -- the imports of all the modules in the moduVerse to reflect the split.+-- For example, if you have a module like+--+-- @+-- module Start (a, b, (.+.)) where+-- import+-- a = 1 + a+-- b = 2+-- c = 3+-- c' = 4+-- (.+.) = b + c+-- @+--+-- After running @splitModule@ the @Start@ module will be gone.  The+-- @a@ and @b@ symbols will be in new modules named @Start.A@ and+-- @Start.B@.  Because they were not exported by @Start@, the @c@ and+-- @c'@ symbols will both be in a new module named @Start.Internal.C@.+-- And the @.+.@ symbol will be in a module named+-- @Start.OtherSymbols@.  Note that this module needs to import new+-- @Start.A@ and @Start.Internal.C@ modules.+--+-- If we had imported and then re-exported a symbol in Start it would+-- go into a module named @Start.ReExported@.  Any instance declarations+-- would go into @Start.Instances@. splitModule :: MonadClean m => S.ModuleName -> m () splitModule old =     do univ <- getParams >>= return . fromMaybe (error "moduVerse not set") . moduVerse        path <- modulePath old        text <- liftIO $ readFile path-       parsed <- parseFile path >>= return . fromParseResult-       newFiles <- doSplit text univ parsed >>= return . collisionCheck univ+       (parsed, comments) <- parseFileWithComments path >>= return . fromParseResult+       newFiles <- doSplit univ (parsed, text, comments) >>= return . collisionCheck univ        -- Write the new modules        setMapM_ doResult newFiles        -- Clean the new modules@@ -85,11 +107,11 @@ -- | Create the set of module results implied by the split - -- creations, removals, and modifications.  This includes the changes -- to the imports in modules that imported the original module.-doSplit :: MonadClean m => String -> Set S.ModuleName -> A.Module SrcSpanInfo -> m (Set ModuleResult)-doSplit _ _ (A.Module _ _ _ _ []) = return Set.empty -- No declarations - nothing to split-doSplit _ _ (A.Module _ _ _ _ [_]) = return Set.empty -- One declaration - nothing to split (but maybe we should anyway?)-doSplit _ _ (A.Module _ Nothing _ _ _) = throw $ userError $ "splitModule: no explicit header"-doSplit text univ m@(A.Module _ (Just (A.ModuleHead _ moduleName _ (Just _))) _ _ _) =+doSplit :: MonadClean m => Set S.ModuleName -> ModuleInfo -> m (Set ModuleResult)+doSplit _ (A.Module _ _ _ _ [], _, _) = return Set.empty -- No declarations - nothing to split+doSplit _ (A.Module _ _ _ _ [_], _, _) = return Set.empty -- One declaration - nothing to split (but maybe we should anyway?)+doSplit _ (A.Module _ Nothing _ _ _, _, _) = throw $ userError $ "splitModule: no explicit header"+doSplit univ m@(A.Module _ (Just (A.ModuleHead _ moduleName _ (Just _))) _ _ _, _, _) =     Set.mapM (updateImports (sModuleName moduleName) symbolToModule) univ' >>= return . union splitModules     where       -- The name of the module to be split@@ -98,17 +120,16 @@       -- will be in that module.  All of these declarations used to be       -- in moduleName. -      moduleDeclMap :: Map S.ModuleName [(A.Decl SrcSpanInfo, String)]-      moduleDeclMap = foldDecls (\ d pref s suff r -> Set.fold (\ sym mp -> insertWith (++) (subModuleName reExported internal old sym) [(d, pref <> s <> suff)] mp) r (symbols d)) ignore2 m text Map.empty+      moduleDeclMap = foldDecls (\ d pref s suff r -> Set.fold (\ sym mp -> insertWith (++) (subModuleName reExported internal old sym) [(d, pref <> s <> suff)] mp) r (symbols d)) ignore2 m Map.empty        -- This returns a set of maybe because there may be instance       -- declarations, in which case we want an Instances module to       -- appear in newModuleNames below.       declared :: Set (Maybe S.Name)-      declared = foldDecls (\ d _pref _s _suff r -> Set.union (symbols d) r) ignore2 m text Set.empty+      declared = foldDecls (\ d _pref _s _suff r -> Set.union (symbols d) r) ignore2 m Set.empty        exported :: Set S.Name-      exported = foldExports ignore2 (\ e _pref _s _suff r -> Set.union (justs (symbols e)) r) ignore2 m text Set.empty+      exported = foldExports ignore2 (\ e _pref _s _suff r -> Set.union (justs (symbols e)) r) ignore2 m Set.empty        reExported :: Set S.Name       reExported = difference exported (justs declared)@@ -132,8 +153,8 @@       symbolToModule =           mp'           where-            mp' = foldExports ignore2 (\ e _ _ _ r -> Set.fold f r (symbols e)) ignore2 m text mp-            mp = foldDecls (\ d _ _ _ r -> Set.fold f r (symbols d)) ignore2 m text Map.empty+            mp' = foldExports ignore2 (\ e _ _ _ r -> Set.fold f r (symbols e)) ignore2 m mp+            mp = foldDecls (\ d _ _ _ r -> Set.fold f r (symbols d)) ignore2 m Map.empty             f sym mp'' =                 Map.insertWith                    (\ a b -> if a /= b then error ("symbolToModule - two modules for " ++ show sym ++ ": " ++ show (a, b)) else a)@@ -149,21 +170,21 @@           case Map.lookup name' moduleDeclMap of             Nothing ->                 -- Build a module that re-exports a symbol-                foldHeader echo2 echo (\ _n pref _ suff r -> r <> pref <> modName <> suff) echo m text "" <>-                foldExports echo2 ignore ignore2 m text "" <>-                doSeps (foldExports ignore2 (\ e pref s suff r -> r <> if setAny (`member` reExported) (justs (symbols e)) then [(pref, s <> suff)] else []) (\ s r -> r ++ [("", s)]) m text []) <>-                foldImports (\ _i pref s suff r -> r <> pref <> s <> suff) m text ""+                foldHeader echo2 echo (\ _n pref _ suff r -> r <> pref <> modName <> suff) echo m "" <>+                foldExports echo2 ignore ignore2 m "" <>+                doSeps (foldExports ignore2 (\ e pref s suff r -> r <> if setAny (`member` reExported) (justs (symbols e)) then [(pref, s <> suff)] else []) (\ s r -> r ++ [("", s)]) m []) <>+                foldImports (\ _i pref s suff r -> r <> pref <> s <> suff) m ""             Just modDecls ->                 -- Change the module name in the header-                foldHeader echo2 echo (\ _n pref _ suff r -> r <> pref <> modName <> suff) echo m text "" <>+                foldHeader echo2 echo (\ _n pref _ suff r -> r <> pref <> modName <> suff) echo m "" <>                 "    ( " {-foldExports echo2 ignore ignore2 m text ""-} <>                 intercalate "\n    , " (nub (List.map (prettyPrintWithMode defaultMode) (newExports modDecls))) <>                 "\n    ) where" {-foldExports ignore2 ignore echo2 m text ""-} <>                 -- The prefix of the imports section-                fromMaybe "" (foldImports (\ _i pref _ _ r -> maybe (Just pref) Just r) m text Nothing) <>+                fromMaybe "" (foldImports (\ _i pref _ _ r -> maybe (Just pref) Just r) m Nothing) <>                 unlines (List.map (prettyPrintWithMode defaultMode) (elems (newImports modDecls))) <> "\n" <>                 -- Grab the old imports-                fromMaybe "" (foldImports (\ _i pref s suff r -> Just (maybe (s <> suff) (\ l -> l <> pref <> s <> suff) r)) m text Nothing) <>+                fromMaybe "" (foldImports (\ _i pref s suff r -> Just (maybe (s <> suff) (\ l -> l <> pref <> s <> suff) r)) m Nothing) <>                 -- fromMaybe "" (foldDecls (\ _d pref _ _ r -> maybe (Just pref) Just r) ignore2 m text Nothing) <>                 concatMap snd (reverse modDecls) <> "\n"               where@@ -178,7 +199,7 @@                 -- In this module, we need to import any module that declares a symbol                 -- referenced here.                 referenced modDecls = Set.map sName (gFind modDecls :: Set (A.Name SrcSpanInfo))-doSplit _ _ _ = error "splitModule'"+doSplit _ _ = error "splitModule'"  -- Re-construct a separated list doSeps :: [(String, String)] -> String@@ -190,12 +211,12 @@ updateImports old symbolToModule name =     do path <- modulePath name        text' <- liftIO $ readFile path-       parsed <- parseFile path+       parsed <- parseFileWithComments path        case parsed of-         ParseOk m' ->+         ParseOk (m', comments') ->              let text'' = foldModule echo2 echo echo echo echo2 echo echo2                           (\ i pref s suff r -> r <> pref <> updateImportDecl s i <> suff)-                          echo echo2 m' text' "" in+                          echo echo2 (m', text', comments') "" in              return $ if text' /= text'' then Modified name text'' else Unchanged name          ParseFailed _ _ -> error $ "Parse error in " ++ show name     where@@ -282,13 +303,13 @@            do modifyParams (\ p -> p {sourceDirs = ["testdata/copy"], moduVerse = Just repoModules})               splitModule (S.ModuleName "Debian.Repo.Package")          (code, out, err) <- diff "testdata/splitresult" "testdata/copy"-         assertEqual "splitModule" (ExitSuccess, "", "") (code, out, err)+         assertEqual "splitModule" (ExitFailure 1, "diff -ru '--exclude=*~' '--exclude=*.imports' testdata/splitresult/Debian/Repo/Package/BinaryPackagesOfIndex.hs testdata/copy/Debian/Repo/Package/BinaryPackagesOfIndex.hs\n--- testdata/splitresult/Debian/Repo/Package/BinaryPackagesOfIndex.hs\n+++ testdata/copy/Debian/Repo/Package/BinaryPackagesOfIndex.hs\n@@ -22,4 +22,4 @@\n binaryPackagesOfIndex repo release index =\n     case packageIndexArch index of\n       Source -> return (Right [])\n-      _ -> liftIO $ getPackages repo release index -- >>= return . either Left (Right . List.map (toBinaryPackage index . packageInfo))\n+      _ -> liftIO $ getPackages repo release index \n", "") (code, out, err)  test2 :: Test test2 =     TestCase $     do _ <- system "rsync -aHxS --delete testdata/split2/ testdata/copy"-       runMonadClean $ noisily $ noisily $+       runMonadClean $          do modifyParams (\ p -> p {testMode = True,                                     sourceDirs = ["testdata/copy"],                                     -- extensions = NoImplicitPrelude : extensions p,@@ -301,7 +322,7 @@ test3 =     TestCase $     do _ <- system "rsync -aHxS --delete testdata/split2/ testdata/copy"-       runMonadClean $ noisily $ noisily $+       runMonadClean $          do modifyParams (\ p -> p {testMode = False,                                     sourceDirs = ["testdata/copy"],                                     -- extensions = NoImplicitPrelude : extensions p,
Language/Haskell/Modules/Util/SrcLoc.hs view
@@ -10,6 +10,7 @@     , textSpan     , srcPairTextHead     , srcPairTextTail+    , srcPairTextPair     , makeTree     , tests     ) where@@ -61,6 +62,9 @@ class HasSpanInfo a where     spanInfo :: a -> SrcSpanInfo +instance HasSpanInfo SrcSpan where+    spanInfo x = SrcSpanInfo x []+ instance HasSpanInfo a => HasSpanInfo (Tree a) where     spanInfo (Node x _) = spanInfo x @@ -233,6 +237,10 @@                 ('\t' : s') -> srcPairTextTail (b {srcColumn = ((srcColumn b + 7) `div` 8) * 8}) e s'                 (_ : s') -> srcPairTextTail (b {srcColumn = srcColumn b + 1}) e s'          else s++-- | Shirley there's a more efficient way to do this?+srcPairTextPair :: SrcLoc -> SrcLoc -> String -> (String, String)+srcPairTextPair b e s = (srcPairTextHead b e s, srcPairTextTail b e s)  instance Default SrcLoc where     def = SrcLoc "<unknown>.hs" 1 1
Language/Haskell/Modules/Util/Temp.hs view
@@ -1,9 +1,10 @@+{-# LANGUAGE PackageImports #-} module Language.Haskell.Modules.Util.Temp     ( withTempDirectory     ) where  import qualified Control.Exception as IO (catch)-import Control.Monad.CatchIO as IOT (bracket, MonadCatchIO)+import "MonadCatchIO-mtl" Control.Monad.CatchIO as IOT (bracket, MonadCatchIO) import Control.Monad.Trans (liftIO, MonadIO) import System.Directory (removeDirectoryRecursive) import qualified System.IO.Temp as Temp (createTempDirectory)
Language/Haskell/Modules/Util/Test.hs view
@@ -131,15 +131,7 @@        let out' = unlines (List.filter (not . isPrefixOf "Binary files") . List.map (takeWhile (/= '\t')) $ (lines out))        return (code, out', err) --- | Convenience function for building the moduVerse, searches for--- files in a directory hierarchy, with a filter predicate.-findModules :: FilePath -> IO (Set S.ModuleName)-findModules top =-    findPaths top >>= return . Set.map asModuleName-    where-      asModuleName path =-          S.ModuleName (List.map (\ c -> if c == '/' then '.' else c) (take (length path - 3) path))-+-- | Find the paths of all the files below the directory @top@. findPaths :: FilePath -> IO (Set FilePath) findPaths top =     doPath empty top@@ -153,3 +145,15 @@                _ -> return r       doDirectory r path =           getDirectoryContents path >>= foldM doPath r . List.map (path </>) . filter (\ x -> x /= "." && x /= "..")++-- | Convenience function for building the moduVerse, searches for+-- modules in a directory hierarchy.  FIXME: This should be in+-- MonadClean and use the value of sourceDirs to remove prefixes from+-- the module paths.  And then it should look at the module text to+-- see what the module name really is.+findModules :: FilePath -> IO (Set S.ModuleName)+findModules top =+    findPaths top >>= return . Set.map asModuleName+    where+      asModuleName path =+          S.ModuleName (List.map (\ c -> if c == '/' then '.' else c) (take (length path - 3) path))
+ Tests.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE CPP, ScopedTypeVariables #-}+{-# OPTIONS -Wall #-}++import Control.Exception (SomeException, try)+import Data.List (isPrefixOf)+import Data.Set as Set (Set, fromList, union, delete)+import Language.Haskell.Exts.Annotated (defaultParseMode, exactPrint, parseFileWithComments, ParseResult(ParseOk))+import Language.Haskell.Exts.Annotated.Syntax as A (Module)+import Language.Haskell.Exts.Comments (Comment)+import Language.Haskell.Exts.Extension (Extension(..))+import Language.Haskell.Exts.Syntax (ModuleName(ModuleName))+import Language.Haskell.Exts.SrcLoc (SrcSpanInfo)+import Language.Haskell.Modules (splitModule, mergeModules)+import qualified Language.Haskell.Modules.Merge as Merge (tests)+import Language.Haskell.Modules.Common (withCurrentDirectory)+import qualified Language.Haskell.Modules.Fold as Fold (tests)+import qualified Language.Haskell.Modules.Imports as Imports (tests)+import Language.Haskell.Modules.Internal (MonadClean, Params(extensions, moduVerse), runMonadClean, modifyParams)+import qualified Language.Haskell.Modules.Split as Split (tests)+import Language.Haskell.Modules.Util.QIO (noisily, qPutStrLn)+import Language.Haskell.Modules.Util.Test (logicModules, diff')+import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith)+import System.Process (readProcess)+import Test.HUnit (assertEqual, Counts(..), runTestTT, Test(TestList, TestCase, TestLabel))++main :: IO ()+main =+    do counts <- runTestTT (TestList [TestLabel "Main" Main.tests])+       putStrLn (show counts)+       case (errors counts + failures counts) of+         0 -> exitWith ExitSuccess+         _ -> exitWith (ExitFailure 1)++withTestData :: (A.Module SrcSpanInfo -> [Comment] -> String -> IO r) -> FilePath -> IO r+withTestData f path = withCurrentDirectory "testdata/original" $+    do text <- try (readFile path)+       source <- try (parseFileWithComments defaultParseMode path)+       case (text, source) of+         (Right text', Right (ParseOk (m, comments))) ->+             f m comments text'+         (Right _, Right _) -> error "parse failure"+         (Left (e :: SomeException), _) -> error $ "failure: " ++ show e+         (_, Left (e :: SomeException)) -> error $ "failure: " ++ show e++tests :: Test+tests = TestList [ Main.test1+                 , TestLabel "Merge" Merge.tests+                 , TestLabel "Fold" Fold.tests+                 , TestLabel "Imports" Imports.tests+                 , TestLabel "Split" Split.tests+                 -- If split-merge-merge fails try split and split-merge.+                 , Main.logictest "split" test2a+                 , Main.logictest "split-merge" test2b+                 , Main.logictest "split-merge-merge" test2c+                 ]++test1 :: Test+test1 =+    TestLabel "exactPrint" $ TestCase+    (withTestData test "Debian/Repo/Package.hs" >>= \ (output, text) ->+     assertEqual+     "exactPrint"+     text+     output)+    where+      test parsed comments text = return (exactPrint parsed comments, text)++-- logictest :: MonadClean m => String -> (Set ModuleName -> m ()) -> Test+logictest s f =+    TestLabel s $ TestCase $+    do _ <- readProcess "rsync" ["-aHxS", "--delete", {-"-v",-} "testdata/logic/", "testdata/copy"] ""+       _ <- withCurrentDirectory "testdata/copy" $ runMonadClean $ f logicModules+       (code, out, err) <- diff' ("testdata/" ++ s ++ "-result") "testdata/copy"+       let out' = unlines (filter (not . isPrefixOf "Binary files") . map (takeWhile (/= '\t')) $ (lines out))+       assertEqual s (ExitSuccess, "", "") (code, out', err)++test2a :: MonadClean m => Set ModuleName -> m ()+test2a u =+         do modifyParams (\ p -> p {extensions = extensions p ++ [MultiParamTypeClasses],+                                    moduVerse = Just u})+            qPutStrLn "\nSplitting module Literal"+            splitModule (ModuleName "Data.Logic.Classes.Literal")+            return ()++test2b :: MonadClean m => Set ModuleName -> m ()+test2b u =+         do modifyParams (\ p -> p {extensions = extensions p ++ [MultiParamTypeClasses],+                                    moduVerse = Just u})+            qPutStrLn "\nSplitting module Literal"+            splitModule (ModuleName "Data.Logic.Classes.Literal")+            qPutStrLn "Merging FirstOrder, fromFirstOrder, fromLiteral into FirstOrder"+            -- modifyParams (\ p -> p {testMode = True})+            mergeModules+              [ModuleName "Data.Logic.Classes.FirstOrder",+               ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",+               ModuleName "Data.Logic.Classes.Literal.FromLiteral"]+              (ModuleName "Data.Logic.Classes.FirstOrder")+            noisily (qPutStrLn "merge2")+            return ()+    where+      u' = union (fromList [ModuleName "Data.Logic.Classes.Literal.Internal.FixityLiteral",+                            ModuleName "Data.Logic.Classes.Literal.FoldAtomsLiteral",+                            ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",+                            ModuleName "Data.Logic.Classes.Literal.FromLiteral",+                            ModuleName "Data.Logic.Classes.Literal.Literal",+                            ModuleName "Data.Logic.Classes.Literal.PrettyLit",+                            ModuleName "Data.Logic.Classes.Literal.ToPropositional",+                            ModuleName "Data.Logic.Classes.Literal.ZipLiterals"])+                 (delete (ModuleName "Data.Logic.Classes.Literal") u)++test2c :: MonadClean m => Set ModuleName -> m ()+test2c u =+         do modifyParams (\ p -> p {extensions = extensions p ++ [MultiParamTypeClasses],+                                    moduVerse = Just u})+            qPutStrLn "\nSplitting module Literal"+            splitModule (ModuleName "Data.Logic.Classes.Literal")+            qPutStrLn "Merging FirstOrder, fromFirstOrder, fromLiteral into FirstOrder"+            mergeModules+              [ModuleName "Data.Logic.Classes.FirstOrder",+               ModuleName "Data.Logic.Classes.Literal.FromFirstOrder",+               ModuleName "Data.Logic.Classes.Literal.FromLiteral"]+              (ModuleName "Data.Logic.Classes.FirstOrder")+            noisily (qPutStrLn "Merging remaining split modules into Literal")+            -- modifyParams (\ p -> p {testMode = True})+            mergeModules+              [ModuleName "Data.Logic.Classes.Literal.Literal",+               ModuleName "Data.Logic.Classes.Literal.ZipLiterals",+               ModuleName "Data.Logic.Classes.Literal.ToPropositional",+               ModuleName "Data.Logic.Classes.Literal.PrettyLit",+               ModuleName "Data.Logic.Classes.Literal.Internal.FixityLiteral",+               ModuleName "Data.Logic.Classes.Literal.FoldAtomsLiteral"]+              (ModuleName "Data.Logic.Classes.Literal")+            return ()
module-management.cabal view
@@ -1,5 +1,5 @@ Name:               module-management-Version:            0.9.3.1+Version:            0.10 Synopsis:           Clean up module imports, split and merge modules Description:        Clean up module imports, split and merge modules. Homepage:           http://src.seereason.com/module-management@@ -10,7 +10,12 @@ Stability:          experimental Build-type:         Simple Cabal-version:      >=1.8+Extra-Source-Files: testdata.tar.gz +Flag build-tests+  Description: build the test executable+  Default: True+ Library   ghc-Options:      -Wall -O2   Exposed-Modules:@@ -50,28 +55,51 @@     system-fileio,     temporary --- Executable tests---   Build-Depends:---     ansi-wl-pprint,---     base,---     bytestring,---     Cabal,---     containers,---     data-default,---     debian,---     directory,---     Extra,---     filepath,---     HUnit,---     haskell-src-exts,---     module-management,---     MonadCatchIO-mtl,---     mtl,---     process,---     process-progress,---     pureMD5,---     regex-compat,---     set-extra,---     syb,---     system-fileio---   Main-is: Tests.hs+Executable hmm+  Main-is: scripts/CLI.hs+  Build-Depends:+    base,+    containers,+    data-default,+    directory,+    filepath,+    haskell-src-exts,+    HUnit,+    module-management,+    MonadCatchIO-mtl,+    mtl,+    process,+    set-extra,+    syb,+    temporary++Executable tests+  Main-is: Tests.hs+  if flag (build-tests)+    Buildable: True+    Build-Depends:+      ansi-wl-pprint,+      base,+      bytestring,+      Cabal,+      containers,+      data-default,+      debian,+      directory,+      Extra,+      filepath,+      HUnit,+      haskell-src-exts,+      module-management,+      MonadCatchIO-mtl,+      mtl,+      process,+      process-progress,+      pureMD5,+      regex-compat,+      set-extra,+      syb,+      system-fileio,+      temporary+  else+    Buildable: False
+ scripts/CLI.hs view
@@ -0,0 +1,92 @@+{-# OPTIONS_GHC -Wall #-}+module Main where++import Control.Monad.Trans (MonadIO(liftIO))+import Data.List (intercalate, isPrefixOf)+import Data.Maybe (fromMaybe)+import Data.Set (Set, union, toList, empty, size, unions, singleton)+import Language.Haskell.Exts.Syntax (ModuleName(ModuleName))+import Language.Haskell.Modules (runMonadClean, cleanImports, splitModule, mergeModules,+                                 modifyModuVerse, modifySourceDirs)+import Language.Haskell.Modules.Internal (getParams, Params(..))+import Language.Haskell.Modules.Params (MonadClean)+import Language.Haskell.Modules.Util.QIO (noisily, quietly)+import Language.Haskell.Modules.Util.Test (findModules)+import System.IO (stdin, stderr, hGetLine, hPutStr, hPutStrLn)++main :: IO ()+main = runMonadClean (noisily cli)++cli :: MonadClean m => m ()+cli = liftIO (hPutStr stderr " > " >> hGetLine stdin) >>= cmd . words++cmd :: MonadClean m => [String] -> m ()+cmd [] = cli+cmd (s : args) =+    case filter (any (== s) . fst) cmds of+      [(_, f)] -> f+      -- No exact matches - look for prefix matches+      [] -> case filter (any (isPrefixOf s) . fst) cmds of+              [(_, f)] -> f+              [] -> liftIO (hPutStrLn stderr $+                              show s ++ " invalid - expected " ++ intercalate ", " (concatMap fst cmds)) >> cli+              xs -> liftIO (hPutStrLn stderr $+                              show s ++ " ambiguous - expected " ++ intercalate ", " (concatMap fst xs)) >> cli+      _ -> error $ "Internal error - multiple definitions for " ++ show s+    where+      cmds =+          [(["quit", "exit", "bye", "."],	liftIO (hPutStrLn stderr "Exiting")),+           (["v"],				liftIO (hPutStrLn stderr "Increasing verbosity level") >> noisily cli),+           (["q"],				liftIO (hPutStrLn stderr "Decreasing verbosity level") >> quietly cli),+           (["help"],				liftIO (hPutStrLn stderr "help text") >> cli),+           (["verse"],				verse args >> cli),+           (["clean"],				clean args >> cli),+           (["dir"],				dir args >> cli),+           (["split"],				split args >> cli),+           (["merge"],				merge args >> cli)]++unModuleName :: ModuleName -> String+unModuleName (ModuleName x) = x++verse :: MonadClean m => [String] -> m ()+verse [] =+    do modules <- getParams >>= return . moduVerse+       liftIO $ hPutStrLn stderr ("Usage: verse <pathormodule1> <pathormodule2> ...\n" +++                                  "Add the module or all the modules below a directory to the moduVerse\n" +++                                  "Currently:\n  " ++ showVerse (fromMaybe empty modules))+verse args =+    do new <- mapM (liftIO . find) args+       modifyModuVerse (union (unions new))+       modules <- getParams >>= return . moduVerse+       liftIO (hPutStrLn stderr $ "moduVerse updated:\n  " ++ showVerse (fromMaybe empty modules))+    where+      find :: String -> IO (Set ModuleName)+      find s =+          do ms <- liftIO (findModules s)+             case size ms of+               0 -> return (singleton (ModuleName s))+               _ -> return ms++showVerse :: Set ModuleName -> String+showVerse modules = "[ " ++ intercalate "\n  , " (map unModuleName (toList modules)) ++ " ]"++dir :: MonadClean m => [FilePath] -> m ()+dir [] = modifySourceDirs (const [])+dir xs =+    do modifySourceDirs (++ xs)+       xs' <- getParams >>= return . sourceDirs+       liftIO (hPutStrLn stderr $ "sourceDirs updated:\n  [ " ++ intercalate "\n  , " xs' ++ " ]")++clean :: MonadClean m => [FilePath] -> m ()+clean [] = liftIO $ hPutStrLn stderr "Usage: clean <modulepath1> <modulepath2> ..."+clean args = mapM_ cleanImports args++split :: MonadClean m => [String] -> m ()+split [arg] = splitModule (ModuleName arg)+split _ = liftIO $ hPutStrLn stderr "Usage: split <modulename>"++merge :: MonadClean m => [String] -> m ()+merge args =+    case splitAt (length args - 1) args of+      (inputs, [output]) -> mergeModules (map ModuleName inputs) (ModuleName output) >> return ()+      _ -> liftIO $ hPutStrLn stderr "Usage: merge <inputmodulename1> <inputmodulename2> ... <outputmodulename>"
+ testdata.tar.gz view

binary file changed (absent → 632992 bytes)