From 8ef7daece8dab2658001e2d7229938473f4d3836 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 26 Sep 2017 23:24:00 +0200 Subject: [PATCH 01/26] Add changelog entry for 0.8.0.3 --- ChangeLog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 4d16652..236a7ad 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,18 @@ # Revision history for brittany +## 0.8.0.3 -- September 2017 + +* Support for ghc-8.2.1 +* Bugfixes: + - Fix quadratic performance issue + - Fix special "where" indentation with indentAmount /= 2 + - Fix negative literals in patterns + - Support type applications +* Accept `-h` for `--help` and improve help layouting (via butcher-1.1.0.2) +* Add continuous integration via travis (cabal, cabal-new, stack) + (brittle due compilation time limit) +* Reduce compilation memory usage a bit + ## 0.8.0.2 -- August 2017 * Add library interface, to be used by `haskell-ide-engine`. -- 2.30.2 From 8438d4a03d1d41fc105c99b04b7d951c19e87326 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 26 Sep 2017 23:41:54 +0200 Subject: [PATCH 02/26] Update README.md (ghc versions) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c048afa..d32a189 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,13 @@ require fixing: be detected and the user will get an error); there are other cases where comments are moved slightly; there are also cases where comments result in wonky newline insertion (although this should be a purely aesthetic issue.) -- There is an **open performance issue on large inputs** (due to an - accidentally quadratic sub-algorithm); noticable for inputs with >1k loc. +- ~~There is an **open performance issue on large inputs** (due to an + accidentally quadratic sub-algorithm); noticable for inputs with >1k loc.~~ + (fixed in `0.8.0.3`) # Other usage notes -- Requires `GHC-8.0.*`; support for 8.2 is on the list, but I haven't even - looked at how much the `GHC` API changes. +- Supports GHC versions `8.0.*` and `8.2.*`. - config (file) documentation is lacking. - some config values can not be configured via commandline yet. - uses/creates user config file in `~/.brittany/config.yaml`; -- 2.30.2 From 8c6eb4d1e2b61ea51004aadc7280da503e883839 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 26 Sep 2017 23:42:11 +0200 Subject: [PATCH 03/26] Update stack.yaml (butcher-1.1.0.2) --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 39c0882..4bbcc0c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,7 +3,7 @@ resolver: lts-9.0 extra-deps: - monad-memo-0.4.1 - czipwith-1.0.0.0 - - butcher-1.1.0.0 + - butcher-1.1.0.2 - data-tree-print-0.1.0.0 - deque-0.2 -- 2.30.2 From a348ae7fbcc4bfbfb301f6dacd4cc0e096b9c7b2 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Fri, 29 Sep 2017 14:59:41 +0200 Subject: [PATCH 04/26] Switch to XDG path for config; Search conf in parents - switch to XDG path should be backwards-compatible: - new config will be written to XDG path - but existing config in ~/.brittany will be respected - looks for "brittany.yaml" not only in cwd, but in parents too. uses the first file found. fixes #45, fixes #55 --- src-brittany/Main.hs | 43 +++++++++++---- .../Haskell/Brittany/Internal/Config.hs | 55 +++++++++++-------- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 129ee50..71b278a 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -292,15 +292,38 @@ coreIO putErrorLnIO config suppressOutput inputPathM outputPathM = EitherT.runEi readConfigs :: CConfig Option -> [System.IO.FilePath] -> MaybeT IO Config readConfigs cmdlineConfig configPaths = do - let defLocalConfigPath = "brittany.yaml" - userBritPath <- liftIO $ Directory.getAppUserDataDirectory "brittany" - let defUserConfigPath = userBritPath FilePath.</> "config.yaml" - merged <- case configPaths of + userBritPathSimple <- liftIO $ Directory.getAppUserDataDirectory "brittany" + userBritPathXdg <- liftIO + $ Directory.getXdgDirectory Directory.XdgConfig "brittany" + let userConfigPathSimple = userBritPathSimple FilePath.</> "config.yaml" + let userConfigPathXdg = userBritPathXdg FilePath.</> "config.yaml" + let + findLocalConfig :: MaybeT IO (Maybe (CConfig Option)) + findLocalConfig = do + cwd <- liftIO $ Directory.getCurrentDirectory + let dirParts = FilePath.splitDirectories cwd + let searchDirs = + [ FilePath.joinPath x | x <- reverse $ List.inits dirParts ] + -- when cwd is "a/b/c", searchDirs is ["a/b/c", "a/b", "a", "/"] + mFilePath <- liftIO $ Directory.findFileWith Directory.doesFileExist + searchDirs + "brittany.yaml" + case mFilePath of + Nothing -> pure Nothing + Just fp -> readConfig fp + configsRead <- case configPaths of [] -> do - liftIO $ Directory.createDirectoryIfMissing False userBritPath - return cmdlineConfig - >>= readMergePersConfig defLocalConfigPath False - >>= readMergePersConfig defUserConfigPath True - -- TODO: ensure that paths exist ? - paths -> foldl (\prev p -> prev >>= readMergePersConfig p False) (return cmdlineConfig) paths + localConfig <- findLocalConfig + userConfigSimple <- readConfig userConfigPathSimple + userConfigXdg <- readConfig userConfigPathXdg + let userConfig = userConfigSimple <|> userConfigXdg + when (Data.Maybe.isNothing userConfig) $ do + liftIO $ Directory.createDirectoryIfMissing False userBritPathXdg + writeDefaultConfig userConfigPathXdg + -- rightmost has highest priority + pure $ [userConfig, localConfig] + paths -> readConfig `mapM` reverse paths + -- reverse to give highest priority to the first + merged <- + pure $ Semigroup.mconcat $ catMaybes $ configsRead ++ [Just cmdlineConfig] return $ cZipWith fromOptionIdentity staticDefaultConfig merged diff --git a/src/Language/Haskell/Brittany/Internal/Config.hs b/src/Language/Haskell/Brittany/Internal/Config.hs index 49651d7..baaca1f 100644 --- a/src/Language/Haskell/Brittany/Internal/Config.hs +++ b/src/Language/Haskell/Brittany/Internal/Config.hs @@ -8,7 +8,8 @@ module Language.Haskell.Brittany.Internal.Config , configParser , staticDefaultConfig , forwardOptionsSyntaxExtsEnabled - , readMergePersConfig + , readConfig + , writeDefaultConfig , showConfigYaml ) where @@ -198,29 +199,37 @@ configParser = do -- , infoIntersperse = True -- } -readMergePersConfig - :: System.IO.FilePath -> Bool -> CConfig Option -> MaybeT IO (CConfig Option) -readMergePersConfig path shouldCreate conf = do + +-- | Reads a config from a file. If the file does not exist, returns +-- Nothing. If the file exists and parsing fails, prints to stderr and +-- aborts the MaybeT. Otherwise succeed via Just. +-- If the second parameter is True and the file does not exist, writes the +-- staticDefaultConfig to the file. +readConfig + :: MonadIO m => System.IO.FilePath -> MaybeT m (Maybe (CConfig Option)) +readConfig path = do exists <- liftIO $ System.Directory.doesFileExist path - if - | exists -> do - contents <- liftIO $ ByteString.readFile path -- no lazy IO, tyvm. - fileConf <- case Data.Yaml.decodeEither contents of - Left e -> do - liftIO - $ putStrErrLn - $ "error reading in brittany config from " ++ path ++ ":" - liftIO $ putStrErrLn e - mzero - Right x -> return x - return $ fileConf Semigroup.<> conf - | shouldCreate -> do - liftIO $ ByteString.writeFile path - $ Data.Yaml.encode - $ cMap (Option . Just . runIdentity) staticDefaultConfig - return $ conf - | otherwise -> do - return conf + if exists + then do + contents <- liftIO $ ByteString.readFile path -- no lazy IO, tyvm. + fileConf <- case Data.Yaml.decodeEither contents of + Left e -> do + liftIO + $ putStrErrLn + $ "error reading in brittany config from " + ++ path + ++ ":" + liftIO $ putStrErrLn e + mzero + Right x -> return x + return $ Just fileConf + else return $ Nothing + +writeDefaultConfig :: MonadIO m => System.IO.FilePath -> m () +writeDefaultConfig path = + liftIO $ ByteString.writeFile path $ Data.Yaml.encode $ cMap + (Option . Just . runIdentity) + staticDefaultConfig showConfigYaml :: Config -> String showConfigYaml = Data.ByteString.Char8.unpack -- 2.30.2 From 5a12b630351695004d24daea58ded23cf7b44bbd Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Fri, 29 Sep 2017 17:39:39 +0200 Subject: [PATCH 05/26] Adapt travis script to improve build times --- .travis.yml | 12 ++++++------ brittany.cabal | 5 ----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6b4a16..8b62149 100644 --- a/.travis.yml +++ b/.travis.yml @@ -180,7 +180,7 @@ before_install: - | function better_wait() { date - time $* & # send the long living command to background! + time "$*" & # send the long living command to background! set +x MINUTES=0 @@ -231,7 +231,7 @@ install: echo "cabal build-cache MISS"; rm -rf $HOME/.cabsnap; mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin; - cabal install -j$JOBS --only-dependencies --enable-tests --enable-benchmarks; + cabal install -j$JOBS --only-dependencies --enable-tests --enable-benchmarks --ghc-options="-j1 +RTS -M500M"; fi # snapshot package-db on cache miss @@ -259,12 +259,12 @@ script: set -ex case "$BUILD" in stack) - better_wait stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps + better_wait stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps --ghc-options="-j1 +RTS -M500M" ;; cabal) if [ -f configure.ac ]; then autoreconf -i; fi cabal configure --enable-tests --enable-benchmarks -v # -v2 provides useful information for debugging - better_wait cabal build -j$JOBS # this builds all libraries and executables (including tests/benchmarks) + better_wait cabal build -j$JOBS --ghc-options="-j1 +RTS -M500M" # this builds all libraries and executables (including tests/benchmarks) cabal test ;; cabaldist) @@ -275,12 +275,12 @@ script: # If there are no other `.tar.gz` files in `dist`, this can be even simpler: # `cabal install --force-reinstalls dist/*-*.tar.gz` SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz && - (cd dist && better_wait cabal install -j$JOBS --force-reinstalls "$SRC_TGZ") + (cd dist && better_wait cabal install -j$JOBS --force-reinstalls "$SRC_TGZ" --ghc-options="-j1 +RTS -M500M") ;; canew) better_wait cabal new-build -j$JOBS --disable-tests --disable-benchmarks better_wait cabal new-build -j$JOBS --enable-tests --enable-benchmarks - cabal new-test + cabal new-test --ghc-options="-j1 +RTS -M500M" ;; esac set +ex diff --git a/brittany.cabal b/brittany.cabal index 07504a0..957fb06 100644 --- a/brittany.cabal +++ b/brittany.cabal @@ -80,7 +80,6 @@ library { } ghc-options: { -Wall - -j -fno-warn-unused-imports -fno-warn-redundant-constraints } @@ -203,7 +202,6 @@ executable brittany } ghc-options: { -Wall - -j -fno-spec-constr -fno-warn-unused-imports -fno-warn-redundant-constraints @@ -283,7 +281,6 @@ test-suite unittests } ghc-options: { -Wall - -j -fno-warn-unused-imports -rtsopts -with-rtsopts "-M2G" @@ -356,7 +353,6 @@ test-suite littests } ghc-options: { -Wall - -j -fno-warn-unused-imports -rtsopts -with-rtsopts "-M2G" @@ -395,7 +391,6 @@ test-suite libinterfacetests } ghc-options: { -Wall - -j -fno-warn-unused-imports -rtsopts -with-rtsopts "-M2G" -- 2.30.2 From 308da71afbbc579aa26b97d62b0a1eb8f6829fec Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Sun, 1 Oct 2017 13:03:49 +0200 Subject: [PATCH 06/26] support multiple inputs and outputs --- src-brittany/Main.hs | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 129ee50..a88e1e3 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -14,6 +14,7 @@ import qualified Data.Map as Map import qualified Data.Text.Lazy.Builder as Text.Builder +import Control.Monad (foldM) import Data.CZipWith import qualified Debug.Trace as Trace @@ -103,8 +104,8 @@ mainCmdParser helpDesc = do printHelp <- addSimpleBoolFlag "h" ["help"] mempty printVersion <- addSimpleBoolFlag "" ["version"] mempty printLicense <- addSimpleBoolFlag "" ["license"] mempty - inputPaths <- addFlagStringParams "i" ["input"] "PATH" (flagHelpStr "path to input haskell source file") - outputPaths <- addFlagStringParams "o" ["output"] "PATH" (flagHelpStr "output file path") + inputPaths <- addFlagStringParams "i" ["input"] "PATH" (flagHelpStr "paths to input haskell source files") + outputPaths <- addFlagStringParams "o" ["output"] "PATH" (flagHelpStr "output file paths") configPaths <- addFlagStringParams "" ["config-file"] "PATH" (flagHelpStr "path to config file") -- TODO: allow default on addFlagStringParam ? cmdlineConfig <- configParser suppressOutput <- addSimpleBoolFlag @@ -128,29 +129,32 @@ mainCmdParser helpDesc = do when printHelp $ do liftIO $ print $ ppHelpShallow desc System.Exit.exitSuccess - inputPathM <- case maybeToList inputParam ++ inputPaths of - [] -> do - return Nothing - [x] -> return $ Just x - _ -> do - putStrErrLn $ "more than one input, aborting" - System.Exit.exitWith (System.Exit.ExitFailure 51) - outputPathM <- case outputPaths of - [] -> do - return Nothing - [x] -> return $ Just x - _ -> do - putStrErrLn $ "more than one output, aborting" - System.Exit.exitWith (System.Exit.ExitFailure 52) + let inputPaths' = nonEmptyList Nothing . map Just $ maybeToList inputParam ++ inputPaths + let outputPaths' = nonEmptyList Nothing . map Just $ outputPaths + when (length inputPaths' /= length outputPaths') $ do + putStrErrLn "the number of inputs must match ther number of outputs" + System.Exit.exitWith (System.Exit.ExitFailure 51) + config <- runMaybeT (readConfigs cmdlineConfig configPaths) >>= \case Nothing -> System.Exit.exitWith (System.Exit.ExitFailure 53) Just x -> return x when (confUnpack $ _dconf_dump_config $ _conf_debug $ config) $ do trace (showConfigYaml config) $ return () - eitherErrSucc <- coreIO putStrErrLn config suppressOutput inputPathM outputPathM - case eitherErrSucc of - Left errNo -> System.Exit.exitWith (System.Exit.ExitFailure errNo) - Right () -> pure () + + let ios = zipWith (coreIO putStrErrLn config suppressOutput) inputPaths' outputPaths' + errNoM <- foldM run Nothing ios + case errNoM of + Just errNo -> System.Exit.exitWith (System.Exit.ExitFailure errNo) + Nothing -> pure () + where + run acc io = do + res <- io + case res of + Left _ -> return (Just 1) + Right () -> return acc + + nonEmptyList def [] = [def] + nonEmptyList _ x = x -- | The main IO parts for the default mode of operation, and after commandline -- 2.30.2 From 36af16f881f489f7b59268146c401dd42fae1945 Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Sun, 1 Oct 2017 15:04:27 +0200 Subject: [PATCH 07/26] add inplace flag --- src-brittany/Main.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index a88e1e3..ed21e50 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -113,6 +113,7 @@ mainCmdParser helpDesc = do ["suppress-output"] (flagHelp $ parDoc "suppress the regular output, i.e. the transformed haskell source") _verbosity <- addSimpleCountFlag "v" ["verbose"] (flagHelp $ parDoc "[currently without effect; TODO]") + inplace <- addSimpleBoolFlag "" ["inplace"] (flagHelp $ parDoc "overwrite the input files") reorderStop inputParam <- addStringParamOpt "PATH" (paramHelpStr "path to input haskell source file") desc <- peekCmdDesc @@ -129,8 +130,12 @@ mainCmdParser helpDesc = do when printHelp $ do liftIO $ print $ ppHelpShallow desc System.Exit.exitSuccess + when (length outputPaths > 0 && inplace) $ do + putStrErrLn "cannot specify output files and inplace at the same time" + System.Exit.exitWith (System.Exit.ExitFailure 52) + let inputPaths' = nonEmptyList Nothing . map Just $ maybeToList inputParam ++ inputPaths - let outputPaths' = nonEmptyList Nothing . map Just $ outputPaths + let outputPaths' = if inplace then inputPaths' else nonEmptyList Nothing . map Just $ outputPaths when (length inputPaths' /= length outputPaths') $ do putStrErrLn "the number of inputs must match ther number of outputs" System.Exit.exitWith (System.Exit.ExitFailure 51) -- 2.30.2 From ccf2eb092f0f9755d64b180807fd69852504f8af Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sun, 1 Oct 2017 17:16:27 +0200 Subject: [PATCH 08/26] Support RecordWildCards, Add one-liner layouting for records fixes #52 --- src-literatetests/tests.blt | 15 +++ .../Brittany/Internal/Layouters/Expr.hs | 125 +++++++++++++----- .../Brittany/Internal/Layouters/Pattern.hs | 20 +++ 3 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src-literatetests/tests.blt b/src-literatetests/tests.blt index e54841b..f4db082 100644 --- a/src-literatetests/tests.blt +++ b/src-literatetests/tests.blt @@ -1042,6 +1042,21 @@ foo = cccc = () in foo +#test issue 52 a + +{-# LANGUAGE RecordWildCards #-} +v = A {a = 1, ..} where b = 2 + +#test issue 52 b + +{-# LANGUAGE RecordWildCards #-} +v = A {..} where b = 2 + +#test issue 52 c + +{-# LANGUAGE RecordWildCards #-} +v = A {a = 1, b = 2, c = 3} + ############################################################################### ############################################################################### diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs index a6ba345..2808df2 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs @@ -672,42 +672,103 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of then return Nothing else Just <$> docSharedWrapper layoutExpr fExpr return $ (fieldl, lrdrNameToText lnameF, fExpDoc) + let line1 appender wrapper = + [ appender $ docLit $ Text.pack "{" + , docWrapNodePrior fd1l $ appSep $ docLit $ fd1n + , case fd1e of + Just x -> docSeq + [ appSep $ docLit $ Text.pack "=" + , docWrapNodeRest fd1l $ wrapper $ x + ] + Nothing -> docEmpty + ] + let lineR wrapper = fdr <&> \(lfield, fText, fDoc) -> + [ docCommaSep + , appSep $ docLit $ fText + , case fDoc of + Just x -> docWrapNode lfield $ docSeq + [ appSep $ docLit $ Text.pack "=" + , wrapper x + ] + Nothing -> docEmpty + ] + let lineN = + [ docNodeAnnKW lexpr (Just AnnOpenC) docEmpty + , docLit $ Text.pack "}" + ] docAlt - [ docSetParSpacing + [ docSeq + $ [docNodeAnnKW lexpr Nothing $ nameDoc, docSeparator] + ++ line1 id docForceSingleline + ++ join (lineR docForceSingleline) + ++ lineN + , docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar (docNodeAnnKW lexpr Nothing $ nameDoc) - (docNonBottomSpacing $ docLines $ let - line1 = docCols ColRecUpdate - [ appSep $ docLit $ Text.pack "{" - , docWrapNodePrior fd1l $ appSep $ docLit $ fd1n - , case fd1e of - Just x -> docSeq - [ appSep $ docLit $ Text.pack "=" - , docWrapNodeRest fd1l $ docAddBaseY BrIndentRegular $ x - ] - Nothing -> docEmpty - ] - lineR = fdr <&> \(lfield, fText, fDoc) -> docCols ColRecUpdate - [ appSep $ docLit $ Text.pack "," - , appSep $ docLit $ fText - , case fDoc of - Just x -> docWrapNode lfield $ docSeq - [ appSep $ docLit $ Text.pack "=" - , docAddBaseY BrIndentRegular x - ] - Nothing -> docEmpty - ] - lineN = docSeq - [ docNodeAnnKW lexpr (Just AnnOpenC) docEmpty - , docLit $ Text.pack "}" - ] - in [line1] ++ lineR ++ [lineN]) - -- TODO oneliner (?) + ( docNonBottomSpacing + $ docLines + $ [docCols ColRecUpdate $ line1 appSep (docAddBaseY BrIndentRegular)] + ++ (docCols ColRecUpdate <$> lineR (docAddBaseY BrIndentRegular)) + ++ [docSeq lineN] + ) ] RecordCon lname _ _ (HsRecFields [] (Just 0)) -> do let t = lrdrNameToText lname docWrapNode lname $ docLit $ t <> Text.pack " {..}" + RecordCon lname _ _ (HsRecFields fs@(_:_) (Just dotdoti)) | dotdoti == length fs -> do + let nameDoc = docWrapNode lname $ docLit $ lrdrNameToText lname + ((fd1l, fd1n, fd1e):fdr) <- fs `forM` \fieldl@(L _ (HsRecField (L _ (FieldOcc lnameF _)) fExpr pun)) -> do + fExpDoc <- if pun + then return Nothing + else Just <$> docSharedWrapper layoutExpr fExpr + return $ (fieldl, lrdrNameToText lnameF, fExpDoc) + let line1 appender wrapper = + [ appender $ docLit $ Text.pack "{" + , docWrapNodePrior fd1l $ appSep $ docLit $ fd1n + , case fd1e of + Just x -> docSeq + [ appSep $ docLit $ Text.pack "=" + , docWrapNodeRest fd1l $ wrapper $ x + ] + Nothing -> docEmpty + ] + let lineR wrapper = fdr <&> \(lfield, fText, fDoc) -> + [ docCommaSep + , appSep $ docLit $ fText + , case fDoc of + Just x -> docWrapNode lfield $ docSeq + [ appSep $ docLit $ Text.pack "=" + , wrapper x + ] + Nothing -> docEmpty + ] + let lineDot = + [ docCommaSep + , docLit $ Text.pack ".." + ] + let lineN = + [ docNodeAnnKW lexpr (Just AnnOpenC) docEmpty + , docLit $ Text.pack "}" + ] + docAlt + [ docSeq + $ [docNodeAnnKW lexpr Nothing $ nameDoc, docSeparator] + ++ line1 id docForceSingleline + ++ join (lineR docForceSingleline) + ++ lineDot + ++ lineN + , docSetParSpacing + $ docAddBaseY BrIndentRegular + $ docPar + (docNodeAnnKW lexpr Nothing $ nameDoc) + ( docNonBottomSpacing + $ docLines + $ [docCols ColRecUpdate $ line1 appSep (docAddBaseY BrIndentRegular)] + ++ (docCols ColRecUpdate <$> lineR (docAddBaseY BrIndentRegular)) + ++ [docSeq lineDot, docSeq lineN] + ) + ] RecordCon{} -> unknownNodeError "RecordCon with puns" lexpr RecordUpd rExpr [] _ _ _ _ -> do @@ -755,7 +816,7 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of Nothing -> docEmpty ] lineR = rFr <&> \(lfield, fText, fDoc) -> docWrapNode lfield $ docCols ColRecUpdate - [ appSep $ docLit $ Text.pack "," + [ docCommaSep , appSep $ docLit $ fText , case fDoc of Just x -> docSeq [ appSep $ docLit $ Text.pack "=" @@ -785,7 +846,7 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of Nothing -> docEmpty ] lineR = rFr <&> \(lfield, fText, fDoc) -> docWrapNode lfield $ docCols ColRecUpdate - [ appSep $ docLit $ Text.pack "," + [ docCommaSep , appSep $ docLit $ fText , case fDoc of Just x -> docSeq [ appSep $ docLit $ Text.pack "=" @@ -829,7 +890,7 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of docSeq [ docLit $ Text.pack "[" , docForceSingleline e1Doc - , appSep $ docLit $ Text.pack "," + , docCommaSep , appSep $ docForceSingleline e2Doc , docLit $ Text.pack "..]" ] @@ -850,7 +911,7 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of docSeq [ docLit $ Text.pack "[" , docForceSingleline e1Doc - , appSep $ docLit $ Text.pack "," + , docCommaSep , appSep $ docForceSingleline e2Doc , appSep $ docLit $ Text.pack ".." , docForceSingleline eNDoc diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs index b36fcaa..3f66932 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs @@ -96,6 +96,26 @@ layoutPat lpat@(L _ pat) = docWrapNode lpat $ case pat of [ appSep $ docLit t , docLit $ Text.pack "{..}" ] + ConPatIn lname (RecCon (HsRecFields fs@(_:_) (Just dotdoti))) | dotdoti == length fs -> do + let t = lrdrNameToText lname + fds <- fs `forM` \(L _ (HsRecField (L _ (FieldOcc lnameF _)) fPat pun)) -> do + fExpDoc <- if pun + then return Nothing + else Just <$> docSharedWrapper layoutPat fPat + return $ (lrdrNameToText lnameF, fExpDoc) + fmap Seq.singleton $ docSeq + [ appSep $ docLit t + , appSep $ docLit $ Text.pack "{" + , docSeq $ fds >>= \case + (fieldName, Just fieldDoc) -> + [ appSep $ docLit $ fieldName + , appSep $ docLit $ Text.pack "=" + , fieldDoc >>= colsWrapPat + , docCommaSep + ] + (fieldName, Nothing) -> [docLit fieldName, docCommaSep] + , docLit $ Text.pack "..}" + ] TuplePat args boxity _ -> do case boxity of Boxed -> wrapPatListy args "(" ")" -- 2.30.2 From 95c40f2b1e2418945761b9a590db005e39c5b33b Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Mon, 2 Oct 2017 13:51:31 +0200 Subject: [PATCH 09/26] address review comments --- src-brittany/Main.hs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index ed21e50..6f2d4d8 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -14,7 +14,6 @@ import qualified Data.Map as Map import qualified Data.Text.Lazy.Builder as Text.Builder -import Control.Monad (foldM) import Data.CZipWith import qualified Debug.Trace as Trace @@ -130,15 +129,22 @@ mainCmdParser helpDesc = do when printHelp $ do liftIO $ print $ ppHelpShallow desc System.Exit.exitSuccess - when (length outputPaths > 0 && inplace) $ do - putStrErrLn "cannot specify output files and inplace at the same time" - System.Exit.exitWith (System.Exit.ExitFailure 52) - let inputPaths' = nonEmptyList Nothing . map Just $ maybeToList inputParam ++ inputPaths - let outputPaths' = if inplace then inputPaths' else nonEmptyList Nothing . map Just $ outputPaths + let inputPaths' = case maybeToList inputParam ++ inputPaths of + [] -> [Nothing] + ps -> map Just ps + + outputPaths' <- case outputPaths of + [] | not inplace -> return [Nothing] + [] -> return inputPaths' + ps | not inplace -> return . map Just $ ps + _ -> do + putStrErrLn "cannot specify output files and inplace at the same time" + System.Exit.exitWith (System.Exit.ExitFailure 51) + when (length inputPaths' /= length outputPaths') $ do putStrErrLn "the number of inputs must match ther number of outputs" - System.Exit.exitWith (System.Exit.ExitFailure 51) + System.Exit.exitWith (System.Exit.ExitFailure 52) config <- runMaybeT (readConfigs cmdlineConfig configPaths) >>= \case Nothing -> System.Exit.exitWith (System.Exit.ExitFailure 53) @@ -147,19 +153,10 @@ mainCmdParser helpDesc = do trace (showConfigYaml config) $ return () let ios = zipWith (coreIO putStrErrLn config suppressOutput) inputPaths' outputPaths' - errNoM <- foldM run Nothing ios - case errNoM of - Just errNo -> System.Exit.exitWith (System.Exit.ExitFailure errNo) - Nothing -> pure () - where - run acc io = do - res <- io + res <- fmap sequence_ $ sequence ios case res of - Left _ -> return (Just 1) - Right () -> return acc - - nonEmptyList def [] = [def] - nonEmptyList _ x = x + Left _ -> System.Exit.exitWith (System.Exit.ExitFailure 1) + Right _ -> pure () -- | The main IO parts for the default mode of operation, and after commandline -- 2.30.2 From a0112524aa4752f089758bbfdbe55fbde6566e8d Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Mon, 2 Oct 2017 20:50:51 +0200 Subject: [PATCH 10/26] Split up littests input into multiple files *.blt instead of just tests.blt yay for unix-style for ordering the inputs "15-regression.blt" --- brittany.cabal | 3 +- src-literatetests/{tests.blt => 10-tests.blt} | 504 ------------------ src-literatetests/15-regressions.blt | 467 ++++++++++++++++ src-literatetests/16-pending.blt | 35 ++ src-literatetests/Main.hs | 7 +- 5 files changed, 509 insertions(+), 507 deletions(-) rename src-literatetests/{tests.blt => 10-tests.blt} (50%) create mode 100644 src-literatetests/15-regressions.blt create mode 100644 src-literatetests/16-pending.blt diff --git a/brittany.cabal b/brittany.cabal index 957fb06..2294238 100644 --- a/brittany.cabal +++ b/brittany.cabal @@ -24,7 +24,7 @@ extra-doc-files: { doc/implementation/*.md } extra-source-files: { - src-literatetests/tests.blt + src-literatetests/*.blt } source-repository head { @@ -330,6 +330,7 @@ test-suite littests , czipwith , ghc-boot-th , hspec >=2.4.1 && <2.5 + , filepath , parsec >=3.1.11 && <3.2 } ghc-options: -Wall diff --git a/src-literatetests/tests.blt b/src-literatetests/10-tests.blt similarity index 50% rename from src-literatetests/tests.blt rename to src-literatetests/10-tests.blt index f4db082..696cbb6 100644 --- a/src-literatetests/tests.blt +++ b/src-literatetests/10-tests.blt @@ -589,507 +589,3 @@ func = ] ++ [ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc] - -############################################################################### -############################################################################### -############################################################################### -#group regression -############################################################################### -############################################################################### -############################################################################### - -#test newlines-comment -func = do - abc <- foo - ---abc -return () - -#test parenthesis-around-unit -func = (()) - -#test let-defs indentation -func = do - let foo True = True - foo _ = False - return () - -#test record update indentation 1 -func = do - s <- mGet - mSet $ s { _lstate_indent = _lstate_indent state } - -#test record update indentation 2 -func = do - s <- mGet - mSet $ s { _lstate_indent = _lstate_indent state - , _lstate_indent = _lstate_indent state - } - -#test record update indentation 3 -func = do - s <- mGet - mSet $ s - { _lstate_indent = _lstate_indent lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd - , _lstate_indent = _lstate_indent lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd - } - -#test post-indent comment -func = do --- abc - -- def - return () - -#test post-unindent comment -func = do - do - return () - -- abc - -- def - return () - -#test CPP empty comment case -#pending CPP parsing needs fixing for roundTripEqual -{-# LANGUAGE CPP #-} -module Test where -func = do -#if FOO - let x = 13 -#endif - stmt x - -## really, the following should be handled by forcing the Alt to multiline -## because there are comments. as long as this is not implemented though, -## we should ensure the trivial solution works. -#test comment inline placement (temporary) -func - :: Int -- basic indentation amount - -> Int -- currently used width in current line (after indent) - -- used to accurately calc placing of the current-line - -> LayoutDesc - -> Int - -#test some indentation thingy -func = - ( lkjadljasldjalskdjaldjalsdjkalsdjlaksdjlasjdlajsaldskj - $ abc - $ def - $ ghi - $ jkl - ) - -#test parenthesized operator -buildG bounds0 edges0 = accumArray (flip (:)) [] bounds0 (map reassoc edges0) - where reassoc (v, e, w) = (v, (e, w)) - -#test record pattern matching stuff -downloadRepoPackage = case repo of - RepoLocal {..} -> return () - RepoLocal { abc } -> return () - RepoLocal{} -> return () - -#test do let comment indentation level problem -func = do - let (primaryPkg, otherPkgs) = selectPrimaryLocalPackage pwd pkgs' - (bproblems, x) = resolveBuildTargets primaryPkg otherPkgs utargets'' - -- default local dir target if there's no given target - utargets'' = "foo" - return () - -#test list comprehension comment placement -func = - [ (thing, take 10 alts) --TODO: select best ones - | (thing, _got, alts@(_:_)) <- nosuchFooThing - , gast <- award - ] - -#test if-then-else comment placement -func = if x - then if y -- y is important - then foo - else bar - else Nothing - -#test qualified infix pattern -#pending "TODO" -wrapPatPrepend pat prepElem = do - patDocs <- layoutPat pat - case Seq.viewl patDocs of - Seq.EmptyL -> return $ Seq.empty - x1 Seq.:< xR -> do - x1' <- docSeq [prepElem, return x1] - return $ x1' Seq.<| xR - -#test type signature multiline forcing issue -layoutWriteNewlineBlock - :: ( MonadMultiWriter Text.Builder.Builder m - , MonadMultiState LayoutState m - , MonadMultiWriter (Seq String) m - ) - => m () - -#test multiwayif proper indentation -{-# LANGUAGE MultiWayIf #-} -readMergePersConfig path shouldCreate conf = do - exists <- liftIO $ System.Directory.doesFileExist path - if - | exists -> do - contents <- liftIO $ ByteString.readFile path -- no lazy IO, tyvm. - fileConf <- case Data.Yaml.decodeEither contents of - Left e -> do - liftIO - $ putStrErrLn - $ "error reading in brittany config from " - ++ path - ++ ":" - liftIO $ putStrErrLn e - mzero - Right x -> return x - return $ fileConf Semigroup.<> conf - | shouldCreate -> do - liftIO $ ByteString.writeFile path $ Data.Yaml.encode $ cMap - (Option . Just . runIdentity) - staticDefaultConfig - return $ conf - | otherwise -> do - return conf - -#test nested pattern alignment issue" -func = BuildReport - where - convertInstallOutcome = case result of - Left BR.PlanningFailed -> PlanningFailed - Left (BR.DependentFailed p) -> DependencyFailed p - Left (BR.DownloadFailed _) -> DownloadFailed - Left (BR.UnpackFailed _) -> UnpackFailed - Left (BR.ConfigureFailed _) -> ConfigureFailed - Left (BR.BuildFailed _) -> BuildFailed - Left (BR.TestsFailed _) -> TestsFailed - Left (BR.InstallFailed _) -> InstallFailed - Right (BR.BuildOk _ _ _ ) -> InstallOk - -#test nested pattern alignment issue" -func = BuildReport - where - convertInstallOutcome = case result of - Left BR.PlanningFailed -> PlanningFailed - Left (BR.DependentFailed p) -> DependencyFailed p - Left (BR.DownloadFailed _) -> DownloadFailed - Left (BR.UnpackFailed _) -> UnpackFailed - Left (BR.ConfigureFailed _) -> ConfigureFailed - Left (BR.BuildFailed _) -> BuildFailed - Left (BR.TestsFailed _) -> TestsFailed - Left (BR.InstallFailed _) -> InstallFailed - Right (BR.BuildOk _ _ _ ) -> InstallOk - -#test partially overflowing alignment issue" -showPackageDetailedInfo pkginfo = - renderStyle (style { lineLength = 80, ribbonsPerLine = 1 }) - $ char '*' - $+$ something - [ entry "Synopsis" synopsis hideIfNull reflowParagraphs - , entry "Versions available" - sourceVersions - (altText null "[ Not available from server ]") - (dispTopVersions 9 (preferredVersions pkginfo)) - , entry - "Versions installed" - installedVersions - ( altText - null - (if hasLib pkginfo then "[ Not installed ]" else "[ Unknown ]") - ) - (dispTopVersions 4 (preferredVersions pkginfo)) - , entry "Homepage" homepage orNotSpecified text - , entry "Bug reports" bugReports orNotSpecified text - , entry "Description" description hideIfNull reflowParagraphs - , entry "Category" category hideIfNull text - , entry "License" license alwaysShow disp - , entry "Author" author hideIfNull reflowLines - , entry "Maintainer" maintainer hideIfNull reflowLines - , entry "Source repo" sourceRepo orNotSpecified text - , entry "Executables" executables hideIfNull (commaSep text) - , entry "Flags" flags hideIfNull (commaSep dispFlag) - , entry "Dependencies" dependencies hideIfNull (commaSep dispExtDep) - , entry "Documentation" haddockHtml showIfInstalled text - , entry "Cached" haveTarball alwaysShow dispYesNo - , if not (hasLib pkginfo) - then empty - else text "Modules:" - $+$ nest 4 (vcat (map disp . sort . modules $ pkginfo)) - ] - -#test issue 7a -isValidPosition position | validX && validY = Just position - | otherwise = Nothing - -#test issue-6-pattern-linebreak-validity -## this is ugly, but at least syntactically valid. -foo = Reflex.runSpiderHost $ ReflexHost.hostApp $ do - (inputEvent :: Reflex.Event Reflex.Spider String, inputFire :: String - -> IO Bool ) <- - ReflexHost.newExternalEvent - liftIO . forkIO . forever $ getLine >>= inputFire - ReflexHost.performEvent_ $ fmap (liftIO . putStrLn) inputEvent - -#test issue 16 -foldrDesc f z = unSwitchQueue $ \q -> - switch (Min.foldrDesc (f unTaggedF) z q) (Min.foldrAsc (f unTaggedF) z q) - -#test issue 18 -autocheckCases = - [ ("Never Deadlocks" , representative deadlocksNever) - , ("No Exceptions" , representative exceptionsNever) - , ("Consistent Result", alwaysSame) -- already representative - ] - -#test issue 18b -autocheckCases = - [ ("Never Deadlocks", representative deadlocksNever) - , ("No Exceptions" , representative exceptionsNever) - , ( "Consistent Result" - , alwaysSame -- already representative - ) - ] - -#test issue 18c -func = - [ (abc, (1111, 1111)) - , (def, (2, 2)) - , foo -- comment - ] - -#test issue 26 -foo a b = g a b -- fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo - where g a b = b + b * a - -#test issue 26b -foo a b = g a b where g a b = b + b * a -- fooooooooooooooooooooooooooooooooooo - -#test aggressive alignment 1 -func = do - abc <- expr - abcccccccccccccccccc <- expr - abcccccccccccccccccccccccccccccccccccccccccc <- expr - abccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc <- expr - -#test example alignment 1 -func (MyLongFoo abc def) = 1 -func (Bar a d ) = 2 -func _ = 3 - -#test listcomprehension-case-of -parserCompactLocation = - [ try - $ [ ParseRelAbs (Text.Read.read digits) _ _ - | digits <- many1 digit - , rel1 :: Maybe (Either Int (Ratio Int)) <- optionMaybe - [ case divPart of - Nothing -> Left $ Text.Read.read digits - Just ddigits -> - Right $ Text.Read.read digits % Text.Read.read ddigits - | digits <- many1 digit - , divPart <- optionMaybe (string "/" *> many1 digit) - ] - ] - ] - -#test opapp-specialcasing-1 -func = fooooooooooooooooooooooooooooooooo $ foooooooooooooooooooooooooooooooo - foooooooooooooooooooooooooooooooo - foooooooooooooooooooooooooooooooo - -#test opapp-specialcasing-2 -func = - fooooooooooooooooooooooooooooooooo - + foooooooooooooooooooooooooooooooo foooooooooooooooooooooooooooooooo - foooooooooooooooooooooooooooooooo - -#test opapp-specialcasing-3 -func = fooooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo - [ foooooooooooooooooooooooooooooooo - , foooooooooooooooooooooooooooooooo - , foooooooooooooooooooooooooooooooo - ] - -#test opapp-indenting -parserPrim = - [ r - | r <- - [ SGPPrimFloat $ bool id (0-) minus $ readGnok "parserPrim" - (d1 ++ d2 ++ d3 ++ d4) - | d2 <- string "." - , d3 <- many1 (oneOf "0123456789") - , _ <- string "f" - ] - <|> [ SGPPrimFloat $ bool id (0-) minus $ fromIntegral - (readGnok "parserPrim" d1 :: Integer) - | _ <- string "f" - ] - <|> [ SGPPrimInt $ bool id (0-) minus $ fromIntegral - (readGnok "parserPrim" d1 :: Integer) - | _ <- string "i" - ] - ] - -#test another-parspacing-testcase - -samples = (SV.unpackaaaaadat) <&> \f -> - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -#test recordupd-singleline-bug - -runBrittany tabSize text = do - let - config' = staticDefaultConfig - config = config' - { _conf_layout = (_conf_layout config') { _lconfig_indentAmount = coerce - tabSize - } - , _conf_forward = forwardOptionsSyntaxExtsEnabled - } - parsePrintModule config text - -#test issue 38 - -{-# LANGUAGE TypeApplications #-} -foo = bar @Baz - -#test comment-before-BDCols -{-# LANGUAGE TypeApplications #-} -layoutPatternBindFinal alignmentToken binderDoc mPatDoc clauseDocs = do - docAlt - $ -- one-line solution - [ docCols - (ColBindingLine alignmentToken) - [ docSeq (patPartInline ++ [guardPart]) - , docSeq - [ appSep $ return binderDoc - , docForceSingleline $ return body - , wherePart - ] - ] - | not hasComments - , [(guards, body, _bodyRaw)] <- [clauseDocs] - , let guardPart = singleLineGuardsDoc guards - , wherePart <- case mWhereDocs of - Nothing -> return @[] $ docEmpty - Just [w] -> return @[] $ docSeq - [ docSeparator - , appSep $ docLit $ Text.pack "where" - , docSetIndentLevel $ docForceSingleline $ return w - ] - _ -> [] - ] - ++ -- one-line solution + where in next line(s) - [ docLines - $ [ docCols - (ColBindingLine alignmentToken) - [ docSeq (patPartInline ++ [guardPart]) - , docSeq - [appSep $ return binderDoc, docForceParSpacing $ return body] - ] - ] - ++ wherePartMultiLine - | [(guards, body, _bodyRaw)] <- [clauseDocs] - , let guardPart = singleLineGuardsDoc guards - , Data.Maybe.isJust mWhereDocs - ] - ++ -- two-line solution + where in next line(s) - [ docLines - $ [ docForceSingleline - $ docSeq (patPartInline ++ [guardPart, return binderDoc]) - , docEnsureIndent BrIndentRegular $ docForceSingleline $ return - body - ] - ++ wherePartMultiLine - | [(guards, body, _bodyRaw)] <- [clauseDocs] - , let guardPart = singleLineGuardsDoc guards - ] - -#test comment-testcase-17 -{-# LANGUAGE MultiWayIf #-} -func = do - let foo = if - | Constuctoooooooooooooooooooooooooooooooooooor `elem` artics -- TODO - -> max - (defLen - 0.2) -- TODO - (defLen * 0.8) - | otherwise -> max (defLen - 0.05) (defLen * 0.95) -- TODO - return True - -#test issue 49 - -foo n = case n of - 1 -> True - -1 -> False - -bar n = case n of - (-2, -2) -> (-2, -2) - -#test issue 48 a - -foo = - let a = b@1 - cccc = () - in foo - -#test issue 48 b - -{-# LANGUAGE TypeApplications #-} -foo = - let a = b @1 - cccc = () - in foo - -#test issue 52 a - -{-# LANGUAGE RecordWildCards #-} -v = A {a = 1, ..} where b = 2 - -#test issue 52 b - -{-# LANGUAGE RecordWildCards #-} -v = A {..} where b = 2 - -#test issue 52 c - -{-# LANGUAGE RecordWildCards #-} -v = A {a = 1, b = 2, c = 3} - - -############################################################################### -############################################################################### -############################################################################### -#group pending -############################################################################### -############################################################################### -############################################################################### - - - -## this testcase is not about idempotency, but about _how_ the output differs -## from the input; i cannot really express this yet with the current -## test-suite. -## #test ayaz -## -## myManageHook = -## composeOne [isFullscreen -?> doFullFloat, isDialog -?> doFloat, transience] -## <+> composeAll -## [ className =? "Pidgin" --> doFloat -## , className =? "XCalc" --> doFloat -## -- plan9port's acme -## , className =? "acme" --> doFloat -## -- Acme with Vi bindings editor -## , title =? "ED" --> doFloat -## , title =? "wlc-x11" --> doFloat -## , className =? "Skype" --> doFloat -## , className =? "ffplay" --> doFloat -## , className =? "mpv" --> doFloat -## , className =? "Plugin-container" --> doFloat -- Firefox flash, etc. -## -- Firefox works well tiled, but it has dialog windows we want to float. -## , appName =? "Browser" --> doFloat -## ] -## where -## role = stringProperty "WM_WINDOW_ROLE" - diff --git a/src-literatetests/15-regressions.blt b/src-literatetests/15-regressions.blt new file mode 100644 index 0000000..0a63b7a --- /dev/null +++ b/src-literatetests/15-regressions.blt @@ -0,0 +1,467 @@ +############################################################################### +############################################################################### +############################################################################### +#group regression +############################################################################### +############################################################################### +############################################################################### + +#test newlines-comment +func = do + abc <- foo + +--abc +return () + +#test parenthesis-around-unit +func = (()) + +#test let-defs indentation +func = do + let foo True = True + foo _ = False + return () + +#test record update indentation 1 +func = do + s <- mGet + mSet $ s { _lstate_indent = _lstate_indent state } + +#test record update indentation 2 +func = do + s <- mGet + mSet $ s { _lstate_indent = _lstate_indent state + , _lstate_indent = _lstate_indent state + } + +#test record update indentation 3 +func = do + s <- mGet + mSet $ s + { _lstate_indent = _lstate_indent lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd + , _lstate_indent = _lstate_indent lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd + } + +#test post-indent comment +func = do +-- abc + -- def + return () + +#test post-unindent comment +func = do + do + return () + -- abc + -- def + return () + +#test CPP empty comment case +#pending CPP parsing needs fixing for roundTripEqual +{-# LANGUAGE CPP #-} +module Test where +func = do +#if FOO + let x = 13 +#endif + stmt x + +## really, the following should be handled by forcing the Alt to multiline +## because there are comments. as long as this is not implemented though, +## we should ensure the trivial solution works. +#test comment inline placement (temporary) +func + :: Int -- basic indentation amount + -> Int -- currently used width in current line (after indent) + -- used to accurately calc placing of the current-line + -> LayoutDesc + -> Int + +#test some indentation thingy +func = + ( lkjadljasldjalskdjaldjalsdjkalsdjlaksdjlasjdlajsaldskj + $ abc + $ def + $ ghi + $ jkl + ) + +#test parenthesized operator +buildG bounds0 edges0 = accumArray (flip (:)) [] bounds0 (map reassoc edges0) + where reassoc (v, e, w) = (v, (e, w)) + +#test record pattern matching stuff +downloadRepoPackage = case repo of + RepoLocal {..} -> return () + RepoLocal { abc } -> return () + RepoLocal{} -> return () + +#test do let comment indentation level problem +func = do + let (primaryPkg, otherPkgs) = selectPrimaryLocalPackage pwd pkgs' + (bproblems, x) = resolveBuildTargets primaryPkg otherPkgs utargets'' + -- default local dir target if there's no given target + utargets'' = "foo" + return () + +#test list comprehension comment placement +func = + [ (thing, take 10 alts) --TODO: select best ones + | (thing, _got, alts@(_:_)) <- nosuchFooThing + , gast <- award + ] + +#test if-then-else comment placement +func = if x + then if y -- y is important + then foo + else bar + else Nothing + +#test qualified infix pattern +#pending "TODO" +wrapPatPrepend pat prepElem = do + patDocs <- layoutPat pat + case Seq.viewl patDocs of + Seq.EmptyL -> return $ Seq.empty + x1 Seq.:< xR -> do + x1' <- docSeq [prepElem, return x1] + return $ x1' Seq.<| xR + +#test type signature multiline forcing issue +layoutWriteNewlineBlock + :: ( MonadMultiWriter Text.Builder.Builder m + , MonadMultiState LayoutState m + , MonadMultiWriter (Seq String) m + ) + => m () + +#test multiwayif proper indentation +{-# LANGUAGE MultiWayIf #-} +readMergePersConfig path shouldCreate conf = do + exists <- liftIO $ System.Directory.doesFileExist path + if + | exists -> do + contents <- liftIO $ ByteString.readFile path -- no lazy IO, tyvm. + fileConf <- case Data.Yaml.decodeEither contents of + Left e -> do + liftIO + $ putStrErrLn + $ "error reading in brittany config from " + ++ path + ++ ":" + liftIO $ putStrErrLn e + mzero + Right x -> return x + return $ fileConf Semigroup.<> conf + | shouldCreate -> do + liftIO $ ByteString.writeFile path $ Data.Yaml.encode $ cMap + (Option . Just . runIdentity) + staticDefaultConfig + return $ conf + | otherwise -> do + return conf + +#test nested pattern alignment issue" +func = BuildReport + where + convertInstallOutcome = case result of + Left BR.PlanningFailed -> PlanningFailed + Left (BR.DependentFailed p) -> DependencyFailed p + Left (BR.DownloadFailed _) -> DownloadFailed + Left (BR.UnpackFailed _) -> UnpackFailed + Left (BR.ConfigureFailed _) -> ConfigureFailed + Left (BR.BuildFailed _) -> BuildFailed + Left (BR.TestsFailed _) -> TestsFailed + Left (BR.InstallFailed _) -> InstallFailed + Right (BR.BuildOk _ _ _ ) -> InstallOk + +#test nested pattern alignment issue" +func = BuildReport + where + convertInstallOutcome = case result of + Left BR.PlanningFailed -> PlanningFailed + Left (BR.DependentFailed p) -> DependencyFailed p + Left (BR.DownloadFailed _) -> DownloadFailed + Left (BR.UnpackFailed _) -> UnpackFailed + Left (BR.ConfigureFailed _) -> ConfigureFailed + Left (BR.BuildFailed _) -> BuildFailed + Left (BR.TestsFailed _) -> TestsFailed + Left (BR.InstallFailed _) -> InstallFailed + Right (BR.BuildOk _ _ _ ) -> InstallOk + +#test partially overflowing alignment issue" +showPackageDetailedInfo pkginfo = + renderStyle (style { lineLength = 80, ribbonsPerLine = 1 }) + $ char '*' + $+$ something + [ entry "Synopsis" synopsis hideIfNull reflowParagraphs + , entry "Versions available" + sourceVersions + (altText null "[ Not available from server ]") + (dispTopVersions 9 (preferredVersions pkginfo)) + , entry + "Versions installed" + installedVersions + ( altText + null + (if hasLib pkginfo then "[ Not installed ]" else "[ Unknown ]") + ) + (dispTopVersions 4 (preferredVersions pkginfo)) + , entry "Homepage" homepage orNotSpecified text + , entry "Bug reports" bugReports orNotSpecified text + , entry "Description" description hideIfNull reflowParagraphs + , entry "Category" category hideIfNull text + , entry "License" license alwaysShow disp + , entry "Author" author hideIfNull reflowLines + , entry "Maintainer" maintainer hideIfNull reflowLines + , entry "Source repo" sourceRepo orNotSpecified text + , entry "Executables" executables hideIfNull (commaSep text) + , entry "Flags" flags hideIfNull (commaSep dispFlag) + , entry "Dependencies" dependencies hideIfNull (commaSep dispExtDep) + , entry "Documentation" haddockHtml showIfInstalled text + , entry "Cached" haveTarball alwaysShow dispYesNo + , if not (hasLib pkginfo) + then empty + else text "Modules:" + $+$ nest 4 (vcat (map disp . sort . modules $ pkginfo)) + ] + +#test issue 7a +isValidPosition position | validX && validY = Just position + | otherwise = Nothing + +#test issue-6-pattern-linebreak-validity +## this is ugly, but at least syntactically valid. +foo = Reflex.runSpiderHost $ ReflexHost.hostApp $ do + (inputEvent :: Reflex.Event Reflex.Spider String, inputFire :: String + -> IO Bool ) <- + ReflexHost.newExternalEvent + liftIO . forkIO . forever $ getLine >>= inputFire + ReflexHost.performEvent_ $ fmap (liftIO . putStrLn) inputEvent + +#test issue 16 +foldrDesc f z = unSwitchQueue $ \q -> + switch (Min.foldrDesc (f unTaggedF) z q) (Min.foldrAsc (f unTaggedF) z q) + +#test issue 18 +autocheckCases = + [ ("Never Deadlocks" , representative deadlocksNever) + , ("No Exceptions" , representative exceptionsNever) + , ("Consistent Result", alwaysSame) -- already representative + ] + +#test issue 18b +autocheckCases = + [ ("Never Deadlocks", representative deadlocksNever) + , ("No Exceptions" , representative exceptionsNever) + , ( "Consistent Result" + , alwaysSame -- already representative + ) + ] + +#test issue 18c +func = + [ (abc, (1111, 1111)) + , (def, (2, 2)) + , foo -- comment + ] + +#test issue 26 +foo a b = g a b -- fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo + where g a b = b + b * a + +#test issue 26b +foo a b = g a b where g a b = b + b * a -- fooooooooooooooooooooooooooooooooooo + +#test aggressive alignment 1 +func = do + abc <- expr + abcccccccccccccccccc <- expr + abcccccccccccccccccccccccccccccccccccccccccc <- expr + abccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc <- expr + +#test example alignment 1 +func (MyLongFoo abc def) = 1 +func (Bar a d ) = 2 +func _ = 3 + +#test listcomprehension-case-of +parserCompactLocation = + [ try + $ [ ParseRelAbs (Text.Read.read digits) _ _ + | digits <- many1 digit + , rel1 :: Maybe (Either Int (Ratio Int)) <- optionMaybe + [ case divPart of + Nothing -> Left $ Text.Read.read digits + Just ddigits -> + Right $ Text.Read.read digits % Text.Read.read ddigits + | digits <- many1 digit + , divPart <- optionMaybe (string "/" *> many1 digit) + ] + ] + ] + +#test opapp-specialcasing-1 +func = fooooooooooooooooooooooooooooooooo $ foooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo + +#test opapp-specialcasing-2 +func = + fooooooooooooooooooooooooooooooooo + + foooooooooooooooooooooooooooooooo foooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo + +#test opapp-specialcasing-3 +func = fooooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo + [ foooooooooooooooooooooooooooooooo + , foooooooooooooooooooooooooooooooo + , foooooooooooooooooooooooooooooooo + ] + +#test opapp-indenting +parserPrim = + [ r + | r <- + [ SGPPrimFloat $ bool id (0-) minus $ readGnok "parserPrim" + (d1 ++ d2 ++ d3 ++ d4) + | d2 <- string "." + , d3 <- many1 (oneOf "0123456789") + , _ <- string "f" + ] + <|> [ SGPPrimFloat $ bool id (0-) minus $ fromIntegral + (readGnok "parserPrim" d1 :: Integer) + | _ <- string "f" + ] + <|> [ SGPPrimInt $ bool id (0-) minus $ fromIntegral + (readGnok "parserPrim" d1 :: Integer) + | _ <- string "i" + ] + ] + +#test another-parspacing-testcase + +samples = (SV.unpackaaaaadat) <&> \f -> + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +#test recordupd-singleline-bug + +runBrittany tabSize text = do + let + config' = staticDefaultConfig + config = config' + { _conf_layout = (_conf_layout config') { _lconfig_indentAmount = coerce + tabSize + } + , _conf_forward = forwardOptionsSyntaxExtsEnabled + } + parsePrintModule config text + +#test issue 38 + +{-# LANGUAGE TypeApplications #-} +foo = bar @Baz + +#test comment-before-BDCols +{-# LANGUAGE TypeApplications #-} +layoutPatternBindFinal alignmentToken binderDoc mPatDoc clauseDocs = do + docAlt + $ -- one-line solution + [ docCols + (ColBindingLine alignmentToken) + [ docSeq (patPartInline ++ [guardPart]) + , docSeq + [ appSep $ return binderDoc + , docForceSingleline $ return body + , wherePart + ] + ] + | not hasComments + , [(guards, body, _bodyRaw)] <- [clauseDocs] + , let guardPart = singleLineGuardsDoc guards + , wherePart <- case mWhereDocs of + Nothing -> return @[] $ docEmpty + Just [w] -> return @[] $ docSeq + [ docSeparator + , appSep $ docLit $ Text.pack "where" + , docSetIndentLevel $ docForceSingleline $ return w + ] + _ -> [] + ] + ++ -- one-line solution + where in next line(s) + [ docLines + $ [ docCols + (ColBindingLine alignmentToken) + [ docSeq (patPartInline ++ [guardPart]) + , docSeq + [appSep $ return binderDoc, docForceParSpacing $ return body] + ] + ] + ++ wherePartMultiLine + | [(guards, body, _bodyRaw)] <- [clauseDocs] + , let guardPart = singleLineGuardsDoc guards + , Data.Maybe.isJust mWhereDocs + ] + ++ -- two-line solution + where in next line(s) + [ docLines + $ [ docForceSingleline + $ docSeq (patPartInline ++ [guardPart, return binderDoc]) + , docEnsureIndent BrIndentRegular $ docForceSingleline $ return + body + ] + ++ wherePartMultiLine + | [(guards, body, _bodyRaw)] <- [clauseDocs] + , let guardPart = singleLineGuardsDoc guards + ] + +#test comment-testcase-17 +{-# LANGUAGE MultiWayIf #-} +func = do + let foo = if + | Constuctoooooooooooooooooooooooooooooooooooor `elem` artics -- TODO + -> max + (defLen - 0.2) -- TODO + (defLen * 0.8) + | otherwise -> max (defLen - 0.05) (defLen * 0.95) -- TODO + return True + +#test issue 49 + +foo n = case n of + 1 -> True + -1 -> False + +bar n = case n of + (-2, -2) -> (-2, -2) + +#test issue 48 a + +foo = + let a = b@1 + cccc = () + in foo + +#test issue 48 b + +{-# LANGUAGE TypeApplications #-} +foo = + let a = b @1 + cccc = () + in foo + +#test issue 52 a + +{-# LANGUAGE RecordWildCards #-} +v = A {a = 1, ..} where b = 2 + +#test issue 52 b + +{-# LANGUAGE RecordWildCards #-} +v = A {..} where b = 2 + +#test issue 52 c + +{-# LANGUAGE RecordWildCards #-} +v = A {a = 1, b = 2, c = 3} + diff --git a/src-literatetests/16-pending.blt b/src-literatetests/16-pending.blt new file mode 100644 index 0000000..c8147d8 --- /dev/null +++ b/src-literatetests/16-pending.blt @@ -0,0 +1,35 @@ +############################################################################### +############################################################################### +############################################################################### +#group pending +############################################################################### +############################################################################### +############################################################################### + + + +## this testcase is not about idempotency, but about _how_ the output differs +## from the input; i cannot really express this yet with the current +## test-suite. +## #test ayaz +## +## myManageHook = +## composeOne [isFullscreen -?> doFullFloat, isDialog -?> doFloat, transience] +## <+> composeAll +## [ className =? "Pidgin" --> doFloat +## , className =? "XCalc" --> doFloat +## -- plan9port's acme +## , className =? "acme" --> doFloat +## -- Acme with Vi bindings editor +## , title =? "ED" --> doFloat +## , title =? "wlc-x11" --> doFloat +## , className =? "Skype" --> doFloat +## , className =? "ffplay" --> doFloat +## , className =? "mpv" --> doFloat +## , className =? "Plugin-container" --> doFloat -- Firefox flash, etc. +## -- Firefox works well tiled, but it has dialog windows we want to float. +## , appName =? "Browser" --> doFloat +## ] +## where +## role = stringProperty "WM_WINDOW_ROLE" + diff --git a/src-literatetests/Main.hs b/src-literatetests/Main.hs index fe966e6..34b4e4e 100644 --- a/src-literatetests/Main.hs +++ b/src-literatetests/Main.hs @@ -24,6 +24,7 @@ import Language.Haskell.Brittany.Internal.Config import Data.Coerce ( coerce ) import qualified Data.Text.IO as Text.IO +import System.FilePath ( (</>) ) @@ -38,8 +39,10 @@ data InputLine main :: IO () main = do - input <- Text.IO.readFile "src-literatetests/tests.blt" - let groups = createChunks input + files <- System.Directory.listDirectory "src-literatetests/" + let blts = List.sort $ filter (".blt" `isSuffixOf`) files + inputs <- blts `forM` \blt -> Text.IO.readFile ("src-literatetests" </> blt) + let groups = createChunks =<< inputs hspec $ groups `forM_` \(groupname, tests) -> do describe (Text.unpack groupname) $ tests `forM_` \(name, pend, inp) -> do (if pend then before_ pending else id) -- 2.30.2 From f21c6b6eacad4542a2c02606d43ba70814d7a919 Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Tue, 3 Oct 2017 23:32:36 +0200 Subject: [PATCH 11/26] rework the cli interface --- brittany.cabal | 8 +++---- src-brittany/Main.hs | 54 +++++++++++++++++++++++++------------------- stack.yaml | 2 +- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/brittany.cabal b/brittany.cabal index 2294238..ca639b8 100644 --- a/brittany.cabal +++ b/brittany.cabal @@ -102,7 +102,7 @@ library { , pretty >=1.1.3.3 && <1.2 , bytestring >=0.10.8.1 && <0.11 , directory >=1.2.6.2 && <1.4 - , butcher >=1.1.0.0 && <1.2 + , butcher >=1.2 && <1.3 , yaml >=0.8.18 && <0.9 , aeson >=1.0.1.0 && <1.3 , extra >=1.4.10 && <1.7 @@ -147,7 +147,7 @@ executable brittany other-modules: { Paths_brittany } - -- other-extensions: + -- other-extensions: build-depends: { brittany , base @@ -335,7 +335,7 @@ test-suite littests } ghc-options: -Wall main-is: Main.hs - other-modules: + other-modules: hs-source-dirs: src-literatetests default-extensions: { CPP @@ -379,7 +379,7 @@ test-suite libinterfacetests } ghc-options: -Wall main-is: Main.hs - other-modules: + other-modules: hs-source-dirs: src-libinterfacetests default-extensions: { FlexibleContexts diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 8edc6b6..4f6992e 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -12,8 +12,12 @@ import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types import qualified Language.Haskell.GHC.ExactPrint.Parsers as ExactPrint.Parsers import qualified Data.Map as Map +import Text.Read (Read(..)) +import qualified Text.ParserCombinators.ReadP as ReadP +import qualified Text.ParserCombinators.ReadPrec as ReadPrec import qualified Data.Text.Lazy.Builder as Text.Builder +import Control.Monad (zipWithM) import Data.CZipWith import qualified Debug.Trace as Trace @@ -39,6 +43,17 @@ import qualified GHC.LanguageExtensions.Type as GHC import Paths_brittany +data WriteMode = Display | Inplace + +instance Read WriteMode where + readPrec = val "display" Display <|> val "inplace" Inplace + where + val iden v = ReadPrec.lift $ ReadP.string iden >> return v + +instance Show WriteMode where + show Display = "display" + show Inplace = "inplace" + main :: IO () main = mainFromCmdParserWithHelpDesc mainCmdParser @@ -103,8 +118,6 @@ mainCmdParser helpDesc = do printHelp <- addSimpleBoolFlag "h" ["help"] mempty printVersion <- addSimpleBoolFlag "" ["version"] mempty printLicense <- addSimpleBoolFlag "" ["license"] mempty - inputPaths <- addFlagStringParams "i" ["input"] "PATH" (flagHelpStr "paths to input haskell source files") - outputPaths <- addFlagStringParams "o" ["output"] "PATH" (flagHelpStr "output file paths") configPaths <- addFlagStringParams "" ["config-file"] "PATH" (flagHelpStr "path to config file") -- TODO: allow default on addFlagStringParam ? cmdlineConfig <- configParser suppressOutput <- addSimpleBoolFlag @@ -112,10 +125,17 @@ mainCmdParser helpDesc = do ["suppress-output"] (flagHelp $ parDoc "suppress the regular output, i.e. the transformed haskell source") _verbosity <- addSimpleCountFlag "v" ["verbose"] (flagHelp $ parDoc "[currently without effect; TODO]") - inplace <- addSimpleBoolFlag "" ["inplace"] (flagHelp $ parDoc "overwrite the input files") + writeMode <- addFlagReadParam + "" + ["write-mode"] + "" + Flag + { _flag_help = Just (PP.text "output mode: [display|inplace]") + , _flag_default = Just Display + } reorderStop - inputParam <- addStringParamOpt "PATH" (paramHelpStr "path to input haskell source file") - desc <- peekCmdDesc + inputParams <- addParamNoFlagStrings "PATH" (paramHelpStr "paths to input haskell source files") + desc <- peekCmdDesc addCmdImpl $ void $ do when printLicense $ do print licenseDoc @@ -130,21 +150,10 @@ mainCmdParser helpDesc = do liftIO $ print $ ppHelpShallow desc System.Exit.exitSuccess - let inputPaths' = case maybeToList inputParam ++ inputPaths of - [] -> [Nothing] - ps -> map Just ps - - outputPaths' <- case outputPaths of - [] | not inplace -> return [Nothing] - [] -> return inputPaths' - ps | not inplace -> return . map Just $ ps - _ -> do - putStrErrLn "cannot specify output files and inplace at the same time" - System.Exit.exitWith (System.Exit.ExitFailure 51) - - when (length inputPaths' /= length outputPaths') $ do - putStrErrLn "the number of inputs must match ther number of outputs" - System.Exit.exitWith (System.Exit.ExitFailure 52) + let inputPaths = if null inputParams then [Nothing] else map Just inputParams + let outputPaths = case writeMode of + Display -> repeat Nothing + Inplace -> inputPaths config <- runMaybeT (readConfigs cmdlineConfig configPaths) >>= \case Nothing -> System.Exit.exitWith (System.Exit.ExitFailure 53) @@ -152,9 +161,8 @@ mainCmdParser helpDesc = do when (confUnpack $ _dconf_dump_config $ _conf_debug $ config) $ do trace (showConfigYaml config) $ return () - let ios = zipWith (coreIO putStrErrLn config suppressOutput) inputPaths' outputPaths' - res <- fmap sequence_ $ sequence ios - case res of + results <- zipWithM (coreIO putStrErrLn config suppressOutput) inputPaths outputPaths + case sequence_ results of Left _ -> System.Exit.exitWith (System.Exit.ExitFailure 1) Right _ -> pure () diff --git a/stack.yaml b/stack.yaml index 4bbcc0c..539cd6d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,7 +3,7 @@ resolver: lts-9.0 extra-deps: - monad-memo-0.4.1 - czipwith-1.0.0.0 - - butcher-1.1.0.2 + - butcher-1.2.0.0 - data-tree-print-0.1.0.0 - deque-0.2 -- 2.30.2 From 752048882e6049f02413bbd2882ea2066e43a50b Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Wed, 4 Oct 2017 20:56:37 +0200 Subject: [PATCH 12/26] move inputParams into the reordered block --- src-brittany/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 4f6992e..76ed94e 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -133,8 +133,8 @@ mainCmdParser helpDesc = do { _flag_help = Just (PP.text "output mode: [display|inplace]") , _flag_default = Just Display } - reorderStop inputParams <- addParamNoFlagStrings "PATH" (paramHelpStr "paths to input haskell source files") + reorderStop desc <- peekCmdDesc addCmdImpl $ void $ do when printLicense $ do -- 2.30.2 From 7d7ec3e8b4793a163f38f257cbb9b87b7760928e Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Wed, 4 Oct 2017 23:43:30 +0200 Subject: [PATCH 13/26] Update commandline help output --- src-brittany/Main.hs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 76ed94e..4928acf 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -62,13 +62,24 @@ helpDoc :: PP.Doc helpDoc = PP.vcat $ List.intersperse (PP.text "") [ parDocW - [ "Transforms one haskell module by reformatting" - , "(parts of) the source code (while preserving the" - , "parts not transformed)." + [ "Reformats one or more haskell modules." + , "Currently affects only type signatures and function bindings;" + , "everything else is left unmodified." , "Based on ghc-exactprint, thus (theoretically) supporting all" , "that ghc does." - , "Currently, only type-signatures and function-bindings are transformed." ] + , parDoc $ "Example invocations:" + , PP.hang (PP.text "") 2 $ PP.vcat + [ PP.text "brittany" + , PP.hang (PP.text " ") 2 $ PP.text "read from stdin, output to stdout" + ] + , PP.hang (PP.text "") 2 $ PP.vcat + [ PP.text "brittany --indent=4 --write-mode=inplace *.hs" + , PP.nest 2 $ PP.vcat + [ PP.text "run on all modules in current directory (no backup!)" + , PP.text "4 spaces indentation" + ] + ] , parDocW [ "This program is written carefully and contains safeguards to ensure" , "the transformation does not change semantics (or the syntax tree at all)" @@ -128,9 +139,12 @@ mainCmdParser helpDesc = do writeMode <- addFlagReadParam "" ["write-mode"] - "" + "(display|inplace)" Flag - { _flag_help = Just (PP.text "output mode: [display|inplace]") + { _flag_help = Just $ PP.vcat + [ PP.text "display: output for any input(s) goes to stdout" + , PP.text "inplace: override respective input file (without backup!)" + ] , _flag_default = Just Display } inputParams <- addParamNoFlagStrings "PATH" (paramHelpStr "paths to input haskell source files") -- 2.30.2 From f86665a251463daa03b8f6f39118b11beaca5d54 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sat, 14 Oct 2017 23:21:13 +0200 Subject: [PATCH 14/26] Fix promoted HsTyVars on ghc-8.2.1 This fix does not work on ghc-8.0, because I do not understand the 8.0 API in this instance. Could be resolved by looking at annotations, but that really should not be necessary. --- src-literatetests/15-regressions.blt | 12 ++++++++++++ .../Brittany/Internal/LayouterBasics.hs | 4 ++++ .../Brittany/Internal/Layouters/Type.hs | 18 +++++++++++++----- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src-literatetests/15-regressions.blt b/src-literatetests/15-regressions.blt index 0a63b7a..2a7185b 100644 --- a/src-literatetests/15-regressions.blt +++ b/src-literatetests/15-regressions.blt @@ -465,3 +465,15 @@ v = A {..} where b = 2 {-# LANGUAGE RecordWildCards #-} v = A {a = 1, b = 2, c = 3} +#test issue 63 a +#pending fix does not work on 8.0.2 +test :: Proxy 'Int + +#test issue 63 b +#pending fix does not work on 8.0.2 +test :: Proxy '[ 'True] + +#test issue 63 c +#pending fix does not work on 8.0.2 +test :: Proxy '[Bool] + diff --git a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs index cffcad7..14a0510 100644 --- a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs +++ b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs @@ -42,6 +42,7 @@ module Language.Haskell.Brittany.Internal.LayouterBasics , appSep , docCommaSep , docParenLSep + , docTick , spacifyDocs , briDocMToPPM , allocateNode @@ -447,6 +448,9 @@ docCommaSep = appSep $ docLit $ Text.pack "," docParenLSep :: ToBriDocM BriDocNumbered docParenLSep = appSep $ docLit $ Text.pack "(" +docTick :: ToBriDocM BriDocNumbered +docTick = docLit $ Text.pack "'" + docNodeAnnKW :: Data.Data.Data ast => Located ast diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs index 36d1633..9fa7262 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs @@ -29,12 +29,20 @@ layoutType :: ToBriDoc HsType layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of -- _ | traceShow (ExactPrint.Types.mkAnnKey ltype) False -> error "impossible" #if MIN_VERSION_ghc(8,2,0) /* ghc-8.2 */ - HsTyVar _ name -> do + HsTyVar promoted name -> do + t <- lrdrNameToTextAnn name + case promoted of + Promoted -> docSeq + [ docSeparator + , docTick + , docWrapNode name $ docLit t + ] + NotPromoted -> docWrapNode name $ docLit t #else /* ghc-8.0 */ HsTyVar name -> do -#endif t <- lrdrNameToTextAnn name docWrapNode name $ docLit t +#endif HsForAllTy bndrs (L _ (HsQualTy (L _ cntxts@(_:_)) typ2)) -> do typeDoc <- docSharedWrapper layoutType typ2 tyVarDocs <- bndrs `forM` \case @@ -294,7 +302,7 @@ layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of docAlt [ docSeq [ docForceSingleline typeDoc1 - , docLit $ Text.pack " " + , docSeparator , docForceSingleline typeDoc2 ] , docPar @@ -324,7 +332,7 @@ layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of docAlt [ docSeq $ docForceSingleline docHead : (docRest >>= \d -> - [ docLit $ Text.pack " ", docForceSingleline d ]) + [ docSeparator, docForceSingleline d ]) , docPar docHead (docLines $ docEnsureIndent BrIndentRegular <$> docRest) ] HsAppsTy (typHead:typRest) -> do @@ -333,7 +341,7 @@ layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of docAlt [ docSeq $ docForceSingleline docHead : (docRest >>= \d -> - [ docLit $ Text.pack " ", docForceSingleline d ]) + [ docSeparator, docForceSingleline d ]) , docPar docHead (docLines $ docEnsureIndent BrIndentRegular <$> docRest) ] where -- 2.30.2 From b1c6be7acd3a65bd55667388288ea15c1ea31cba Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sun, 15 Oct 2017 00:23:14 +0200 Subject: [PATCH 15/26] Fix parentheses around kind signatures, fixes #64 --- src-literatetests/15-regressions.blt | 9 +++ .../Brittany/Internal/LayouterBasics.hs | 14 +++++ .../Brittany/Internal/Layouters/Type.hs | 56 +++++++++++++++---- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src-literatetests/15-regressions.blt b/src-literatetests/15-regressions.blt index 2a7185b..bea97cc 100644 --- a/src-literatetests/15-regressions.blt +++ b/src-literatetests/15-regressions.blt @@ -477,3 +477,12 @@ test :: Proxy '[ 'True] #pending fix does not work on 8.0.2 test :: Proxy '[Bool] +#test issue 64 +{-# LANGUAGE RankNTypes, KindSignatures #-} +func + :: forall m str + . (Str str, Monad m) + => Int + -> Proxy (str :: [*]) + -> m (Tagged str String) + diff --git a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs index 14a0510..a0a3c7b 100644 --- a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs +++ b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs @@ -48,6 +48,7 @@ module Language.Haskell.Brittany.Internal.LayouterBasics , allocateNode , docSharedWrapper , hasAnyCommentsBelow + , hasAnnKeyword ) where @@ -239,6 +240,19 @@ hasAnyCommentsBelow ast@(L l _) = do $ Map.elems $ anns +hasAnnKeyword + :: (Data a, MonadMultiReader (Map AnnKey Annotation) m) + => Located a + -> AnnKeywordId + -> m Bool +hasAnnKeyword ast annKeyword = do + anns <- mAsk + let hasK (ExactPrint.Types.G x, _) = x == annKeyword + hasK _ = False + pure $ case Map.lookup (ExactPrint.Types.mkAnnKey ast) anns of + Nothing -> False + Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> any hasK aks + -- new BriDoc stuff allocateNode diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs index 9fa7262..a5148f5 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Type.hs @@ -14,7 +14,11 @@ import Language.Haskell.Brittany.Internal.Types import Language.Haskell.Brittany.Internal.LayouterBasics import RdrName ( RdrName(..) ) -import GHC ( runGhc, GenLocated(L), moduleNameString ) +import GHC ( runGhc + , GenLocated(L) + , moduleNameString + , AnnKeywordId (..) + ) import Language.Haskell.GHC.ExactPrint.Types ( mkAnnKey ) import HsSyn import Name @@ -521,19 +525,47 @@ layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of HsKindSig typ1 kind1 -> do typeDoc1 <- docSharedWrapper layoutType typ1 kindDoc1 <- docSharedWrapper layoutType kind1 + hasParens <- hasAnnKeyword ltype AnnOpenP docAlt - [ docSeq - [ docForceSingleline typeDoc1 - , docLit $ Text.pack " :: " - , docForceSingleline kindDoc1 - ] - , docPar + [ if hasParens + then docSeq + [ docLit $ Text.pack "(" + , docForceSingleline typeDoc1 + , docSeparator + , docLit $ Text.pack "::" + , docSeparator + , docForceSingleline kindDoc1 + , docLit $ Text.pack ")" + ] + else docSeq + [ docForceSingleline typeDoc1 + , docSeparator + , docLit $ Text.pack "::" + , docSeparator + , docForceSingleline kindDoc1 + ] + , if hasParens + then docLines + [ docCols + ColTyOpPrefix + [ docWrapNodeRest ltype $ docParenLSep + , docAddBaseY (BrIndentSpecial 3) $ typeDoc1 + ] + , docCols + ColTyOpPrefix + [ docWrapNodeRest ltype $ docLit $ Text.pack ":: " + , docAddBaseY (BrIndentSpecial 3) kindDoc1 + ] + , (docLit $ Text.pack ")") + ] + else docPar typeDoc1 - ( docCols ColTyOpPrefix - [ docWrapNodeRest ltype - $ docLit $ Text.pack ":: " - , docAddBaseY (BrIndentSpecial 3) kindDoc1 - ]) + ( docCols + ColTyOpPrefix + [ docWrapNodeRest ltype $ docLit $ Text.pack ":: " + , docAddBaseY (BrIndentSpecial 3) kindDoc1 + ] + ) ] HsBangTy{} -> -- TODO briDocByExactInlineOnly "HsBangTy{}" ltype -- 2.30.2 From 585c345c356f8e8dd2db564503a8c1f8d8c31fbb Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sun, 15 Oct 2017 00:32:10 +0200 Subject: [PATCH 16/26] Fix silently broken travis setup (rahhh) --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8b62149..da510f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -180,7 +180,7 @@ before_install: - | function better_wait() { date - time "$*" & # send the long living command to background! + time "$@" & # send the long living command to background! set +x MINUTES=0 @@ -231,7 +231,7 @@ install: echo "cabal build-cache MISS"; rm -rf $HOME/.cabsnap; mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin; - cabal install -j$JOBS --only-dependencies --enable-tests --enable-benchmarks --ghc-options="-j1 +RTS -M500M"; + cabal install -j$JOBS --only-dependencies --enable-tests --enable-benchmarks --ghc-options="-j1 +RTS -M500M -RTS"; fi # snapshot package-db on cache miss @@ -259,12 +259,12 @@ script: set -ex case "$BUILD" in stack) - better_wait stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps --ghc-options="-j1 +RTS -M500M" + better_wait stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps --ghc-options="-j1 +RTS -M500M -RTS" ;; cabal) if [ -f configure.ac ]; then autoreconf -i; fi cabal configure --enable-tests --enable-benchmarks -v # -v2 provides useful information for debugging - better_wait cabal build -j$JOBS --ghc-options="-j1 +RTS -M500M" # this builds all libraries and executables (including tests/benchmarks) + better_wait cabal build -j$JOBS --ghc-options="-j1 +RTS -M500M -RTS" # this builds all libraries and executables (including tests/benchmarks) cabal test ;; cabaldist) @@ -275,12 +275,12 @@ script: # If there are no other `.tar.gz` files in `dist`, this can be even simpler: # `cabal install --force-reinstalls dist/*-*.tar.gz` SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz && - (cd dist && better_wait cabal install -j$JOBS --force-reinstalls "$SRC_TGZ" --ghc-options="-j1 +RTS -M500M") + (cd dist && better_wait cabal install -j$JOBS --force-reinstalls "$SRC_TGZ" --ghc-options="-j1 +RTS -M500M -RTS") ;; canew) better_wait cabal new-build -j$JOBS --disable-tests --disable-benchmarks better_wait cabal new-build -j$JOBS --enable-tests --enable-benchmarks - cabal new-test --ghc-options="-j1 +RTS -M500M" + cabal new-test --ghc-options="-j1 +RTS -M500M -RTS" ;; esac set +ex -- 2.30.2 From ddd7c6b439588547d488574b5c019df029dc9d77 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 24 Oct 2017 00:00:34 +0200 Subject: [PATCH 17/26] Fix some rare issue and add some comments (it is so rare i cannot reproduce anymore right now, because the code that caused it has changed since..) --- .../Brittany/Internal/Layouters/Expr.hs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs index 2808df2..90fd435 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs @@ -154,19 +154,32 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of expDoc1 <- docSharedWrapper layoutExpr exp1 expDoc2 <- docSharedWrapper layoutExpr exp2 docAlt - [ docSeq [appSep $ docForceSingleline expDoc1, docForceSingleline expDoc2] - , docSetParSpacing + [ -- func arg + docSeq [appSep $ docForceSingleline expDoc1, docForceSingleline expDoc2] + , -- func argline1 + -- arglines + -- e.g. + -- func if x + -- then 1 + -- else 2 + docSetParSpacing $ docAddBaseY BrIndentRegular $ docSeq [ appSep $ docForceSingleline expDoc1 , docForceParSpacing expDoc2 ] - , docSetParSpacing + , -- func + -- arg + docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar (docForceSingleline expDoc1) - expDoc2 - , docAddBaseY BrIndentRegular + (docNonBottomSpacing expDoc2) + , -- fu + -- nc + -- ar + -- gument + docAddBaseY BrIndentRegular $ docPar expDoc1 expDoc2 -- 2.30.2 From 338beb8eea96809c0030541f96ff8fd1c8c0f68c Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 24 Oct 2017 00:15:53 +0200 Subject: [PATCH 18/26] Move testcases for extensions in separate testfile --- src-literatetests/10-tests.blt | 44 ------------------------- src-literatetests/14-extensions.blt | 50 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 src-literatetests/14-extensions.blt diff --git a/src-literatetests/10-tests.blt b/src-literatetests/10-tests.blt index 696cbb6..03b1c6b 100644 --- a/src-literatetests/10-tests.blt +++ b/src-literatetests/10-tests.blt @@ -260,21 +260,6 @@ func -- b ) -- j -- k -############################################################################### - -#test ImplicitParams 1 -{-# LANGUAGE ImplicitParams #-} -func :: (?asd::Int) -> () - -#test ImplicitParams 2 -{-# LANGUAGE ImplicitParams #-} -func - :: ( ?asd - :: lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd - -> lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd - ) - -> () - ############################################################################### ############################################################################### @@ -454,12 +439,6 @@ func = 1.1e5 func = 'x' func = 981409823458910394810928414192837123987123987123 -#test lambdacase -{-# LANGUAGE LambdaCase #-} -func = \case - FooBar -> x - Baz -> y - #test lambda func = \x -> abc @@ -550,29 +529,6 @@ func = ] -############################################################################### -############################################################################### -############################################################################### -#group expression.multiwayif -############################################################################### -############################################################################### -############################################################################### - -#test simple -{-# LANGUAGE MultiWayIf #-} -func = if - | cond1 -> loooooooooooooooooooooooooooooong expr1 - | cond2 -> loooooooooooooooooooooooooooooong expr2 - -#test simplenested -{-# LANGUAGE MultiWayIf #-} -func = do - foo - bar $ if - | cond1 -> loooooooooooooooooooooooooooooong expr1 - | cond2 -> loooooooooooooooooooooooooooooong expr2 - - ############################################################################### ############################################################################### ############################################################################### diff --git a/src-literatetests/14-extensions.blt b/src-literatetests/14-extensions.blt new file mode 100644 index 0000000..d038b64 --- /dev/null +++ b/src-literatetests/14-extensions.blt @@ -0,0 +1,50 @@ +############################################################################### +############################################################################### +############################################################################### +#group extensions +############################################################################### +############################################################################### +############################################################################### + +############################################################################### +## MultiWayIf +#test multiwayif 1 +{-# LANGUAGE MultiWayIf #-} +func = if + | cond1 -> loooooooooooooooooooooooooooooong expr1 + | cond2 -> loooooooooooooooooooooooooooooong expr2 + +#test multiwayif 2 +{-# LANGUAGE MultiWayIf #-} +func = do + foo + bar $ if + | cond1 -> loooooooooooooooooooooooooooooong expr1 + | cond2 -> loooooooooooooooooooooooooooooong expr2 + + +############################################################################### +## LambdaCase +#test lambdacase 1 +{-# LANGUAGE LambdaCase #-} +func = \case + FooBar -> x + Baz -> y + + + +############################################################################### +## ImplicitParams +#test ImplicitParams 1 +{-# LANGUAGE ImplicitParams #-} +func :: (?asd::Int) -> () + +#test ImplicitParams 2 +{-# LANGUAGE ImplicitParams #-} +func + :: ( ?asd + :: lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd + -> lkasdlkjalsdjlakjsdlkjasldkjalskdjlkajsd + ) + -> () + -- 2.30.2 From 26f8cdfb659876f6233dac70dd76c64578c9048d Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 24 Oct 2017 00:16:49 +0200 Subject: [PATCH 19/26] Support RecursiveDo/`rec` keyword --- src-literatetests/14-extensions.blt | 18 ++++++++++++++++++ .../Brittany/Internal/Layouters/Stmt.hs | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/src-literatetests/14-extensions.blt b/src-literatetests/14-extensions.blt index d038b64..896d105 100644 --- a/src-literatetests/14-extensions.blt +++ b/src-literatetests/14-extensions.blt @@ -48,3 +48,21 @@ func ) -> () + +############################################################################### +## RecursiveDo +#test recursivedo 1 +{-# LANGUAGE RecursiveDo #-} +foo = do + rec a <- f b + b <- g a + return (a, b) + +#test recursivedo 2 +{-# LANGUAGE RecursiveDo #-} +foo = do + rec -- comment + a <- f b + b <- g a + return (a, b) + diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs index 692b467..a8d95aa 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs @@ -70,6 +70,12 @@ layoutStmt lstmt@(L _ stmt) = docWrapNode lstmt $ case stmt of (docLit $ Text.pack "let") (docSetBaseAndIndent $ docLines $ return <$> bindDocs) ] + RecStmt stmts _ _ _ _ _ _ _ _ _ -> do + docSeq + [ docLit (Text.pack "rec") + , docSeparator + , docSetBaseAndIndent $ docLines $ layoutStmt <$> stmts + ] BodyStmt expr _ _ _ -> do expDoc <- docSharedWrapper layoutExpr expr docAddBaseY BrIndentRegular $ expDoc -- 2.30.2 From f46fcc135d7985c8c95025e21f0aa5a1d09fe16f Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Wed, 8 Nov 2017 21:54:32 +0100 Subject: [PATCH 20/26] Update doc/HCAR entry --- doc/hcar/Brittany.tex | 55 ++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/doc/hcar/Brittany.tex b/doc/hcar/Brittany.tex index 5c6760d..f181b2f 100644 --- a/doc/hcar/Brittany.tex +++ b/doc/hcar/Brittany.tex @@ -1,6 +1,6 @@ -% Brittany-LE.tex -\begin{hcarentry}[new]{Brittany} -\report{Lennart Spitzner}%11/16 +% Brittany-LB.tex +\begin{hcarentry}[updated]{Brittany} +\report{Lennart Spitzner}%11/17 \status{work in progress} \makeheader @@ -11,44 +11,35 @@ haskell-src-exts such as hindent or haskell-formatter. The goals of the project are to: \begin{compactitem} -\item - support the full ghc-haskell syntax including syntactic extensions; -\item - retain newlines and comments unmodified (to the degree possible when code - around them gets reformatted); -\item - be clever about using horizontal space while not overflowing it if it cannot - be avoided; -\item - have linear complexity in the size of the input text / the number of +\item support the full ghc-haskell syntax including syntactic extensions; +\item retain newlines and comments unmodified (to the degree possible when + code around them gets reformatted); +\item be clever about using horizontal space while not overflowing it if it + cannot be avoided; +\item have linear complexity in the size of the input text / the number of syntactic nodes in the input. -\item - support horizontal alignments (e.g. different equations/pattern matches in - the some function's definition). +\item support horizontal alignments (e.g. different equations/pattern matches + in the some function's definition). \end{compactitem} -In contrast to other formatters brittany internally works in two steps: Firstly -transforming the syntax tree into a document tree representation, similar to -the document representation in general-purpose pretty-printers such as the -\emph{pretty} package, but much more specialized for the specific purpose of -handling a Haskell source code document. Secondly this document representation -is transformed into the output text document. This approach allows to handle -many different syntactic constructs in a uniform way, making it possible -to attain the above goals with a manageable amount of work. +In contrast to other formatters brittany internally works in two steps: +Firstly transforming the syntax tree into a document tree representation, +similar to the document representation in general-purpose pretty-printers such +as the \emph{pretty} package, but much more specialized for the specific +purpose of handling a Haskell source code document. Secondly this document +representation is transformed into the output text document. This approach +allows to handle many different syntactic constructs in a uniform way, making +it possible to attain the above goals with a manageable amount of work. Brittany is work in progress; currently only type signatures and function bindings are transformed, and not all syntactic constructs are supported. -Nonetheless Brittany is safe to try/use as there are checks in place to -ensure that the output is syntactically valid. +Nonetheless Brittany is safe to try/use as there are checks in place to ensure +that the output is syntactically valid. -Brittany requires ghc-8, and is not released on hackage yet; for a description -of how to build it see the repository README. +Brittany requires ghc-8.*, and is available on Hackage and on Stackage. \FurtherReading -{\small \begin{compactitem} - \item - \url{https://github.com/lspitzner/brittany} + \item \url{https://github.com/lspitzner/brittany} \end{compactitem} -} \end{hcarentry} -- 2.30.2 From 37436e675aff945d756d49cecbd3487f428da507 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sat, 25 Nov 2017 00:58:33 +0100 Subject: [PATCH 21/26] Update README.md: Mention stackage nightly, contribution, dev branch --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d32a189..5d232a2 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ require fixing: # Other usage notes - Supports GHC versions `8.0.*` and `8.2.*`. +- as of November'17, `brittany` is available on stackage nightly. - config (file) documentation is lacking. - some config values can not be configured via commandline yet. - uses/creates user config file in `~/.brittany/config.yaml`; @@ -109,9 +110,21 @@ require fixing: - -XBangPatterns ~~~~ -# Implementation/High-level Documentation +# Feature Requests, Contribution, Documentation -[See the documentation index](doc/implementation/index.md) +This currently is a one-person project in the sense that 90% of the code is +written by one person. And (unfortunately) it is not my job to keep improving +this project. Please forgive that as a consequence my time to invest on new +features is rather limited. + +Nonetheless I consider it "in active development" :) + +One way of speeding things up is to make your own contributions. There is +a good amount of high-level documentation at + +[the documentation index](doc/implementation/index.md) + +Note that most development happens on the `dev` branch of this repository! # License -- 2.30.2 From c7095132094d1adcc8622cd4ded738131432f9dc Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sat, 25 Nov 2017 19:23:56 +0100 Subject: [PATCH 22/26] Remove dependency on either package Following the deprecation and removal of the EitherT transformer --- brittany.cabal | 4 -- src-brittany/Main.hs | 8 ++-- src/Language/Haskell/Brittany/Internal.hs | 8 ++-- .../Brittany/Internal/ExactPrintUtils.hs | 37 +++++++++---------- srcinc/prelude.inc | 2 +- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/brittany.cabal b/brittany.cabal index ca639b8..bf2ba63 100644 --- a/brittany.cabal +++ b/brittany.cabal @@ -112,7 +112,6 @@ library { , unsafe >=0.0 && <0.1 , safe >=0.3.9 && <0.4 , deepseq >=1.4.2.0 && <1.5 - , either >=4.4.1.1 && <4.5 , semigroups >=0.18.2 && <0.19 , cmdargs >=0.10.14 && <0.11 , czipwith >=1.0.0.0 && <1.1 @@ -175,7 +174,6 @@ executable brittany , unsafe , safe , deepseq - , either , semigroups , cmdargs , czipwith @@ -252,7 +250,6 @@ test-suite unittests , unsafe , safe , deepseq - , either , semigroups , cmdargs , czipwith @@ -324,7 +321,6 @@ test-suite littests , unsafe , safe , deepseq - , either , semigroups , cmdargs , czipwith diff --git a/src-brittany/Main.hs b/src-brittany/Main.hs index 4928acf..046c830 100644 --- a/src-brittany/Main.hs +++ b/src-brittany/Main.hs @@ -193,8 +193,8 @@ coreIO -> Maybe FilePath.FilePath -- ^ input filepath; stdin if Nothing. -> Maybe FilePath.FilePath -- ^ output filepath; stdout if Nothing. -> IO (Either Int ()) -- ^ Either an errorNo, or success. -coreIO putErrorLnIO config suppressOutput inputPathM outputPathM = EitherT.runEitherT $ do - let putErrorLn = liftIO . putErrorLnIO :: String -> EitherT.EitherT e IO () +coreIO putErrorLnIO config suppressOutput inputPathM outputPathM = ExceptT.runExceptT $ do + let putErrorLn = liftIO . putErrorLnIO :: String -> ExceptT.ExceptT e IO () let ghcOptions = config & _conf_forward & _options_ghc & runIdentity -- there is a good of code duplication between the following code and the -- `pureModuleTransform` function. Unfortunately, there are also a good @@ -234,7 +234,7 @@ coreIO putErrorLnIO config suppressOutput inputPathM outputPathM = EitherT.runEi Left left -> do putErrorLn "parse error:" putErrorLn $ show left - EitherT.left 60 + ExceptT.throwE 60 Right (anns, parsedSource, hasCPP) -> do when (config & _conf_debug .> _dconf_dump_ast_full .> confUnpack) $ do let val = printTreeWithCustom 100 (customLayouterF anns) parsedSource @@ -300,7 +300,7 @@ coreIO putErrorLnIO config suppressOutput inputPathM outputPathM = EitherT.runEi Nothing -> liftIO $ TextL.IO.putStr $ outLText Just p -> liftIO $ TextL.IO.writeFile p $ outLText - when hasErrors $ EitherT.left 70 + when hasErrors $ ExceptT.throwE 70 where addTraceSep conf = if or diff --git a/src/Language/Haskell/Brittany/Internal.hs b/src/Language/Haskell/Brittany/Internal.hs index 4c4bbf0..64c139a 100644 --- a/src/Language/Haskell/Brittany/Internal.hs +++ b/src/Language/Haskell/Brittany/Internal.hs @@ -20,7 +20,7 @@ import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types import qualified Language.Haskell.GHC.ExactPrint.Parsers as ExactPrint.Parsers import Data.Data -import Control.Monad.Trans.Either +import Control.Monad.Trans.Except import Data.HList.HList import Data.CZipWith @@ -62,7 +62,7 @@ import qualified GHC.LanguageExtensions.Type as GHC -- Note that this function ignores/resets all config values regarding -- debugging, i.e. it will never use `trace`/write to stderr. parsePrintModule :: Config -> Text -> IO (Either [BrittanyError] Text) -parsePrintModule configRaw inputText = runEitherT $ do +parsePrintModule configRaw inputText = runExceptT $ do let config = configRaw { _conf_debug = _conf_debug staticDefaultConfig } let ghcOptions = config & _conf_forward & _options_ghc & runIdentity let config_pp = config & _conf_preprocessor @@ -87,7 +87,7 @@ parsePrintModule configRaw inputText = runEitherT $ do cppCheckFunc (hackTransform $ Text.unpack inputText) case parseResult of - Left err -> left $ [ErrorInput err] + Left err -> throwE $ [ErrorInput err] Right x -> pure $ x (errsWarns, outputTextL) <- do let omitCheck = @@ -117,7 +117,7 @@ parsePrintModule configRaw inputText = runEitherT $ do case config & _conf_errorHandling & _econf_Werror & confUnpack of False -> 0 < maximum (-1 : fmap customErrOrder errsWarns) True -> not $ null errsWarns - if hasErrors then left $ errsWarns else pure $ TextL.toStrict outputTextL + if hasErrors then throwE $ errsWarns else pure $ TextL.toStrict outputTextL diff --git a/src/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs b/src/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs index faa9526..74ed50d 100644 --- a/src/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs +++ b/src/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs @@ -58,26 +58,24 @@ parseModuleWithCpp -> (GHC.DynFlags -> IO (Either String a)) -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a)) parseModuleWithCpp cpp opts args fp dynCheck = - ExactPrint.ghcWrapper $ EitherT.runEitherT $ do + ExactPrint.ghcWrapper $ ExceptT.runExceptT $ do dflags0 <- lift $ GHC.getSessionDynFlags - (dflags1, leftover, warnings) <- lift $ GHC.parseDynamicFlagsCmdLine - dflags0 - (GHC.noLoc <$> args) + (dflags1, leftover, warnings) <- lift + $ GHC.parseDynamicFlagsCmdLine dflags0 (GHC.noLoc <$> args) void $ lift $ GHC.setSessionDynFlags dflags1 - dflags2 <- lift $ ExactPrint.initDynFlags fp + dflags2 <- lift $ ExactPrint.initDynFlags fp when (not $ null leftover) - $ EitherT.left + $ ExceptT.throwE $ "when parsing ghc flags: leftover flags: " ++ show (leftover <&> \(L _ s) -> s) when (not $ null warnings) - $ EitherT.left + $ ExceptT.throwE $ "when parsing ghc flags: encountered warnings: " ++ show (warnings <&> \(L _ s) -> s) - x <- EitherT.EitherT $ liftIO $ dynCheck dflags2 + x <- ExceptT.ExceptT $ liftIO $ dynCheck dflags2 res <- lift $ ExactPrint.parseModuleApiAnnsWithCppInternal cpp dflags2 fp - EitherT.hoistEither - $ either (\(span, err) -> Left $ show span ++ ": " ++ err) - (\(a, m) -> Right (a, m, x)) + either (\(span, err) -> ExceptT.throwE $ show span ++ ": " ++ err) + (\(a, m) -> pure (a, m, x)) $ ExactPrint.postParseTransform res opts parseModuleFromString @@ -87,22 +85,21 @@ parseModuleFromString -> String -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a)) parseModuleFromString args fp dynCheck str = - ExactPrint.ghcWrapper $ EitherT.runEitherT $ do + ExactPrint.ghcWrapper $ ExceptT.runExceptT $ do dflags0 <- lift $ ExactPrint.initDynFlagsPure fp str - (dflags1, leftover, warnings) <- - lift $ GHC.parseDynamicFlagsCmdLine dflags0 (GHC.noLoc <$> args) + (dflags1, leftover, warnings) <- lift + $ GHC.parseDynamicFlagsCmdLine dflags0 (GHC.noLoc <$> args) when (not $ null leftover) - $ EitherT.left + $ ExceptT.throwE $ "when parsing ghc flags: leftover flags: " ++ show (leftover <&> \(L _ s) -> s) when (not $ null warnings) - $ EitherT.left + $ ExceptT.throwE $ "when parsing ghc flags: encountered warnings: " ++ show (warnings <&> \(L _ s) -> s) - x <- EitherT.EitherT $ liftIO $ dynCheck dflags1 - EitherT.hoistEither - $ either (\(span, err) -> Left $ show span ++ ": " ++ err) - (\(a, m) -> Right (a, m, x)) + x <- ExceptT.ExceptT $ liftIO $ dynCheck dflags1 + either (\(span, err) -> ExceptT.throwE $ show span ++ ": " ++ err) + (\(a, m) -> pure (a, m, x)) $ ExactPrint.parseWith dflags1 fp GHC.parseModule str ----------- diff --git a/srcinc/prelude.inc b/srcinc/prelude.inc index 805b941..81ca53a 100644 --- a/srcinc/prelude.inc +++ b/srcinc/prelude.inc @@ -136,7 +136,7 @@ import qualified Data.Text.Lazy.IO as TextL.IO import qualified Control.Monad.Trans.State as State import qualified Control.Monad.Trans.State.Lazy as StateL import qualified Control.Monad.Trans.State.Strict as StateS -import qualified Control.Monad.Trans.Either as EitherT +import qualified Control.Monad.Trans.Except as ExceptT import qualified Data.Strict.Maybe as Strict -- 2.30.2 From fdd2f5f6dc2ac4e5e307e50f16d373db0d5210b5 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Sun, 26 Nov 2017 21:28:06 +0100 Subject: [PATCH 23/26] Try fix shitty travis CI script again --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index da510f3..5922f50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -196,6 +196,7 @@ before_install: sleep 60 done + wait $! set -x } -- 2.30.2 From 6a97379b330078463cfd89353cf76787ce66a678 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 28 Nov 2017 17:56:28 +0100 Subject: [PATCH 24/26] Add whitespace around operator in section, Fixes #67 --- src-literatetests/10-tests.blt | 9 ++++----- src-literatetests/15-regressions.blt | 12 ++++++++---- .../Haskell/Brittany/Internal/Layouters/Expr.hs | 4 ++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src-literatetests/10-tests.blt b/src-literatetests/10-tests.blt index 03b1c6b..e04887c 100644 --- a/src-literatetests/10-tests.blt +++ b/src-literatetests/10-tests.blt @@ -461,17 +461,16 @@ func = lakjsdlajsdljasdlkjasldjasldjasldjalsdjlaskjd lakjsdlajsdljas ### #test left -func = (1+) +func = (1 +) #test right -func = (+1) +func = (+ 1) #test left inf -## TODO: this could be improved.. -func = (1`abc`) +func = (1 `abc`) #test right inf -func = (`abc`1) +func = (`abc` 1) ### #group tuples diff --git a/src-literatetests/15-regressions.blt b/src-literatetests/15-regressions.blt index bea97cc..319713b 100644 --- a/src-literatetests/15-regressions.blt +++ b/src-literatetests/15-regressions.blt @@ -324,17 +324,17 @@ func = fooooooooooooooooooooooooooooooooo + foooooooooooooooooooooooooooooooo parserPrim = [ r | r <- - [ SGPPrimFloat $ bool id (0-) minus $ readGnok "parserPrim" - (d1 ++ d2 ++ d3 ++ d4) + [ SGPPrimFloat $ bool id (0 -) minus $ readGnok "parserPrim" + (d1 ++ d2 ++ d3 ++ d4) | d2 <- string "." , d3 <- many1 (oneOf "0123456789") , _ <- string "f" ] - <|> [ SGPPrimFloat $ bool id (0-) minus $ fromIntegral + <|> [ SGPPrimFloat $ bool id (0 -) minus $ fromIntegral (readGnok "parserPrim" d1 :: Integer) | _ <- string "f" ] - <|> [ SGPPrimInt $ bool id (0-) minus $ fromIntegral + <|> [ SGPPrimInt $ bool id (0 -) minus $ fromIntegral (readGnok "parserPrim" d1 :: Integer) | _ <- string "i" ] @@ -486,3 +486,7 @@ func -> Proxy (str :: [*]) -> m (Tagged str String) +#test issue 67 +fmapuv :: U.Unbox a => (a -> b) -> U.Vector a -> V.Vector b +fmapuv f xs = G.generate (G.length xs) (f . (xs G.!)) + diff --git a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs index 90fd435..0e36a21 100644 --- a/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs +++ b/src/Language/Haskell/Brittany/Internal/Layouters/Expr.hs @@ -321,11 +321,11 @@ layoutExpr lexpr@(L _ expr) = docWrapNode lexpr $ case expr of SectionL left op -> do -- TODO: add to testsuite leftDoc <- docSharedWrapper layoutExpr left opDoc <- docSharedWrapper layoutExpr op - docSeq [leftDoc, opDoc] + docSeq [leftDoc, docSeparator, opDoc] SectionR op right -> do -- TODO: add to testsuite opDoc <- docSharedWrapper layoutExpr op rightDoc <- docSharedWrapper layoutExpr right - docSeq [opDoc, rightDoc] + docSeq [opDoc, docSeparator, rightDoc] ExplicitTuple args boxity | Just argExprs <- args `forM` (\case (L _ (Present e)) -> Just e; _ -> Nothing) -> do argDocs <- docSharedWrapper layoutExpr `mapM` argExprs -- 2.30.2 From 8a401d291efb44550e4b0a641aa311dab04d3891 Mon Sep 17 00:00:00 2001 From: Lennart Spitzner <hexagoxel@hexagoxel.de> Date: Tue, 28 Nov 2017 18:23:05 +0100 Subject: [PATCH 25/26] Workaround for #68: trim exactprinted text for unknown nodes --- src-literatetests/15-regressions.blt | 3 +++ .../Brittany/Internal/LayouterBasics.hs | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src-literatetests/15-regressions.blt b/src-literatetests/15-regressions.blt index 319713b..0876dc3 100644 --- a/src-literatetests/15-regressions.blt +++ b/src-literatetests/15-regressions.blt @@ -490,3 +490,6 @@ func fmapuv :: U.Unbox a => (a -> b) -> U.Vector a -> V.Vector b fmapuv f xs = G.generate (G.length xs) (f . (xs G.!)) + +#test parallellistcomp-workaround +cs0 = 0 : [ c / Interval n | c <- cs | n <- [1..] ] diff --git a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs index a0a3c7b..52c9e08 100644 --- a/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs +++ b/src/Language/Haskell/Brittany/Internal/LayouterBasics.hs @@ -82,6 +82,8 @@ import ApiAnnotation ( AnnKeywordId(..) ) import Data.Data import Data.Generics.Schemes +import qualified Data.Char as Char + import DataTreePrint import Data.HList.HList @@ -154,20 +156,21 @@ briDocByExactInlineOnly infoStr ast = do let exactPrinted = Text.pack $ ExactPrint.exactPrint ast anns fallbackMode <- mAsk <&> _conf_errorHandling .> _econf_ExactPrintFallback .> confUnpack - let exactPrintNode = allocateNode $ BDFExternal + let exactPrintNode t = allocateNode $ BDFExternal (ExactPrint.Types.mkAnnKey ast) (foldedAnnKeys ast) False - exactPrinted - let - errorAction = do - mTell $ [ErrorUnknownNode infoStr ast] - docLit $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}" + t + let errorAction = do + mTell $ [ErrorUnknownNode infoStr ast] + docLit + $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}" case (fallbackMode, Text.lines exactPrinted) of (ExactPrintFallbackModeNever, _ ) -> errorAction - (_ , [_]) -> exactPrintNode - (ExactPrintFallbackModeRisky, _ ) -> exactPrintNode - _ -> errorAction + (_ , [t]) -> exactPrintNode + (Text.dropWhile Char.isSpace . Text.dropWhileEnd Char.isSpace $ t) + (ExactPrintFallbackModeRisky, _) -> exactPrintNode exactPrinted + _ -> errorAction rdrNameToText :: RdrName -> Text -- rdrNameToText = Text.pack . show . flip runSDoc unsafeGlobalDynFlags . ppr -- 2.30.2 From 910937985a4237a170d4c704c22f6ba222130c9e Mon Sep 17 00:00:00 2001 From: Evan Rutledge Borden <eborden@frontrowed.com> Date: Tue, 28 Nov 2017 18:46:57 -0500 Subject: [PATCH 26/26] Add failing test for template haskell splices For some reason `brittany` is failing to print exact for top level splices. This may be an issue in `brittany` or `ghc-exact-print`, I'm not sure. I've added failing tests to highlight this issue. This bug causes `brittany` to produce syntactically invalid Haskell. ``` 1) template haskell top level splice expected: Right {-# LANGUAGE TemplateHaskell #-} deriveFromJSON (unPrefix "assignPost") ''AssignmentPost but got: Right {-# LANGUAGE TemplateHaskell #-} deriveFromJSON (unPrefix "assignPost") '' ``` --- src-literatetests/tests.blt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src-literatetests/tests.blt b/src-literatetests/tests.blt index e54841b..b87a418 100644 --- a/src-literatetests/tests.blt +++ b/src-literatetests/tests.blt @@ -590,6 +590,23 @@ func = ++ [ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc] +############################################################################### +############################################################################### +############################################################################### +#group template haskell +############################################################################### +############################################################################### +############################################################################### + +#test top level splice +{-# LANGUAGE TemplateHaskell #-} +deriveFromJSON (unPrefix "assignPost") ''AssignmentPost + +#test top level splice wrapped +{-# LANGUAGE TemplateHaskell #-} +$(deriveFromJSON (unPrefix "assignPost") ''AssignmentPost) + + ############################################################################### ############################################################################### ############################################################################### @@ -1057,7 +1074,7 @@ foo = ## from the input; i cannot really express this yet with the current ## test-suite. ## #test ayaz -## +## ## myManageHook = ## composeOne [isFullscreen -?> doFullFloat, isDialog -?> doFloat, transience] ## <+> composeAll -- 2.30.2