brittany 0.14.0.0 → 0.14.0.1
raw patch · 68 files changed
+11193/−11184 lines, 68 files
Files
- README.md +3/−3
- brittany.cabal +31/−31
- source/executable/Main.hs +1/−1
- source/library/Brittany.hs +24/−0
- source/library/Brittany/Internal.hs +684/−0
- source/library/Brittany/Internal/Backend.hs +732/−0
- source/library/Brittany/Internal/BackendUtils.hs +487/−0
- source/library/Brittany/Internal/Config.hs +288/−0
- source/library/Brittany/Internal/Config/Types.hs +338/−0
- source/library/Brittany/Internal/Config/Types/Instances.hs +131/−0
- source/library/Brittany/Internal/ExactPrintUtils.hs +254/−0
- source/library/Brittany/Internal/LayouterBasics.hs +781/−0
- source/library/Brittany/Internal/Layouters/DataDecl.hs +434/−0
- source/library/Brittany/Internal/Layouters/Decl.hs +998/−0
- source/library/Brittany/Internal/Layouters/Expr.hs +1086/−0
- source/library/Brittany/Internal/Layouters/Expr.hs-boot +16/−0
- source/library/Brittany/Internal/Layouters/IE.hs +224/−0
- source/library/Brittany/Internal/Layouters/Import.hs +159/−0
- source/library/Brittany/Internal/Layouters/Module.hs +197/−0
- source/library/Brittany/Internal/Layouters/Pattern.hs +209/−0
- source/library/Brittany/Internal/Layouters/Stmt.hs +116/−0
- source/library/Brittany/Internal/Layouters/Stmt.hs-boot +10/−0
- source/library/Brittany/Internal/Layouters/Type.hs +635/−0
- source/library/Brittany/Internal/Obfuscation.hs +102/−0
- source/library/Brittany/Internal/ParseModule.hs +316/−0
- source/library/Brittany/Internal/Prelude.hs +194/−0
- source/library/Brittany/Internal/PreludeUtils.hs +66/−0
- source/library/Brittany/Internal/Transformations/Alt.hs +961/−0
- source/library/Brittany/Internal/Transformations/Columns.hs +162/−0
- source/library/Brittany/Internal/Transformations/Floating.hs +213/−0
- source/library/Brittany/Internal/Transformations/Indent.hs +54/−0
- source/library/Brittany/Internal/Transformations/Par.hs +48/−0
- source/library/Brittany/Internal/Types.hs +463/−0
- source/library/Brittany/Internal/Utils.hs +275/−0
- source/library/Brittany/Main.hs +500/−0
- source/library/Language/Haskell/Brittany.hs +0/−24
- source/library/Language/Haskell/Brittany/Internal.hs +0/−660
- source/library/Language/Haskell/Brittany/Internal/Backend.hs +0/−707
- source/library/Language/Haskell/Brittany/Internal/BackendUtils.hs +0/−522
- source/library/Language/Haskell/Brittany/Internal/Config.hs +0/−286
- source/library/Language/Haskell/Brittany/Internal/Config/Types.hs +0/−339
- source/library/Language/Haskell/Brittany/Internal/Config/Types/Instances.hs +0/−121
- source/library/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs +0/−247
- source/library/Language/Haskell/Brittany/Internal/LayouterBasics.hs +0/−781
- source/library/Language/Haskell/Brittany/Internal/Layouters/DataDecl.hs +0/−437
- source/library/Language/Haskell/Brittany/Internal/Layouters/Decl.hs +0/−975
- source/library/Language/Haskell/Brittany/Internal/Layouters/Expr.hs +0/−1078
- source/library/Language/Haskell/Brittany/Internal/Layouters/Expr.hs-boot +0/−16
- source/library/Language/Haskell/Brittany/Internal/Layouters/IE.hs +0/−214
- source/library/Language/Haskell/Brittany/Internal/Layouters/Import.hs +0/−142
- source/library/Language/Haskell/Brittany/Internal/Layouters/Module.hs +0/−204
- source/library/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs +0/−220
- source/library/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs +0/−113
- source/library/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs-boot +0/−10
- source/library/Language/Haskell/Brittany/Internal/Layouters/Type.hs +0/−643
- source/library/Language/Haskell/Brittany/Internal/Obfuscation.hs +0/−101
- source/library/Language/Haskell/Brittany/Internal/ParseModule.hs +0/−500
- source/library/Language/Haskell/Brittany/Internal/Prelude.hs +0/−194
- source/library/Language/Haskell/Brittany/Internal/PreludeUtils.hs +0/−66
- source/library/Language/Haskell/Brittany/Internal/Transformations/Alt.hs +0/−907
- source/library/Language/Haskell/Brittany/Internal/Transformations/Columns.hs +0/−133
- source/library/Language/Haskell/Brittany/Internal/Transformations/Floating.hs +0/−195
- source/library/Language/Haskell/Brittany/Internal/Transformations/Indent.hs +0/−52
- source/library/Language/Haskell/Brittany/Internal/Transformations/Par.hs +0/−45
- source/library/Language/Haskell/Brittany/Internal/Types.hs +0/−463
- source/library/Language/Haskell/Brittany/Internal/Utils.hs +0/−270
- source/library/Language/Haskell/Brittany/Main.hs +0/−483
- source/test-suite/Main.hs +1/−1
README.md view
@@ -61,7 +61,7 @@ - via `stack` ~~~~.sh- stack install brittany # --resolver lts-10.0+ stack install brittany # --resolver lts-16.31 ~~~~ If you use an lts that includes brittany this should just work; otherwise@@ -104,8 +104,8 @@ #### VSCode [This extension](https://marketplace.visualstudio.com/items?itemName=MaxGabriel.brittany) connects commandline `brittany` to VSCode formatting API. Thanks to @MaxGabriel.-#### Via HIE- [haskell-ide-engine](https://github.com/haskell/haskell-ide-engine)+#### Via HLS+ [haskell-language-server](https://github.com/haskell/haskell-language-server) includes a `brittany` plugin that directly uses the brittany library. Relevant for any editors that properly support the language-server-protocol. #### Neovim / Vim 8
brittany.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: brittany-version: 0.14.0.0+version: 0.14.0.1 synopsis: Haskell source code formatter description: See <https://github.com/lspitzner/brittany/blob/master/README.md the README>.@@ -101,36 +101,36 @@ autogen-modules: Paths_brittany hs-source-dirs: source/library exposed-modules:- Language.Haskell.Brittany- Language.Haskell.Brittany.Internal- Language.Haskell.Brittany.Internal.Backend- Language.Haskell.Brittany.Internal.BackendUtils- Language.Haskell.Brittany.Internal.Config- Language.Haskell.Brittany.Internal.Config.Types- Language.Haskell.Brittany.Internal.Config.Types.Instances- Language.Haskell.Brittany.Internal.ExactPrintUtils- Language.Haskell.Brittany.Internal.LayouterBasics- Language.Haskell.Brittany.Internal.Layouters.DataDecl- Language.Haskell.Brittany.Internal.Layouters.Decl- Language.Haskell.Brittany.Internal.Layouters.Expr- Language.Haskell.Brittany.Internal.Layouters.IE- Language.Haskell.Brittany.Internal.Layouters.Import- Language.Haskell.Brittany.Internal.Layouters.Module- Language.Haskell.Brittany.Internal.Layouters.Pattern- Language.Haskell.Brittany.Internal.Layouters.Stmt- Language.Haskell.Brittany.Internal.Layouters.Type- Language.Haskell.Brittany.Internal.Obfuscation- Language.Haskell.Brittany.Internal.ParseModule- Language.Haskell.Brittany.Internal.Prelude- Language.Haskell.Brittany.Internal.PreludeUtils- Language.Haskell.Brittany.Internal.Transformations.Alt- Language.Haskell.Brittany.Internal.Transformations.Columns- Language.Haskell.Brittany.Internal.Transformations.Floating- Language.Haskell.Brittany.Internal.Transformations.Indent- Language.Haskell.Brittany.Internal.Transformations.Par- Language.Haskell.Brittany.Internal.Types- Language.Haskell.Brittany.Internal.Utils- Language.Haskell.Brittany.Main+ Brittany+ Brittany.Internal+ Brittany.Internal.Backend+ Brittany.Internal.BackendUtils+ Brittany.Internal.Config+ Brittany.Internal.Config.Types+ Brittany.Internal.Config.Types.Instances+ Brittany.Internal.ExactPrintUtils+ Brittany.Internal.LayouterBasics+ Brittany.Internal.Layouters.DataDecl+ Brittany.Internal.Layouters.Decl+ Brittany.Internal.Layouters.Expr+ Brittany.Internal.Layouters.IE+ Brittany.Internal.Layouters.Import+ Brittany.Internal.Layouters.Module+ Brittany.Internal.Layouters.Pattern+ Brittany.Internal.Layouters.Stmt+ Brittany.Internal.Layouters.Type+ Brittany.Internal.Obfuscation+ Brittany.Internal.ParseModule+ Brittany.Internal.Prelude+ Brittany.Internal.PreludeUtils+ Brittany.Internal.Transformations.Alt+ Brittany.Internal.Transformations.Columns+ Brittany.Internal.Transformations.Floating+ Brittany.Internal.Transformations.Indent+ Brittany.Internal.Transformations.Par+ Brittany.Internal.Types+ Brittany.Internal.Utils+ Brittany.Main Paths_brittany executable brittany
source/executable/Main.hs view
@@ -1,4 +1,4 @@-import qualified Language.Haskell.Brittany.Main as BrittanyMain+import qualified Brittany.Main as BrittanyMain main :: IO () main = BrittanyMain.main
+ source/library/Brittany.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany+ ( parsePrintModule+ , staticDefaultConfig+ , forwardOptionsSyntaxExtsEnabled+ , userConfigPath+ , findLocalConfigPath+ , readConfigs+ , readConfigsWithUserConfig+ , Config+ , CConfig(..)+ , CDebugConfig(..)+ , CLayoutConfig(..)+ , CErrorHandlingConfig(..)+ , CForwardOptions(..)+ , CPreProcessorConfig(..)+ , BrittanyError(..)+ ) where++import Brittany.Internal+import Brittany.Internal.Config+import Brittany.Internal.Config.Types+import Brittany.Internal.Types
+ source/library/Brittany/Internal.hs view
@@ -0,0 +1,684 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal+ ( parsePrintModule+ , parsePrintModuleTests+ , pPrintModule+ , pPrintModuleAndCheck+ -- re-export from utils:+ , parseModule+ , parseModuleFromString+ , extractCommentConfigs+ , getTopLevelDeclNameMap+ ) where++import Control.Monad.Trans.Except+import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS+import qualified Data.ByteString.Char8+import Data.CZipWith+import Data.Char (isSpace)+import Data.HList.HList+import qualified Data.Map as Map+import qualified Data.Maybe+import qualified Data.Semigroup as Semigroup+import qualified Data.Sequence as Seq+import qualified Data.Text as Text+import qualified Data.Text.Lazy as TextL+import qualified Data.Text.Lazy.Builder as Text.Builder+import qualified Data.Yaml+import qualified GHC hiding (parseModule)+import GHC (GenLocated(L))+import qualified GHC.Driver.Session as GHC+import GHC.Hs+import qualified GHC.LanguageExtensions.Type as GHC+import qualified GHC.OldList as List+import GHC.Parser.Annotation (AnnKeywordId(..))+import GHC.Types.SrcLoc (SrcSpan)+import Brittany.Internal.Backend+import Brittany.Internal.BackendUtils+import Brittany.Internal.Config+import Brittany.Internal.Config.Types+import Brittany.Internal.ExactPrintUtils+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.Decl+import Brittany.Internal.Layouters.Module+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Transformations.Alt+import Brittany.Internal.Transformations.Columns+import Brittany.Internal.Transformations.Floating+import Brittany.Internal.Transformations.Indent+import Brittany.Internal.Transformations.Par+import Brittany.Internal.Types+import Brittany.Internal.Utils+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint+import qualified UI.Butcher.Monadic as Butcher++++data InlineConfigTarget+ = InlineConfigTargetModule+ | InlineConfigTargetNextDecl -- really only next in module+ | InlineConfigTargetNextBinding -- by name+ | InlineConfigTargetBinding String++extractCommentConfigs+ :: ExactPrint.Anns+ -> TopLevelDeclNameMap+ -> Either (String, String) (CConfig Maybe, PerItemConfig)+extractCommentConfigs anns (TopLevelDeclNameMap declNameMap) = do+ let+ commentLiness =+ [ ( k+ , [ x+ | (ExactPrint.Comment x _ _, _) <-+ (ExactPrint.annPriorComments ann+ ++ ExactPrint.annFollowingComments ann+ )+ ]+ ++ [ x+ | (ExactPrint.AnnComment (ExactPrint.Comment x _ _), _) <-+ ExactPrint.annsDP ann+ ]+ )+ | (k, ann) <- Map.toList anns+ ]+ let+ configLiness = commentLiness <&> second+ (Data.Maybe.mapMaybe $ \line -> do+ l1 <-+ List.stripPrefix "-- BRITTANY" line+ <|> List.stripPrefix "--BRITTANY" line+ <|> List.stripPrefix "-- brittany" line+ <|> List.stripPrefix "--brittany" line+ <|> (List.stripPrefix "{- BRITTANY" line >>= stripSuffix "-}")+ let l2 = dropWhile isSpace l1+ guard+ (("@" `isPrefixOf` l2)+ || ("-disable" `isPrefixOf` l2)+ || ("-next" `isPrefixOf` l2)+ || ("{" `isPrefixOf` l2)+ || ("--" `isPrefixOf` l2)+ )+ pure l2+ )+ let+ configParser = Butcher.addAlternatives+ [ ( "commandline-config"+ , \s -> "-" `isPrefixOf` dropWhile (== ' ') s+ , cmdlineConfigParser+ )+ , ( "yaml-config-document"+ , \s -> "{" `isPrefixOf` dropWhile (== ' ') s+ , Butcher.addCmdPart (Butcher.varPartDesc "yaml-config-document")+ $ fmap (\lconf -> (mempty { _conf_layout = lconf }, ""))+ . either (const Nothing) Just+ . Data.Yaml.decodeEither'+ . Data.ByteString.Char8.pack+ -- TODO: use some proper utf8 encoder instead?+ )+ ]+ parser = do -- we will (mis?)use butcher here to parse the inline config+ -- line.+ let+ nextDecl = do+ conf <- configParser+ Butcher.addCmdImpl (InlineConfigTargetNextDecl, conf)+ Butcher.addCmd "-next-declaration" nextDecl+ Butcher.addCmd "-Next-Declaration" nextDecl+ Butcher.addCmd "-NEXT-DECLARATION" nextDecl+ let+ nextBinding = do+ conf <- configParser+ Butcher.addCmdImpl (InlineConfigTargetNextBinding, conf)+ Butcher.addCmd "-next-binding" nextBinding+ Butcher.addCmd "-Next-Binding" nextBinding+ Butcher.addCmd "-NEXT-BINDING" nextBinding+ let+ disableNextBinding = do+ Butcher.addCmdImpl+ ( InlineConfigTargetNextBinding+ , mempty { _conf_roundtrip_exactprint_only = pure $ pure True }+ )+ Butcher.addCmd "-disable-next-binding" disableNextBinding+ Butcher.addCmd "-Disable-Next-Binding" disableNextBinding+ Butcher.addCmd "-DISABLE-NEXT-BINDING" disableNextBinding+ let+ disableNextDecl = do+ Butcher.addCmdImpl+ ( InlineConfigTargetNextDecl+ , mempty { _conf_roundtrip_exactprint_only = pure $ pure True }+ )+ Butcher.addCmd "-disable-next-declaration" disableNextDecl+ Butcher.addCmd "-Disable-Next-Declaration" disableNextDecl+ Butcher.addCmd "-DISABLE-NEXT-DECLARATION" disableNextDecl+ let+ disableFormatting = do+ Butcher.addCmdImpl+ ( InlineConfigTargetModule+ , mempty { _conf_disable_formatting = pure $ pure True }+ )+ Butcher.addCmd "-disable" disableFormatting+ Butcher.addCmd "@" $ do+ -- Butcher.addCmd "module" $ do+ -- conf <- configParser+ -- Butcher.addCmdImpl (InlineConfigTargetModule, conf)+ Butcher.addNullCmd $ do+ bindingName <- Butcher.addParamString "BINDING" mempty+ conf <- configParser+ Butcher.addCmdImpl (InlineConfigTargetBinding bindingName, conf)+ conf <- configParser+ Butcher.addCmdImpl (InlineConfigTargetModule, conf)+ lineConfigss <- configLiness `forM` \(k, ss) -> do+ r <- ss `forM` \s -> case Butcher.runCmdParserSimple s parser of+ Left err -> Left $ (err, s)+ Right c -> Right $ c+ pure (k, r)++ let+ perModule = foldl'+ (<>)+ mempty+ [ conf+ | (_, lineConfigs) <- lineConfigss+ , (InlineConfigTargetModule, conf) <- lineConfigs+ ]+ let+ perBinding = Map.fromListWith+ (<>)+ [ (n, conf)+ | (k, lineConfigs) <- lineConfigss+ , (target, conf) <- lineConfigs+ , n <- case target of+ InlineConfigTargetBinding s -> [s]+ InlineConfigTargetNextBinding | Just name <- Map.lookup k declNameMap ->+ [name]+ _ -> []+ ]+ let+ perKey = Map.fromListWith+ (<>)+ [ (k, conf)+ | (k, lineConfigs) <- lineConfigss+ , (target, conf) <- lineConfigs+ , case target of+ InlineConfigTargetNextDecl -> True+ InlineConfigTargetNextBinding | Nothing <- Map.lookup k declNameMap ->+ True+ _ -> False+ ]++ pure+ $ ( perModule+ , PerItemConfig { _icd_perBinding = perBinding, _icd_perKey = perKey }+ )+++getTopLevelDeclNameMap :: GHC.ParsedSource -> TopLevelDeclNameMap+getTopLevelDeclNameMap (L _ (HsModule _ _name _exports _ decls _ _)) =+ TopLevelDeclNameMap $ Map.fromList+ [ (ExactPrint.mkAnnKey decl, name)+ | decl <- decls+ , (name : _) <- [getDeclBindingNames decl]+ ]+++-- | Exposes the transformation in an pseudo-pure fashion. The signature+-- contains `IO` due to the GHC API not exposing a pure parsing function, but+-- there should be no observable effects.+--+-- Note that this function ignores/resets all config values regarding+-- debugging, i.e. it will never use `trace`/write to stderr.+--+-- Note that the ghc parsing function used internally currently is wrapped in+-- `mask_`, so cannot be killed easily. If you don't control the input, you+-- may wish to put some proper upper bound on the input's size as a timeout+-- won't do.+parsePrintModule :: Config -> Text -> IO (Either [BrittanyError] Text)+parsePrintModule configWithDebugs inputText = runExceptT $ do+ let+ config = configWithDebugs { _conf_debug = _conf_debug staticDefaultConfig }+ let ghcOptions = config & _conf_forward & _options_ghc & runIdentity+ let config_pp = config & _conf_preprocessor+ let cppMode = config_pp & _ppconf_CPPMode & confUnpack+ let hackAroundIncludes = config_pp & _ppconf_hackAroundIncludes & confUnpack+ (anns, parsedSource, hasCPP) <- do+ let+ hackF s =+ if "#include" `isPrefixOf` s then "-- BRITANY_INCLUDE_HACK " ++ s else s+ let+ hackTransform = if hackAroundIncludes+ then List.intercalate "\n" . fmap hackF . lines'+ else id+ let+ cppCheckFunc dynFlags = if GHC.xopt GHC.Cpp dynFlags+ then case cppMode of+ CPPModeAbort -> return $ Left "Encountered -XCPP. Aborting."+ CPPModeWarn -> return $ Right True+ CPPModeNowarn -> return $ Right True+ else return $ Right False+ parseResult <- lift $ parseModuleFromString+ ghcOptions+ "stdin"+ cppCheckFunc+ (hackTransform $ Text.unpack inputText)+ case parseResult of+ Left err -> throwE [ErrorInput err]+ Right x -> pure x+ (inlineConf, perItemConf) <-+ either (throwE . (: []) . uncurry ErrorMacroConfig) pure+ $ extractCommentConfigs anns (getTopLevelDeclNameMap parsedSource)+ let moduleConfig = cZipWith fromOptionIdentity config inlineConf+ let disableFormatting = moduleConfig & _conf_disable_formatting & confUnpack+ if disableFormatting+ then do+ return inputText+ else do+ (errsWarns, outputTextL) <- do+ let+ omitCheck =+ moduleConfig+ & _conf_errorHandling+ & _econf_omit_output_valid_check+ & confUnpack+ (ews, outRaw) <- if hasCPP || omitCheck+ then return $ pPrintModule moduleConfig perItemConf anns parsedSource+ else lift+ $ pPrintModuleAndCheck moduleConfig perItemConf anns parsedSource+ let+ hackF s = fromMaybe s+ $ TextL.stripPrefix (TextL.pack "-- BRITANY_INCLUDE_HACK ") s+ pure $ if hackAroundIncludes+ then+ ( ews+ , TextL.intercalate (TextL.pack "\n")+ $ hackF+ <$> TextL.splitOn (TextL.pack "\n") outRaw+ )+ else (ews, outRaw)+ let+ customErrOrder ErrorInput{} = 4+ customErrOrder LayoutWarning{} = 0 :: Int+ customErrOrder ErrorOutputCheck{} = 1+ customErrOrder ErrorUnusedComment{} = 2+ customErrOrder ErrorUnknownNode{} = 3+ customErrOrder ErrorMacroConfig{} = 5+ let+ hasErrors =+ if moduleConfig & _conf_errorHandling & _econf_Werror & confUnpack+ then not $ null errsWarns+ else 0 < maximum (-1 : fmap customErrOrder errsWarns)+ if hasErrors+ then throwE $ errsWarns+ else pure $ TextL.toStrict outputTextL++++-- BrittanyErrors can be non-fatal warnings, thus both are returned instead+-- of an Either.+-- This should be cleaned up once it is clear what kinds of errors really+-- can occur.+pPrintModule+ :: Config+ -> PerItemConfig+ -> ExactPrint.Anns+ -> GHC.ParsedSource+ -> ([BrittanyError], TextL.Text)+pPrintModule conf inlineConf anns parsedModule =+ let+ ((out, errs), debugStrings) =+ runIdentity+ $ MultiRWSS.runMultiRWSTNil+ $ MultiRWSS.withMultiWriterAW+ $ MultiRWSS.withMultiWriterAW+ $ MultiRWSS.withMultiWriterW+ $ MultiRWSS.withMultiReader anns+ $ MultiRWSS.withMultiReader conf+ $ MultiRWSS.withMultiReader inlineConf+ $ MultiRWSS.withMultiReader (extractToplevelAnns parsedModule anns)+ $ do+ traceIfDumpConf "bridoc annotations raw" _dconf_dump_annotations+ $ annsDoc anns+ ppModule parsedModule+ tracer = if Seq.null debugStrings+ then id+ else+ trace ("---- DEBUGMESSAGES ---- ")+ . foldr (seq . join trace) id debugStrings+ in tracer $ (errs, Text.Builder.toLazyText out)+ -- unless () $ do+ --+ -- debugStrings `forM_` \s ->+ -- trace s $ return ()++-- | Additionally checks that the output compiles again, appending an error+-- if it does not.+pPrintModuleAndCheck+ :: Config+ -> PerItemConfig+ -> ExactPrint.Anns+ -> GHC.ParsedSource+ -> IO ([BrittanyError], TextL.Text)+pPrintModuleAndCheck conf inlineConf anns parsedModule = do+ let ghcOptions = conf & _conf_forward & _options_ghc & runIdentity+ let (errs, output) = pPrintModule conf inlineConf anns parsedModule+ parseResult <- parseModuleFromString+ ghcOptions+ "output"+ (\_ -> return $ Right ())+ (TextL.unpack output)+ let+ errs' = errs ++ case parseResult of+ Left{} -> [ErrorOutputCheck]+ Right{} -> []+ return (errs', output)+++-- used for testing mostly, currently.+-- TODO: use parsePrintModule instead and remove this function.+parsePrintModuleTests :: Config -> String -> Text -> IO (Either String Text)+parsePrintModuleTests conf filename input = do+ let inputStr = Text.unpack input+ parseResult <- parseModuleFromString+ (conf & _conf_forward & _options_ghc & runIdentity)+ filename+ (const . pure $ Right ())+ inputStr+ case parseResult of+ Left err -> return $ Left err+ Right (anns, parsedModule, _) -> runExceptT $ do+ (inlineConf, perItemConf) <-+ case extractCommentConfigs anns (getTopLevelDeclNameMap parsedModule) of+ Left err -> throwE $ "error in inline config: " ++ show err+ Right x -> pure x+ let moduleConf = cZipWith fromOptionIdentity conf inlineConf+ let+ omitCheck =+ conf+ & _conf_errorHandling+ .> _econf_omit_output_valid_check+ .> confUnpack+ (errs, ltext) <- if omitCheck+ then return $ pPrintModule moduleConf perItemConf anns parsedModule+ else lift+ $ pPrintModuleAndCheck moduleConf perItemConf anns parsedModule+ if null errs+ then pure $ TextL.toStrict $ ltext+ else+ let+ errStrs = errs <&> \case+ ErrorInput str -> str+ ErrorUnusedComment str -> str+ LayoutWarning str -> str+ ErrorUnknownNode str _ -> str+ ErrorMacroConfig str _ -> "when parsing inline config: " ++ str+ ErrorOutputCheck -> "Output is not syntactically valid."+ in throwE $ "pretty printing error(s):\n" ++ List.unlines errStrs++-- this approach would for if there was a pure GHC.parseDynamicFilePragma.+-- Unfortunately that does not exist yet, so we cannot provide a nominally+-- pure interface.++-- parsePrintModuleTests :: Text -> Either String Text+-- parsePrintModuleTests input = do+-- let dflags = GHC.unsafeGlobalDynFlags+-- let fakeFileName = "SomeTestFakeFileName.hs"+-- let pragmaInfo = GHC.getOptions+-- dflags+-- (GHC.stringToStringBuffer $ Text.unpack input)+-- fakeFileName+-- (dflags1, _, _) <- GHC.parseDynamicFilePragma dflags pragmaInfo+-- let parseResult = ExactPrint.Parsers.parseWith+-- dflags1+-- fakeFileName+-- GHC.parseModule+-- inputStr+-- case parseResult of+-- Left (_, s) -> Left $ "parsing error: " ++ s+-- Right (anns, parsedModule) -> do+-- let (out, errs) = runIdentity+-- $ runMultiRWSTNil+-- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiWriterAW+-- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiWriterW+-- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiReader anns+-- $ ppModule parsedModule+-- if (not $ null errs)+-- then do+-- let errStrs = errs <&> \case+-- ErrorUnusedComment str -> str+-- Left $ "pretty printing error(s):\n" ++ List.unlines errStrs+-- else return $ TextL.toStrict $ Text.Builder.toLazyText out++toLocal :: Config -> ExactPrint.Anns -> PPMLocal a -> PPM a+toLocal conf anns m = do+ (x, write) <-+ lift $ MultiRWSS.runMultiRWSTAW (conf :+: anns :+: HNil) HNil $ m+ MultiRWSS.mGetRawW >>= \w -> MultiRWSS.mPutRawW (w `mappend` write)+ pure x++ppModule :: GenLocated SrcSpan HsModule -> PPM ()+ppModule lmod@(L _loc _m@(HsModule _ _name _exports _ decls _ _)) = do+ defaultAnns <- do+ anns <- mAsk+ let annKey = ExactPrint.mkAnnKey lmod+ let annMap = Map.findWithDefault Map.empty annKey anns+ let isEof = (== ExactPrint.AnnEofPos)+ let overAnnsDP f a = a { ExactPrint.annsDP = f $ ExactPrint.annsDP a }+ pure $ fmap (overAnnsDP . filter $ isEof . fst) annMap++ post <- ppPreamble lmod+ decls `forM_` \decl -> do+ let declAnnKey = ExactPrint.mkAnnKey decl+ let declBindingNames = getDeclBindingNames decl+ inlineConf <- mAsk+ let mDeclConf = Map.lookup declAnnKey $ _icd_perKey inlineConf+ let+ mBindingConfs =+ declBindingNames <&> \n -> Map.lookup n $ _icd_perBinding inlineConf+ filteredAnns <- mAsk <&> \annMap ->+ Map.union defaultAnns $ Map.findWithDefault Map.empty declAnnKey annMap++ traceIfDumpConf+ "bridoc annotations filtered/transformed"+ _dconf_dump_annotations+ $ annsDoc filteredAnns++ config <- mAsk++ let+ config' = cZipWith fromOptionIdentity config+ $ mconcat (catMaybes (mBindingConfs ++ [mDeclConf]))++ let exactprintOnly = config' & _conf_roundtrip_exactprint_only & confUnpack+ toLocal config' filteredAnns $ do+ bd <- if exactprintOnly+ then briDocMToPPM $ briDocByExactNoComment decl+ else do+ (r, errs, debugs) <- briDocMToPPMInner $ layoutDecl decl+ mTell debugs+ mTell errs+ if null errs+ then pure r+ else briDocMToPPM $ briDocByExactNoComment decl+ layoutBriDoc bd++ let+ finalComments = filter+ (fst .> \case+ ExactPrint.AnnComment{} -> True+ _ -> False+ )+ post+ post `forM_` \case+ (ExactPrint.AnnComment (ExactPrint.Comment cmStr _ _), l) -> do+ ppmMoveToExactLoc l+ mTell $ Text.Builder.fromString cmStr+ (ExactPrint.AnnEofPos, (ExactPrint.DP (eofZ, eofX))) ->+ let+ folder (acc, _) (kw, ExactPrint.DP (y, x)) = case kw of+ ExactPrint.AnnComment cm | span <- ExactPrint.commentIdentifier cm ->+ ( acc + y + GHC.srcSpanEndLine span - GHC.srcSpanStartLine span+ , x + GHC.srcSpanEndCol span - GHC.srcSpanStartCol span+ )+ _ -> (acc + y, x)+ (cmY, cmX) = foldl' folder (0, 0) finalComments+ in ppmMoveToExactLoc $ ExactPrint.DP (eofZ - cmY, eofX - cmX)+ _ -> return ()++getDeclBindingNames :: LHsDecl GhcPs -> [String]+getDeclBindingNames (L _ decl) = case decl of+ SigD _ (TypeSig _ ns _) -> ns <&> \(L _ n) -> Text.unpack (rdrNameToText n)+ ValD _ (FunBind _ (L _ n) _ _) -> [Text.unpack $ rdrNameToText n]+ _ -> []+++-- Prints the information associated with the module annotation+-- This includes the imports+ppPreamble+ :: GenLocated SrcSpan HsModule+ -> PPM [(ExactPrint.KeywordId, ExactPrint.DeltaPos)]+ppPreamble lmod@(L loc m@HsModule{}) = do+ filteredAnns <- mAsk <&> \annMap ->+ Map.findWithDefault Map.empty (ExactPrint.mkAnnKey lmod) annMap+ -- Since ghc-exactprint adds annotations following (implicit)+ -- modules to both HsModule and the elements in the module+ -- this can cause duplication of comments. So strip+ -- attached annotations that come after the module's where+ -- from the module node+ config <- mAsk+ let+ shouldReformatPreamble =+ config & _conf_layout & _lconfig_reformatModulePreamble & confUnpack++ let+ (filteredAnns', post) =+ case Map.lookup (ExactPrint.mkAnnKey lmod) filteredAnns of+ Nothing -> (filteredAnns, [])+ Just mAnn ->+ let+ modAnnsDp = ExactPrint.annsDP mAnn+ isWhere (ExactPrint.G AnnWhere) = True+ isWhere _ = False+ isEof (ExactPrint.AnnEofPos) = True+ isEof _ = False+ whereInd = List.findIndex (isWhere . fst) modAnnsDp+ eofInd = List.findIndex (isEof . fst) modAnnsDp+ (pre, post') = case (whereInd, eofInd) of+ (Nothing, Nothing) -> ([], modAnnsDp)+ (Just i, Nothing) -> List.splitAt (i + 1) modAnnsDp+ (Nothing, Just _i) -> ([], modAnnsDp)+ (Just i, Just j) -> List.splitAt (min (i + 1) j) modAnnsDp+ mAnn' = mAnn { ExactPrint.annsDP = pre }+ filteredAnns'' =+ Map.insert (ExactPrint.mkAnnKey lmod) mAnn' filteredAnns+ in (filteredAnns'', post')+ traceIfDumpConf+ "bridoc annotations filtered/transformed"+ _dconf_dump_annotations+ $ annsDoc filteredAnns'++ if shouldReformatPreamble+ then toLocal config filteredAnns' $ withTransformedAnns lmod $ do+ briDoc <- briDocMToPPM $ layoutModule lmod+ layoutBriDoc briDoc+ else+ let emptyModule = L loc m { hsmodDecls = [] }+ in MultiRWSS.withMultiReader filteredAnns' $ processDefault emptyModule+ return post++_sigHead :: Sig GhcPs -> String+_sigHead = \case+ TypeSig _ names _ ->+ "TypeSig " ++ intercalate "," (Text.unpack . lrdrNameToText <$> names)+ _ -> "unknown sig"++_bindHead :: HsBind GhcPs -> String+_bindHead = \case+ FunBind _ fId _ [] -> "FunBind " ++ (Text.unpack $ lrdrNameToText $ fId)+ PatBind _ _pat _ ([], []) -> "PatBind smth"+ _ -> "unknown bind"++++layoutBriDoc :: BriDocNumbered -> PPMLocal ()+layoutBriDoc briDoc = do+ -- first step: transform the briDoc.+ briDoc' <- MultiRWSS.withMultiStateS BDEmpty $ do+ -- Note that briDoc is BriDocNumbered, but state type is BriDoc.+ -- That's why the alt-transform looks a bit special here.+ traceIfDumpConf "bridoc raw" _dconf_dump_bridoc_raw+ $ briDocToDoc+ $ unwrapBriDocNumbered+ $ briDoc+ -- bridoc transformation: remove alts+ transformAlts briDoc >>= mSet+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf "bridoc post-alt" _dconf_dump_bridoc_simpl_alt+ -- bridoc transformation: float stuff in+ mGet >>= transformSimplifyFloating .> mSet+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf+ "bridoc post-floating"+ _dconf_dump_bridoc_simpl_floating+ -- bridoc transformation: par removal+ mGet >>= transformSimplifyPar .> mSet+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf "bridoc post-par" _dconf_dump_bridoc_simpl_par+ -- bridoc transformation: float stuff in+ mGet >>= transformSimplifyColumns .> mSet+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf "bridoc post-columns" _dconf_dump_bridoc_simpl_columns+ -- bridoc transformation: indent+ mGet >>= transformSimplifyIndent .> mSet+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf "bridoc post-indent" _dconf_dump_bridoc_simpl_indent+ mGet+ >>= briDocToDoc+ .> traceIfDumpConf "bridoc final" _dconf_dump_bridoc_final+ -- -- convert to Simple type+ -- simpl <- mGet <&> transformToSimple+ -- return simpl++ anns :: ExactPrint.Anns <- mAsk++ let+ state = LayoutState+ { _lstate_baseYs = [0]+ , _lstate_curYOrAddNewline = Right 0 -- important that we dont use left+ -- here because moveToAnn stuff+ -- of the first node needs to do+ -- its thing properly.+ , _lstate_indLevels = [0]+ , _lstate_indLevelLinger = 0+ , _lstate_comments = anns+ , _lstate_commentCol = Nothing+ , _lstate_addSepSpace = Nothing+ , _lstate_commentNewlines = 0+ }++ state' <- MultiRWSS.withMultiStateS state $ layoutBriDocM briDoc'++ let+ remainingComments =+ [ c+ | (ExactPrint.AnnKey _ con, elemAnns) <- Map.toList+ (_lstate_comments state')+ -- With the new import layouter, we manually process comments+ -- without relying on the backend to consume the comments out of+ -- the state/map. So they will end up here, and we need to ignore+ -- them.+ , ExactPrint.unConName con /= "ImportDecl"+ , c <- extractAllComments elemAnns+ ]+ remainingComments+ `forM_` (fst .> show .> ErrorUnusedComment .> (: []) .> mTell)++ return $ ()
+ source/library/Brittany/Internal/Backend.hs view
@@ -0,0 +1,732 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal.Backend where++import qualified Control.Monad.Trans.State.Strict as StateS+import qualified Data.Either as Either+import qualified Data.Foldable as Foldable+import qualified Data.IntMap.Lazy as IntMapL+import qualified Data.IntMap.Strict as IntMapS+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Semigroup as Semigroup+import qualified Data.Sequence as Seq+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified Data.Text.Lazy.Builder as Text.Builder+import qualified GHC.OldList as List+import Brittany.Internal.BackendUtils+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types++++type ColIndex = Int++data ColumnSpacing+ = ColumnSpacingLeaf Int+ | ColumnSpacingRef Int Int++type ColumnBlock a = [a]+type ColumnBlocks a = Seq [a]+type ColMap1+ = IntMapL.IntMap {- ColIndex -}+ (Bool, ColumnBlocks ColumnSpacing)+type ColMap2+ = IntMapL.IntMap {- ColIndex -}+ (Float, ColumnBlock Int, ColumnBlocks Int)+ -- (ratio of hasSpace, maximum, raw)++data ColInfo+ = ColInfoStart -- start value to begin the mapAccumL.+ | ColInfoNo BriDoc+ | ColInfo ColIndex ColSig [(Int, ColInfo)]++instance Show ColInfo where+ show ColInfoStart = "ColInfoStart"+ show (ColInfoNo bd) =+ "ColInfoNo " ++ show (take 30 (show (briDocToDoc bd)) ++ "..")+ show (ColInfo ind sig list) =+ "ColInfo " ++ show ind ++ " " ++ show sig ++ " " ++ show list++data ColBuildState = ColBuildState+ { _cbs_map :: ColMap1+ , _cbs_index :: ColIndex+ }++type LayoutConstraints m+ = ( MonadMultiReader Config m+ , MonadMultiReader ExactPrint.Types.Anns m+ , MonadMultiWriter Text.Builder.Builder m+ , MonadMultiWriter (Seq String) m+ , MonadMultiState LayoutState m+ )++layoutBriDocM :: forall m . LayoutConstraints m => BriDoc -> m ()+layoutBriDocM = \case+ BDEmpty -> do+ return () -- can it be that simple+ BDLit t -> do+ layoutIndentRestorePostComment+ layoutRemoveIndentLevelLinger+ layoutWriteAppend t+ BDSeq list -> do+ list `forM_` layoutBriDocM+ -- in this situation, there is nothing to do about cols.+ -- i think this one does not happen anymore with the current simplifications.+ -- BDCols cSig list | BDPar sameLine lines <- List.last list ->+ -- alignColsPar $ BDCols cSig (List.init list ++ [sameLine]) : lines+ BDCols _ list -> do+ list `forM_` layoutBriDocM+ BDSeparator -> do+ layoutAddSepSpace+ BDAddBaseY indent bd -> do+ let+ indentF = case indent of+ BrIndentNone -> id+ BrIndentRegular -> layoutWithAddBaseCol+ BrIndentSpecial i -> layoutWithAddBaseColN i+ indentF $ layoutBriDocM bd+ BDBaseYPushCur bd -> do+ layoutBaseYPushCur+ layoutBriDocM bd+ BDBaseYPop bd -> do+ layoutBriDocM bd+ layoutBaseYPop+ BDIndentLevelPushCur bd -> do+ layoutIndentLevelPushCur+ layoutBriDocM bd+ BDIndentLevelPop bd -> do+ layoutBriDocM bd+ layoutIndentLevelPop+ BDEnsureIndent indent bd -> do+ let+ indentF = case indent of+ BrIndentNone -> id+ BrIndentRegular -> layoutWithAddBaseCol+ BrIndentSpecial i -> layoutWithAddBaseColN i+ indentF $ do+ layoutWriteEnsureBlock+ layoutBriDocM bd+ BDPar indent sameLine indented -> do+ layoutBriDocM sameLine+ let+ indentF = case indent of+ BrIndentNone -> id+ BrIndentRegular -> layoutWithAddBaseCol+ BrIndentSpecial i -> layoutWithAddBaseColN i+ indentF $ do+ layoutWriteNewlineBlock+ layoutBriDocM indented+ BDLines lines -> alignColsLines lines+ BDAlt [] -> error "empty BDAlt"+ BDAlt (alt : _) -> layoutBriDocM alt+ BDForceMultiline bd -> layoutBriDocM bd+ BDForceSingleline bd -> layoutBriDocM bd+ BDForwardLineMode bd -> layoutBriDocM bd+ BDExternal annKey subKeys shouldAddComment t -> do+ let+ tlines = Text.lines $ t <> Text.pack "\n"+ tlineCount = length tlines+ anns :: ExactPrint.Anns <- mAsk+ when shouldAddComment $ do+ layoutWriteAppend+ $ Text.pack+ $ "{-"+ ++ show (annKey, Map.lookup annKey anns)+ ++ "-}"+ zip [1 ..] tlines `forM_` \(i, l) -> do+ layoutWriteAppend $ l+ unless (i == tlineCount) layoutWriteNewlineBlock+ do+ state <- mGet+ let filterF k _ = not $ k `Set.member` subKeys+ mSet $ state+ { _lstate_comments = Map.filterWithKey filterF $ _lstate_comments state+ }+ BDPlain t -> do+ layoutWriteAppend t+ BDAnnotationPrior annKey bd -> do+ state <- mGet+ let m = _lstate_comments state+ let+ moveToExactLocationAction = case _lstate_curYOrAddNewline state of+ Left{} -> pure ()+ Right{} -> moveToExactAnn annKey+ mAnn <- do+ let mAnn = ExactPrint.annPriorComments <$> Map.lookup annKey m+ mSet $ state+ { _lstate_comments = Map.adjust+ (\ann -> ann { ExactPrint.annPriorComments = [] })+ annKey+ m+ }+ return mAnn+ case mAnn of+ Nothing -> moveToExactLocationAction+ Just [] -> moveToExactLocationAction+ Just priors -> do+ -- layoutResetSepSpace+ priors+ `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->+ when (comment /= "(" && comment /= ")") $ do+ let commentLines = Text.lines $ Text.pack $ comment+ case comment of+ ('#' : _) ->+ layoutMoveToCommentPos y (-999) (length commentLines)+ -- ^ evil hack for CPP+ _ -> layoutMoveToCommentPos y x (length commentLines)+ -- fixedX <- fixMoveToLineByIsNewline x+ -- replicateM_ fixedX layoutWriteNewline+ -- layoutMoveToIndentCol y+ layoutWriteAppendMultiline commentLines+ -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }+ moveToExactLocationAction+ layoutBriDocM bd+ BDAnnotationKW annKey keyword bd -> do+ layoutBriDocM bd+ mComments <- do+ state <- mGet+ let m = _lstate_comments state+ let mAnn = ExactPrint.annsDP <$> Map.lookup annKey m+ let+ mToSpan = case mAnn of+ Just anns | Maybe.isNothing keyword -> Just anns+ Just ((ExactPrint.Types.G kw1, _) : annR) | keyword == Just kw1 ->+ Just annR+ _ -> Nothing+ case mToSpan of+ Just anns -> do+ let+ (comments, rest) = flip spanMaybe anns $ \case+ (ExactPrint.Types.AnnComment x, dp) -> Just (x, dp)+ _ -> Nothing+ mSet $ state+ { _lstate_comments = Map.adjust+ (\ann -> ann { ExactPrint.annsDP = rest })+ annKey+ m+ }+ return $ nonEmpty comments+ _ -> return Nothing+ case mComments of+ Nothing -> pure ()+ Just comments -> do+ comments+ `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->+ when (comment /= "(" && comment /= ")") $ do+ let commentLines = Text.lines $ Text.pack $ comment+ -- evil hack for CPP:+ case comment of+ ('#' : _) ->+ layoutMoveToCommentPos y (-999) (length commentLines)+ _ -> layoutMoveToCommentPos y x (length commentLines)+ -- fixedX <- fixMoveToLineByIsNewline x+ -- replicateM_ fixedX layoutWriteNewline+ -- layoutMoveToIndentCol y+ layoutWriteAppendMultiline commentLines+ -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }+ BDAnnotationRest annKey bd -> do+ layoutBriDocM bd+ annMay <- do+ state <- mGet+ let m = _lstate_comments state+ pure $ Map.lookup annKey m+ let mComments = nonEmpty . extractAllComments =<< annMay+ let+ semiCount = length+ [ ()+ | Just ann <- [annMay]+ , (ExactPrint.Types.AnnSemiSep, _) <- ExactPrint.Types.annsDP ann+ ]+ shouldAddSemicolonNewlines <-+ mAsk+ <&> _conf_layout+ .> _lconfig_experimentalSemicolonNewlines+ .> confUnpack+ mModify $ \state -> state+ { _lstate_comments = Map.adjust+ (\ann -> ann+ { ExactPrint.annFollowingComments = []+ , ExactPrint.annPriorComments = []+ , ExactPrint.annsDP = flip filter (ExactPrint.annsDP ann) $ \case+ (ExactPrint.Types.AnnComment{}, _) -> False+ _ -> True+ }+ )+ annKey+ (_lstate_comments state)+ }+ case mComments of+ Nothing -> do+ when shouldAddSemicolonNewlines $ do+ [1 .. semiCount] `forM_` const layoutWriteNewline+ Just comments -> do+ comments+ `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->+ when (comment /= "(" && comment /= ")") $ do+ let commentLines = Text.lines $ Text.pack comment+ case comment of+ ('#' : _) -> layoutMoveToCommentPos y (-999) 1+ -- ^ evil hack for CPP+ ")" -> pure ()+ -- ^ fixes the formatting of parens+ -- on the lhs of type alias defs+ _ -> layoutMoveToCommentPos y x (length commentLines)+ -- fixedX <- fixMoveToLineByIsNewline x+ -- replicateM_ fixedX layoutWriteNewline+ -- layoutMoveToIndentCol y+ layoutWriteAppendMultiline commentLines+ -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }+ BDMoveToKWDP annKey keyword shouldRestoreIndent bd -> do+ mDP <- do+ state <- mGet+ let m = _lstate_comments state+ let mAnn = ExactPrint.annsDP <$> Map.lookup annKey m+ let+ relevant =+ [ dp+ | Just ann <- [mAnn]+ , (ExactPrint.Types.G kw1, dp) <- ann+ , keyword == kw1+ ]+ -- mTell $ Seq.fromList [show keyword, "KWDP: " ++ show annKey ++ " " ++ show mAnn, show relevant]+ case relevant of+ [] -> pure Nothing+ (ExactPrint.Types.DP (y, x) : _) -> do+ mSet state { _lstate_commentNewlines = 0 }+ pure $ Just (y - _lstate_commentNewlines state, x)+ case mDP of+ Nothing -> pure ()+ Just (y, x) ->+ -- we abuse this, as we probably will print the KW next, which is+ -- _not_ a comment..+ layoutMoveToCommentPos y (if shouldRestoreIndent then x else 0) 1+ layoutBriDocM bd+ BDNonBottomSpacing _ bd -> layoutBriDocM bd+ BDSetParSpacing bd -> layoutBriDocM bd+ BDForceParSpacing bd -> layoutBriDocM bd+ BDDebug s bd -> do+ mTell $ Text.Builder.fromText $ Text.pack $ "{-" ++ s ++ "-}"+ layoutBriDocM bd++briDocLineLength :: BriDoc -> Int+briDocLineLength briDoc = flip StateS.evalState False $ rec briDoc+ -- the state encodes whether a separator was already+ -- appended at the current position.+ where+ rec = \case+ BDEmpty -> return $ 0+ BDLit t -> StateS.put False $> Text.length t+ BDSeq bds -> sum <$> rec `mapM` bds+ BDCols _ bds -> sum <$> rec `mapM` bds+ BDSeparator -> StateS.get >>= \b -> StateS.put True $> if b then 0 else 1+ BDAddBaseY _ bd -> rec bd+ BDBaseYPushCur bd -> rec bd+ BDBaseYPop bd -> rec bd+ BDIndentLevelPushCur bd -> rec bd+ BDIndentLevelPop bd -> rec bd+ BDPar _ line _ -> rec line+ BDAlt{} -> error "briDocLineLength BDAlt"+ BDForceMultiline bd -> rec bd+ BDForceSingleline bd -> rec bd+ BDForwardLineMode bd -> rec bd+ BDExternal _ _ _ t -> return $ Text.length t+ BDPlain t -> return $ Text.length t+ BDAnnotationPrior _ bd -> rec bd+ BDAnnotationKW _ _ bd -> rec bd+ BDAnnotationRest _ bd -> rec bd+ BDMoveToKWDP _ _ _ bd -> rec bd+ BDLines ls@(_ : _) -> do+ x <- StateS.get+ return $ maximum $ ls <&> \l -> StateS.evalState (rec l) x+ BDLines [] -> error "briDocLineLength BDLines []"+ BDEnsureIndent _ bd -> rec bd+ BDSetParSpacing bd -> rec bd+ BDForceParSpacing bd -> rec bd+ BDNonBottomSpacing _ bd -> rec bd+ BDDebug _ bd -> rec bd++briDocIsMultiLine :: BriDoc -> Bool+briDocIsMultiLine briDoc = rec briDoc+ where+ rec :: BriDoc -> Bool+ rec = \case+ BDEmpty -> False+ BDLit _ -> False+ BDSeq bds -> any rec bds+ BDCols _ bds -> any rec bds+ BDSeparator -> False+ BDAddBaseY _ bd -> rec bd+ BDBaseYPushCur bd -> rec bd+ BDBaseYPop bd -> rec bd+ BDIndentLevelPushCur bd -> rec bd+ BDIndentLevelPop bd -> rec bd+ BDPar{} -> True+ BDAlt{} -> error "briDocIsMultiLine BDAlt"+ BDForceMultiline _ -> True+ BDForceSingleline bd -> rec bd+ BDForwardLineMode bd -> rec bd+ BDExternal _ _ _ t | [_] <- Text.lines t -> False+ BDExternal{} -> True+ BDPlain t | [_] <- Text.lines t -> False+ BDPlain _ -> True+ BDAnnotationPrior _ bd -> rec bd+ BDAnnotationKW _ _ bd -> rec bd+ BDAnnotationRest _ bd -> rec bd+ BDMoveToKWDP _ _ _ bd -> rec bd+ BDLines (_ : _ : _) -> True+ BDLines [_] -> False+ BDLines [] -> error "briDocIsMultiLine BDLines []"+ BDEnsureIndent _ bd -> rec bd+ BDSetParSpacing bd -> rec bd+ BDForceParSpacing bd -> rec bd+ BDNonBottomSpacing _ bd -> rec bd+ BDDebug _ bd -> rec bd++-- In theory+-- =========++-- .. this algorithm works roughly in these steps:+--+-- 1. For each line, get the (nested) column info, descending as far as+-- BDCols nodes go. The column info is a (rose) tree where the leafs+-- are arbitrary (non-BDCols) BriDocs.+-- 2. Walk through the lines and compare its column info with that of its+-- predecessor. If both are non-leafs and the column "signatures" align+-- (they don't align e.g. when they are totally different syntactical+-- structures or the number of children differs), mark these parts of+-- the two tree structures as connected and recurse to its children+-- (i.e. again comparing the children in this line with the children in+-- the previous line).+-- 3. What we now have is one tree per line, and connections between "same"+-- nodes between lines. These connection can span multiple lines.+-- We next look at spacing information. This is available at the leafs,+-- but in this step we aggregate _over connections_. At the top level, this+-- gives us one piece of data: How long would each line be, if we fully+-- aligned everything (kept all connections "active"). In contrast to+-- just taking the sum of all leafs for each tree, this line length includes+-- the spaces used for alignment.+-- 4. Treat those lines where alignment would result in overflowing of the+-- column limit. This "treatment" is currently configurable, and can e.g.+-- mean:+-- a) we stop alignment alltogether,+-- b) we remove alignment just from the overflowing lines,+-- c) we reduce the number of spaces inserted in overflowing lines using+-- some technique to make them not overflow, but without reducing the+-- space insertion to zero,+-- d) don't do anything+-- 5. Actually print the lines, walking over each tree and inserting spaces+-- according to the info and decisions gathered in the previous steps.+--+-- Possible improvements+-- =====================+--+-- - If alignment is disabled for specific lines, the aggregated per-connection+-- info of those lines is still retained and not recalculated. This can+-- result in spaces being inserted to create alignment with a line that+-- would overflow and thus gets disabled entirely.+-- An better approach would be to repeat step 3 after marking overflowing+-- lines as such, and not include the overflowing spacings as references+-- for non-overflowing ones. In the simplest case one additional iteration+-- would suffice, e.g. 1-2-3-4-3-5, but it would also be possible to refine+-- this and first remove alignment in the deepest parts of the tree for+-- overflowing lines, repeating and moving upwards until no lines are+-- anymore overflowing.+-- Further, it may make sense to break up connections when overflowing would+-- occur.+-- - It may also make sense to not filter all overflowing lines, but remove+-- them one-by-one and in each step recalculate the aggregated connection+-- spacing info. Because removing one overflowing line from the calculation+-- may very well cause another previously overflowing line to not overflow+-- any longer.+-- There is also a nasty optimization problem hiding in there (find the+-- minimal amount of alignment disabling that results in no overflows)+-- but that is overkill.+--+-- (with both these improvements there would be quite some repetition between+-- steps 3 and 4, but it should be possible to ensure termination. Still,+-- performance might become an issue as such an approach is not necessarily+-- linear in bridoc size any more.)+--+-- In practice+-- ===========+--+-- .. the current implementation is somewhat sloppy. Steps 1 and 2+-- are executed in one step, step 3 already applies one strategy that disables+-- certain connections (see `_lconfig_alignmentLimit`) and step 4 does some+-- of the calculations one might expect to occur in step 3. Steps 4 and 5+-- are executed in the same recursion, too.+-- Also, _lconfig_alignmentLimit really is itself a hack that hides the issue+-- mentioned in the first "possible improvement".+alignColsLines :: LayoutConstraints m => [BriDoc] -> m ()+alignColsLines bridocs = do -- colInfos `forM_` \colInfo -> do+ -- tellDebugMess ("alignColsLines: at " ++ take 100 (show $ briDocToDoc $ head bridocs))+ curX <- do+ state <- mGet+ return $ Either.fromLeft 0 (_lstate_curYOrAddNewline state) + fromMaybe+ 0+ (_lstate_addSepSpace state)+ colMax <- mAsk <&> _conf_layout .> _lconfig_cols .> confUnpack+ alignMax <- mAsk <&> _conf_layout .> _lconfig_alignmentLimit .> confUnpack+ alignBreak <-+ mAsk <&> _conf_layout .> _lconfig_alignmentBreakOnMultiline .> confUnpack+ case () of+ _ -> do+ -- tellDebugMess ("processedMap: " ++ show processedMap)+ sequence_+ $ List.intersperse layoutWriteEnsureNewlineBlock+ $ colInfos+ <&> processInfo colMax processedMap+ where+ (colInfos, finalState) =+ StateS.runState (mergeBriDocs bridocs) (ColBuildState IntMapS.empty 0)+ -- maxZipper :: [Int] -> [Int] -> [Int]+ -- maxZipper [] ys = ys+ -- maxZipper xs [] = xs+ -- maxZipper (x:xr) (y:yr) = max x y : maxZipper xr yr+ colAggregation :: [Int] -> Int+ colAggregation [] = 0 -- this probably cannot happen the way we call+ -- this function, because _cbs_map only ever+ -- contains nonempty Seqs.+ colAggregation xs = maximum [ x | x <- xs, x <= minimum xs + alignMax' ]+ where alignMax' = max 0 alignMax++ processedMap :: ColMap2+ processedMap = fix $ \result ->+ _cbs_map finalState <&> \(lastFlag, colSpacingss) ->+ let+ colss = colSpacingss <&> \spss -> case reverse spss of+ [] -> []+ (xN : xR) ->+ reverse $ (if lastFlag then fLast else fInit) xN : fmap fInit xR+ where+ fLast (ColumnSpacingLeaf len) = len+ fLast (ColumnSpacingRef len _) = len+ fInit (ColumnSpacingLeaf len) = len+ fInit (ColumnSpacingRef _ i) = case IntMapL.lookup i result of+ Nothing -> 0+ Just (_, maxs, _) -> sum maxs+ maxCols = {-Foldable.foldl1 maxZipper-}+ fmap colAggregation $ transpose $ Foldable.toList colss+ (_, posXs) = -- trace ("colss=" ++ show colss ++ ", maxCols=" ++ show maxCols ++ " for " ++ take 100 (show $ briDocToDoc $ head bridocs)) $+ mapAccumL (\acc x -> (acc + x, acc)) curX maxCols+ counter count l = if List.last posXs + List.last l <= colMax+ then count + 1+ else count+ ratio = fromIntegral (foldl counter (0 :: Int) colss)+ / fromIntegral (length colss)+ in (ratio, maxCols, colss)++ mergeBriDocs :: [BriDoc] -> StateS.State ColBuildState [ColInfo]+ mergeBriDocs bds = mergeBriDocsW ColInfoStart bds++ mergeBriDocsW+ :: ColInfo -> [BriDoc] -> StateS.State ColBuildState [ColInfo]+ mergeBriDocsW _ [] = return []+ mergeBriDocsW lastInfo (bd : bdr) = do+ info <- mergeInfoBriDoc True lastInfo bd+ infor <- mergeBriDocsW+ -- (if alignBreak && briDocIsMultiLine bd then ColInfoStart else info)+ (if shouldBreakAfter bd then ColInfoStart else info)+ bdr+ return $ info : infor++ -- even with alignBreak config flag, we don't stop aligning for certain+ -- ColSigs - the ones with "False" below. The main reason is that+ -- there are uses of BDCols where they provide the alignment of several+ -- consecutive full larger code segments, for example ColOpPrefix.+ -- Motivating example is+ -- > foo+ -- > $ [ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+ -- > , bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+ -- > ]+ -- > ++ [ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ]+ -- If we break the alignment here, then all three lines for the first+ -- list move left by one, which is horrible. We really don't want to+ -- break whole-block alignments.+ -- For list, listcomp, tuple and tuples the reasoning is much simpler:+ -- alignment should not have much effect anyways, so i simply make the+ -- choice here that enabling alignment is the safer route for preventing+ -- potential glitches, and it should never have a negative effect.+ -- For RecUpdate the argument is much less clear - it is mostly a+ -- personal preference to not break alignment for those, even if+ -- multiline. Really, this should be configurable.. (TODO)+ shouldBreakAfter :: BriDoc -> Bool+ shouldBreakAfter bd = alignBreak && briDocIsMultiLine bd && case bd of+ (BDCols ColTyOpPrefix _) -> False+ (BDCols ColPatternsFuncPrefix _) -> True+ (BDCols ColPatternsFuncInfix _) -> True+ (BDCols ColPatterns _) -> True+ (BDCols ColCasePattern _) -> True+ (BDCols ColBindingLine{} _) -> True+ (BDCols ColGuard _) -> True+ (BDCols ColGuardedBody _) -> True+ (BDCols ColBindStmt _) -> True+ (BDCols ColDoLet _) -> True+ (BDCols ColRec _) -> False+ (BDCols ColRecUpdate _) -> False+ (BDCols ColRecDecl _) -> False+ (BDCols ColListComp _) -> False+ (BDCols ColList _) -> False+ (BDCols ColApp{} _) -> True+ (BDCols ColTuple _) -> False+ (BDCols ColTuples _) -> False+ (BDCols ColOpPrefix _) -> False+ _ -> True++ mergeInfoBriDoc+ :: Bool+ -> ColInfo+ -> BriDoc+ -> StateS.StateT ColBuildState Identity ColInfo+ mergeInfoBriDoc lastFlag ColInfoStart = briDocToColInfo lastFlag+ mergeInfoBriDoc lastFlag ColInfoNo{} = briDocToColInfo lastFlag+ mergeInfoBriDoc lastFlag (ColInfo infoInd infoSig subLengthsInfos) =+ \case+ brdc@(BDCols colSig subDocs)+ | infoSig == colSig && length subLengthsInfos == length subDocs -> do+ let+ isLastList = if lastFlag+ then (== length subDocs) <$> [1 ..]+ else repeat False+ infos <- zip3 isLastList (snd <$> subLengthsInfos) subDocs+ `forM` \(lf, info, bd) -> mergeInfoBriDoc lf info bd+ let curLengths = briDocLineLength <$> subDocs+ let trueSpacings = getTrueSpacings (zip curLengths infos)+ do -- update map+ s <- StateS.get+ let m = _cbs_map s+ let (Just (_, spaces)) = IntMapS.lookup infoInd m+ StateS.put s+ { _cbs_map = IntMapS.insert+ infoInd+ (lastFlag, spaces Seq.|> trueSpacings)+ m+ }+ return $ ColInfo infoInd colSig (zip curLengths infos)+ | otherwise -> briDocToColInfo lastFlag brdc+ brdc -> return $ ColInfoNo brdc++briDocToColInfo :: Bool -> BriDoc -> StateS.State ColBuildState ColInfo+briDocToColInfo lastFlag = \case+ BDCols sig list -> withAlloc lastFlag $ \ind -> do+ let+ isLastList =+ if lastFlag then (== length list) <$> [1 ..] else repeat False+ subInfos <- zip isLastList list `forM` uncurry briDocToColInfo+ let lengthInfos = zip (briDocLineLength <$> list) subInfos+ let trueSpacings = getTrueSpacings lengthInfos+ return $ (Seq.singleton trueSpacings, ColInfo ind sig lengthInfos)+ bd -> return $ ColInfoNo bd++getTrueSpacings :: [(Int, ColInfo)] -> [ColumnSpacing]+getTrueSpacings lengthInfos = lengthInfos <&> \case+ (len, ColInfo i _ _) -> ColumnSpacingRef len i+ (len, _) -> ColumnSpacingLeaf len++withAlloc+ :: Bool+ -> ( ColIndex+ -> StateS.State ColBuildState (ColumnBlocks ColumnSpacing, ColInfo)+ )+ -> StateS.State ColBuildState ColInfo+withAlloc lastFlag f = do+ cbs <- StateS.get+ let ind = _cbs_index cbs+ StateS.put $ cbs { _cbs_index = ind + 1 }+ (space, info) <- f ind+ StateS.get >>= \c -> StateS.put+ $ c { _cbs_map = IntMapS.insert ind (lastFlag, space) $ _cbs_map c }+ return info++processInfo :: LayoutConstraints m => Int -> ColMap2 -> ColInfo -> m ()+processInfo maxSpace m = \case+ ColInfoStart -> error "should not happen (TM)"+ ColInfoNo doc -> layoutBriDocM doc+ ColInfo ind _ list -> -- trace ("processInfo ind=" ++ show ind ++ ", list=" ++ show list ++ ", colmap=" ++ show m) $+ do+ colMaxConf <- mAsk <&> _conf_layout .> _lconfig_cols .> confUnpack+ alignMode <- mAsk <&> _conf_layout .> _lconfig_columnAlignMode .> confUnpack+ curX <- do+ state <- mGet+ -- tellDebugMess ("processInfo: " ++ show (_lstate_curYOrAddNewline state) ++ " - " ++ show ((_lstate_addSepSpace state)))+ let spaceAdd = fromMaybe 0 $ _lstate_addSepSpace state+ return $ case _lstate_curYOrAddNewline state of+ Left i -> case _lstate_commentCol state of+ Nothing -> spaceAdd + i+ Just c -> c+ Right{} -> spaceAdd+ let colMax = min colMaxConf (curX + maxSpace)+ -- tellDebugMess $ show curX+ let Just (ratio, maxCols1, _colss) = IntMapS.lookup ind m+ let+ maxCols2 = list <&> \case+ (_, ColInfo i _ _) ->+ let Just (_, ms, _) = IntMapS.lookup i m in sum ms+ (l, _) -> l+ let maxCols = zipWith max maxCols1 maxCols2+ let (maxX, posXs) = mapAccumL (\acc x -> (acc + x, acc)) curX maxCols+ -- handle the cases that the vertical alignment leads to more than max+ -- cols:+ -- this is not a full fix, and we must correct individually in addition.+ -- because: the (at least) line with the largest element in the last+ -- column will always still overflow, because we just updated the column+ -- sizes in such a way that it works _if_ we have sizes (*factor)+ -- in each column. but in that line, in the last column, we will be+ -- forced to occupy the full vertical space, not reduced by any factor.+ let+ fixedPosXs = case alignMode of+ ColumnAlignModeAnimouslyScale i | maxX > colMax -> fixed <&> (+ curX)+ where+ factor :: Float =+ -- 0.0001 as an offering to the floating point gods.+ min+ 1.0001+ (fromIntegral (i + colMax - curX) / fromIntegral (maxX - curX))+ offsets = (subtract curX) <$> posXs+ fixed = offsets <&> fromIntegral .> (* factor) .> truncate+ _ -> posXs+ let+ spacings =+ zipWith (-) (List.tail fixedPosXs ++ [min maxX colMax]) fixedPosXs+ -- tellDebugMess $ "ind = " ++ show ind+ -- tellDebugMess $ "maxCols = " ++ show maxCols+ -- tellDebugMess $ "fixedPosXs = " ++ show fixedPosXs+ -- tellDebugMess $ "list = " ++ show list+ -- tellDebugMess $ "maxSpace = " ++ show maxSpace+ let+ alignAct = zip3 fixedPosXs spacings list `forM_` \(destX, s, x) -> do+ layoutWriteEnsureAbsoluteN destX+ processInfo s m (snd x)+ noAlignAct = list `forM_` (snd .> processInfoIgnore)+ animousAct = -- trace ("animousAct fixedPosXs=" ++ show fixedPosXs ++ ", list=" ++ show list ++ ", maxSpace=" ++ show maxSpace ++ ", colMax="++show colMax) $+ if List.last fixedPosXs + fst (List.last list) > colMax+ -- per-item check if there is overflowing.+ then noAlignAct+ else alignAct+ case alignMode of+ ColumnAlignModeDisabled -> noAlignAct+ ColumnAlignModeUnanimously | maxX <= colMax -> alignAct+ ColumnAlignModeUnanimously -> noAlignAct+ ColumnAlignModeMajority limit | ratio >= limit -> animousAct+ ColumnAlignModeMajority{} -> noAlignAct+ ColumnAlignModeAnimouslyScale{} -> animousAct+ ColumnAlignModeAnimously -> animousAct+ ColumnAlignModeAlways -> alignAct++processInfoIgnore :: LayoutConstraints m => ColInfo -> m ()+processInfoIgnore = \case+ ColInfoStart -> error "should not happen (TM)"+ ColInfoNo doc -> layoutBriDocM doc+ ColInfo _ _ list -> list `forM_` (snd .> processInfoIgnore)
+ source/library/Brittany/Internal/BackendUtils.hs view
@@ -0,0 +1,487 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.BackendUtils where++import qualified Data.Data+import qualified Data.Either+import qualified Data.Map as Map+import qualified Data.Maybe+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import qualified Data.Text.Lazy.Builder as Text.Builder+import GHC (Located)+import qualified GHC.OldList as List+import Brittany.Internal.Config.Types+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils+import Language.Haskell.GHC.ExactPrint.Types (AnnKey, Annotation)+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint++++traceLocal :: (MonadMultiState LayoutState m) => a -> m ()+traceLocal _ = return ()+++layoutWriteAppend+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Text+ -> m ()+layoutWriteAppend t = do+ traceLocal ("layoutWriteAppend", t)+ state <- mGet+ case _lstate_curYOrAddNewline state of+ Right i -> do+ replicateM_ i $ mTell $ Text.Builder.fromString $ "\n"+ Left{} -> do+ return ()+ let spaces = fromMaybe 0 $ _lstate_addSepSpace state+ mTell $ Text.Builder.fromText $ Text.pack (replicate spaces ' ')+ mTell $ Text.Builder.fromText $ t+ mModify $ \s -> s+ { _lstate_curYOrAddNewline = Left $ case _lstate_curYOrAddNewline s of+ Left c -> c + Text.length t + spaces+ Right{} -> Text.length t + spaces+ , _lstate_addSepSpace = Nothing+ }++layoutWriteAppendSpaces+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Int+ -> m ()+layoutWriteAppendSpaces i = do+ traceLocal ("layoutWriteAppendSpaces", i)+ unless (i == 0) $ do+ state <- mGet+ mSet $ state+ { _lstate_addSepSpace = Just $ maybe i (+ i) $ _lstate_addSepSpace state+ }++layoutWriteAppendMultiline+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => [Text]+ -> m ()+layoutWriteAppendMultiline ts = do+ traceLocal ("layoutWriteAppendMultiline", ts)+ case ts of+ [] -> layoutWriteAppend (Text.pack "") -- need to write empty, too.+ (l : lr) -> do+ layoutWriteAppend l+ lr `forM_` \x -> do+ layoutWriteNewline+ layoutWriteAppend x++-- adds a newline and adds spaces to reach the base column.+layoutWriteNewlineBlock+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => m ()+layoutWriteNewlineBlock = do+ traceLocal ("layoutWriteNewlineBlock")+ state <- mGet+ mSet $ state+ { _lstate_curYOrAddNewline = Right 1+ , _lstate_addSepSpace = Just $ lstate_baseY state+ }++-- layoutMoveToIndentCol :: ( MonadMultiState LayoutState m+-- , MonadMultiWriter (Seq String) m) => Int -> m ()+-- layoutMoveToIndentCol i = do+-- #if INSERTTRACES+-- tellDebugMessShow ("layoutMoveToIndentCol", i)+-- #endif+-- state <- mGet+-- mSet $ state+-- { _lstate_addSepSpace = Just+-- $ if isJust $ _lstate_addNewline state+-- then i+-- else _lstate_indLevelLinger state + i - _lstate_curY state+-- }++layoutSetCommentCol :: (MonadMultiState LayoutState m) => m ()+layoutSetCommentCol = do+ state <- mGet+ let+ col = case _lstate_curYOrAddNewline state of+ Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)+ Right{} -> lstate_baseY state+ traceLocal ("layoutSetCommentCol", col)+ unless (Data.Maybe.isJust $ _lstate_commentCol state)+ $ mSet state { _lstate_commentCol = Just col }++-- This is also used to move to non-comments in a couple of places. Seems+-- to be harmless so far..+layoutMoveToCommentPos+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Int+ -> Int+ -> Int+ -> m ()+layoutMoveToCommentPos y x commentLines = do+ traceLocal ("layoutMoveToCommentPos", y, x, commentLines)+ state <- mGet+ mSet state+ { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of+ Left i -> if y == 0 then Left i else Right y+ Right{} -> Right y+ , _lstate_addSepSpace =+ Just $ if Data.Maybe.isJust (_lstate_commentCol state)+ then case _lstate_curYOrAddNewline state of+ Left{} -> if y == 0 then x else _lstate_indLevelLinger state + x+ Right{} -> _lstate_indLevelLinger state + x+ else if y == 0 then x else _lstate_indLevelLinger state + x+ , _lstate_commentCol = Just $ case _lstate_commentCol state of+ Just existing -> existing+ Nothing -> case _lstate_curYOrAddNewline state of+ Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)+ Right{} -> lstate_baseY state+ , _lstate_commentNewlines =+ _lstate_commentNewlines state + y + commentLines - 1+ }++-- | does _not_ add spaces to again reach the current base column.+layoutWriteNewline+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => m ()+layoutWriteNewline = do+ traceLocal ("layoutWriteNewline")+ state <- mGet+ mSet $ state+ { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of+ Left{} -> Right 1+ Right i -> Right (i + 1)+ , _lstate_addSepSpace = Nothing+ }++_layoutResetCommentNewlines :: MonadMultiState LayoutState m => m ()+_layoutResetCommentNewlines = do+ mModify $ \state -> state { _lstate_commentNewlines = 0 }++layoutWriteEnsureNewlineBlock+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => m ()+layoutWriteEnsureNewlineBlock = do+ traceLocal ("layoutWriteEnsureNewlineBlock")+ state <- mGet+ mSet $ state+ { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of+ Left{} -> Right 1+ Right i -> Right $ max 1 i+ , _lstate_addSepSpace = Just $ lstate_baseY state+ , _lstate_commentCol = Nothing+ }++layoutWriteEnsureAbsoluteN+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Int+ -> m ()+layoutWriteEnsureAbsoluteN n = do+ state <- mGet+ let+ diff = case (_lstate_commentCol state, _lstate_curYOrAddNewline state) of+ (Just c, _) -> n - c+ (Nothing, Left i) -> n - i+ (Nothing, Right{}) -> n+ traceLocal ("layoutWriteEnsureAbsoluteN", n, diff)+ when (diff > 0) $ do+ mSet $ state { _lstate_addSepSpace = Just diff } -- this always sets to+ -- at least (Just 1), so we won't+ -- overwrite any old value in any+ -- bad way.++layoutBaseYPushInternal :: (MonadMultiState LayoutState m) => Int -> m ()+layoutBaseYPushInternal i = do+ traceLocal ("layoutBaseYPushInternal", i)+ mModify $ \s -> s { _lstate_baseYs = i : _lstate_baseYs s }++layoutBaseYPopInternal :: (MonadMultiState LayoutState m) => m ()+layoutBaseYPopInternal = do+ traceLocal ("layoutBaseYPopInternal")+ mModify $ \s -> s { _lstate_baseYs = List.tail $ _lstate_baseYs s }++layoutIndentLevelPushInternal :: (MonadMultiState LayoutState m) => Int -> m ()+layoutIndentLevelPushInternal i = do+ traceLocal ("layoutIndentLevelPushInternal", i)+ mModify $ \s -> s+ { _lstate_indLevelLinger = lstate_indLevel s+ , _lstate_indLevels = i : _lstate_indLevels s+ }++layoutIndentLevelPopInternal :: (MonadMultiState LayoutState m) => m ()+layoutIndentLevelPopInternal = do+ traceLocal ("layoutIndentLevelPopInternal")+ mModify $ \s -> s+ { _lstate_indLevelLinger = lstate_indLevel s+ , _lstate_indLevels = List.tail $ _lstate_indLevels s+ }++layoutRemoveIndentLevelLinger :: (MonadMultiState LayoutState m) => m ()+layoutRemoveIndentLevelLinger = do+ mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s }++layoutWithAddBaseCol+ :: ( MonadMultiWriter Text.Builder.Builder m+ , MonadMultiState LayoutState m+ , MonadMultiReader Config m+ )+ => m ()+ -> m ()+layoutWithAddBaseCol m = do+ amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ state <- mGet+ layoutBaseYPushInternal $ lstate_baseY state + amount+ m+ layoutBaseYPopInternal++layoutWithAddBaseColBlock+ :: ( MonadMultiWriter Text.Builder.Builder m+ , MonadMultiState LayoutState m+ , MonadMultiReader Config m+ )+ => m ()+ -> m ()+layoutWithAddBaseColBlock m = do+ amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ state <- mGet+ layoutBaseYPushInternal $ lstate_baseY state + amount+ layoutWriteEnsureBlock+ m+ layoutBaseYPopInternal++layoutWithAddBaseColNBlock+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Int+ -> m ()+ -> m ()+layoutWithAddBaseColNBlock amount m = do+ traceLocal ("layoutWithAddBaseColNBlock", amount)+ state <- mGet+ layoutBaseYPushInternal $ lstate_baseY state + amount+ layoutWriteEnsureBlock+ m+ layoutBaseYPopInternal++layoutWriteEnsureBlock+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => m ()+layoutWriteEnsureBlock = do+ traceLocal ("layoutWriteEnsureBlock")+ state <- mGet+ let+ diff = case (_lstate_addSepSpace state, _lstate_curYOrAddNewline state) of+ (Nothing, Left i) -> lstate_baseY state - i+ (Nothing, Right{}) -> lstate_baseY state+ (Just sp, Left i) -> max sp (lstate_baseY state - i)+ (Just sp, Right{}) -> max sp (lstate_baseY state)+ -- when (diff>0) $ layoutWriteNewlineBlock+ when (diff > 0) $ do+ mSet $ state { _lstate_addSepSpace = Just $ diff }++layoutWithAddBaseColN+ :: (MonadMultiWriter Text.Builder.Builder m, MonadMultiState LayoutState m)+ => Int+ -> m ()+ -> m ()+layoutWithAddBaseColN amount m = do+ state <- mGet+ layoutBaseYPushInternal $ lstate_baseY state + amount+ m+ layoutBaseYPopInternal++layoutBaseYPushCur :: (MonadMultiState LayoutState m) => m ()+layoutBaseYPushCur = do+ traceLocal ("layoutBaseYPushCur")+ state <- mGet+ case _lstate_commentCol state of+ Nothing ->+ case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of+ (Left i, Just j) -> layoutBaseYPushInternal (i + j)+ (Left i, Nothing) -> layoutBaseYPushInternal i+ (Right{}, _) -> layoutBaseYPushInternal $ lstate_baseY state+ Just cCol -> layoutBaseYPushInternal cCol++layoutBaseYPop :: (MonadMultiState LayoutState m) => m ()+layoutBaseYPop = do+ traceLocal ("layoutBaseYPop")+ layoutBaseYPopInternal++layoutIndentLevelPushCur :: (MonadMultiState LayoutState m) => m ()+layoutIndentLevelPushCur = do+ traceLocal ("layoutIndentLevelPushCur")+ state <- mGet+ let+ y = case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of+ (Left i, Just j) -> i + j+ (Left i, Nothing) -> i+ (Right{}, Just j) -> j+ (Right{}, Nothing) -> 0+ layoutIndentLevelPushInternal y++layoutIndentLevelPop :: (MonadMultiState LayoutState m) => m ()+layoutIndentLevelPop = do+ traceLocal ("layoutIndentLevelPop")+ layoutIndentLevelPopInternal+ -- why are comment indentations relative to the previous indentation on+ -- the first node of an additional indentation, and relative to the outer+ -- indentation after the last node of some indented stuff? sure does not+ -- make sense.+ layoutRemoveIndentLevelLinger++layoutAddSepSpace :: (MonadMultiState LayoutState m) => m ()+layoutAddSepSpace = do+ state <- mGet+ mSet $ state+ { _lstate_addSepSpace = Just $ fromMaybe 1 $ _lstate_addSepSpace state+ }++-- TODO: when refactoring is complete, the other version of this method+-- can probably be removed.+moveToExactAnn+ :: ( MonadMultiWriter Text.Builder.Builder m+ , MonadMultiState LayoutState m+ , MonadMultiReader (Map AnnKey Annotation) m+ )+ => AnnKey+ -> m ()+moveToExactAnn annKey = do+ traceLocal ("moveToExactAnn", annKey)+ anns <- mAsk+ case Map.lookup annKey anns of+ Nothing -> return ()+ Just ann -> do+ -- curY <- mGet <&> _lstate_curY+ let ExactPrint.DP (y, _x) = ExactPrint.annEntryDelta ann+ -- mModify $ \state -> state { _lstate_addNewline = Just x }+ moveToY y++moveToY :: MonadMultiState LayoutState m => Int -> m ()+moveToY y = mModify $ \state ->+ let+ upd = case _lstate_curYOrAddNewline state of+ Left i -> if y == 0 then Left i else Right y+ Right i -> Right $ max y i+ in+ state+ { _lstate_curYOrAddNewline = upd+ , _lstate_addSepSpace = if Data.Either.isRight upd+ then _lstate_commentCol state <|> _lstate_addSepSpace state <|> Just+ (lstate_baseY state)+ else Nothing+ , _lstate_commentCol = Nothing+ }+-- fixMoveToLineByIsNewline :: MonadMultiState+-- LayoutState m => Int -> m Int+-- fixMoveToLineByIsNewline x = do+-- newLineState <- mGet <&> _lstate_isNewline+-- return $ if newLineState == NewLineStateYes+-- then x-1+-- else x++ppmMoveToExactLoc+ :: MonadMultiWriter Text.Builder.Builder m => ExactPrint.DeltaPos -> m ()+ppmMoveToExactLoc (ExactPrint.DP (x, y)) = do+ replicateM_ x $ mTell $ Text.Builder.fromString "\n"+ replicateM_ y $ mTell $ Text.Builder.fromString " "++-- TODO: update and use, or clean up. Currently dead code.+layoutWritePriorComments+ :: ( Data.Data.Data ast+ , MonadMultiWriter Text.Builder.Builder m+ , MonadMultiState LayoutState m+ )+ => Located ast+ -> m ()+layoutWritePriorComments ast = do+ mAnn <- do+ state <- mGet+ let key = ExactPrint.mkAnnKey ast+ let anns = _lstate_comments state+ let mAnn = ExactPrint.annPriorComments <$> Map.lookup key anns+ mSet $ state+ { _lstate_comments = Map.adjust+ (\ann -> ann { ExactPrint.annPriorComments = [] })+ key+ anns+ }+ return mAnn+ case mAnn of+ Nothing -> return ()+ Just priors -> do+ unless (null priors) $ layoutSetCommentCol+ priors `forM_` \(ExactPrint.Comment comment _ _, ExactPrint.DP (x, y)) ->+ do+ replicateM_ x layoutWriteNewline+ layoutWriteAppendSpaces y+ layoutWriteAppendMultiline $ Text.lines $ Text.pack comment++-- TODO: update and use, or clean up. Currently dead code.+-- this currently only extracs from the `annsDP` field of Annotations.+-- per documentation, this seems sufficient, as the+-- "..`annFollowingComments` are only added by AST transformations ..".+layoutWritePostComments+ :: ( Data.Data.Data ast+ , MonadMultiWriter Text.Builder.Builder m+ , MonadMultiState LayoutState m+ )+ => Located ast+ -> m ()+layoutWritePostComments ast = do+ mAnn <- do+ state <- mGet+ let key = ExactPrint.mkAnnKey ast+ let anns = _lstate_comments state+ let mAnn = ExactPrint.annFollowingComments <$> Map.lookup key anns+ mSet $ state+ { _lstate_comments = Map.adjust+ (\ann -> ann { ExactPrint.annFollowingComments = [] })+ key+ anns+ }+ return mAnn+ case mAnn of+ Nothing -> return ()+ Just posts -> do+ unless (null posts) $ layoutSetCommentCol+ posts `forM_` \(ExactPrint.Comment comment _ _, ExactPrint.DP (x, y)) ->+ do+ replicateM_ x layoutWriteNewline+ layoutWriteAppend $ Text.pack $ replicate y ' '+ mModify $ \s -> s { _lstate_addSepSpace = Nothing }+ layoutWriteAppendMultiline $ Text.lines $ Text.pack $ comment++layoutIndentRestorePostComment+ :: (MonadMultiState LayoutState m, MonadMultiWriter Text.Builder.Builder m)+ => m ()+layoutIndentRestorePostComment = do+ state <- mGet+ let mCommentCol = _lstate_commentCol state+ let eCurYAddNL = _lstate_curYOrAddNewline state+ mModify+ $ \s -> s { _lstate_commentCol = Nothing, _lstate_commentNewlines = 0 }+ case (mCommentCol, eCurYAddNL) of+ (Just commentCol, Left{}) -> do+ layoutWriteEnsureNewlineBlock+ layoutWriteEnsureAbsoluteN $ commentCol + fromMaybe+ 0+ (_lstate_addSepSpace state)+ _ -> return ()++-- layoutWritePriorCommentsRestore :: (Data.Data.Data ast,+-- MonadMultiWriter Text.Builder.Builder m,+-- MonadMultiState LayoutState m+-- , MonadMultiWriter (Seq String) m)+-- => Located ast -> m ()+-- layoutWritePriorCommentsRestore x = do+-- layoutWritePriorComments x+-- layoutIndentRestorePostComment+--+-- layoutWritePostCommentsRestore :: (Data.Data.Data ast,+-- MonadMultiWriter Text.Builder.Builder m,+-- MonadMultiState LayoutState m+-- , MonadMultiWriter (Seq String) m)+-- => Located ast -> m ()+-- layoutWritePostCommentsRestore x = do+-- layoutWritePostComments x+-- layoutIndentRestorePostComment
+ source/library/Brittany/Internal/Config.hs view
@@ -0,0 +1,288 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Config where++import qualified Data.Bool as Bool+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8+import Data.CZipWith+import Data.Coerce (coerce)+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Semigroup as Semigroup+import qualified Data.Yaml+import qualified GHC.OldList as List+import Brittany.Internal.Config.Types+import Brittany.Internal.Config.Types.Instances ()+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Utils+import qualified System.Console.CmdArgs.Explicit as CmdArgs+import qualified System.Directory+import qualified System.Directory as Directory+import qualified System.FilePath.Posix as FilePath+import qualified System.IO+import UI.Butcher.Monadic++-- brittany-next-binding { lconfig_indentPolicy: IndentPolicyLeft }+staticDefaultConfig :: Config+staticDefaultConfig = Config+ { _conf_version = coerce (1 :: Int)+ , _conf_debug = DebugConfig+ { _dconf_dump_config = coerce False+ , _dconf_dump_annotations = coerce False+ , _dconf_dump_ast_unknown = coerce False+ , _dconf_dump_ast_full = coerce False+ , _dconf_dump_bridoc_raw = coerce False+ , _dconf_dump_bridoc_simpl_alt = coerce False+ , _dconf_dump_bridoc_simpl_floating = coerce False+ , _dconf_dump_bridoc_simpl_par = coerce False+ , _dconf_dump_bridoc_simpl_columns = coerce False+ , _dconf_dump_bridoc_simpl_indent = coerce False+ , _dconf_dump_bridoc_final = coerce False+ , _dconf_roundtrip_exactprint_only = coerce False+ }+ , _conf_layout = LayoutConfig+ { _lconfig_cols = coerce (80 :: Int)+ , _lconfig_indentPolicy = coerce IndentPolicyFree+ , _lconfig_indentAmount = coerce (2 :: Int)+ , _lconfig_indentWhereSpecial = coerce True+ , _lconfig_indentListSpecial = coerce True+ , _lconfig_importColumn = coerce (50 :: Int)+ , _lconfig_importAsColumn = coerce (50 :: Int)+ , _lconfig_altChooser = coerce (AltChooserBoundedSearch 3)+ , _lconfig_columnAlignMode = coerce (ColumnAlignModeMajority 0.7)+ , _lconfig_alignmentLimit = coerce (30 :: Int)+ , _lconfig_alignmentBreakOnMultiline = coerce True+ , _lconfig_hangingTypeSignature = coerce False+ , _lconfig_reformatModulePreamble = coerce True+ , _lconfig_allowSingleLineExportList = coerce False+ , _lconfig_allowHangingQuasiQuotes = coerce True+ , _lconfig_experimentalSemicolonNewlines = coerce False+ -- , _lconfig_allowSinglelineRecord = coerce False+ }+ , _conf_errorHandling = ErrorHandlingConfig+ { _econf_produceOutputOnErrors = coerce False+ , _econf_Werror = coerce False+ , _econf_ExactPrintFallback = coerce ExactPrintFallbackModeInline+ , _econf_omit_output_valid_check = coerce False+ }+ , _conf_preprocessor = PreProcessorConfig+ { _ppconf_CPPMode = coerce CPPModeAbort+ , _ppconf_hackAroundIncludes = coerce False+ }+ , _conf_forward = ForwardOptions { _options_ghc = Identity [] }+ , _conf_roundtrip_exactprint_only = coerce False+ , _conf_disable_formatting = coerce False+ , _conf_obfuscate = coerce False+ }++forwardOptionsSyntaxExtsEnabled :: ForwardOptions+forwardOptionsSyntaxExtsEnabled = ForwardOptions+ { _options_ghc = Identity+ [ "-XLambdaCase"+ , "-XMultiWayIf"+ , "-XGADTs"+ , "-XPatternGuards"+ , "-XViewPatterns"+ , "-XTupleSections"+ , "-XExplicitForAll"+ , "-XImplicitParams"+ , "-XQuasiQuotes"+ , "-XTemplateHaskell"+ , "-XBangPatterns"+ , "-XTypeApplications"+ ]+ }++-- brittany-next-binding { lconfig_indentPolicy: IndentPolicyLeft, lconfig_cols: 200 }+cmdlineConfigParser :: CmdParser Identity out (CConfig Maybe)+cmdlineConfigParser = do+ -- TODO: why does the default not trigger; ind never should be []!!+ ind <- addFlagReadParams "" ["indent"] "AMOUNT" (flagHelpStr "spaces per indentation level")+ cols <- addFlagReadParams "" ["columns"] "AMOUNT" (flagHelpStr "target max columns (80 is an old default for this)")+ importCol <- addFlagReadParams "" ["import-col"] "N" (flagHelpStr "column to align import lists at")+ importAsCol <- addFlagReadParams "" ["import-as-col"] "N" (flagHelpStr "column to qualified-as module names at")++ dumpConfig <- addSimpleBoolFlag "" ["dump-config"] (flagHelp $ parDoc "dump the programs full config (merged commandline + file + defaults)")+ dumpAnnotations <- addSimpleBoolFlag "" ["dump-annotations"] (flagHelp $ parDoc "dump the full annotations returned by ghc-exactprint")+ dumpUnknownAST <- addSimpleBoolFlag "" ["dump-ast-unknown"] (flagHelp $ parDoc "dump the ast for any nodes not transformed, but copied as-is by brittany")+ dumpCompleteAST <- addSimpleBoolFlag "" ["dump-ast-full"] (flagHelp $ parDoc "dump the full ast")+ dumpBriDocRaw <- addSimpleBoolFlag "" ["dump-bridoc-raw"] (flagHelp $ parDoc "dump the pre-transformation bridoc")+ dumpBriDocAlt <- addSimpleBoolFlag "" ["dump-bridoc-alt"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: alt")+ dumpBriDocPar <- addSimpleBoolFlag "" ["dump-bridoc-par"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: par")+ dumpBriDocFloating <- addSimpleBoolFlag "" ["dump-bridoc-floating"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: floating")+ dumpBriDocColumns <- addSimpleBoolFlag "" ["dump-bridoc-columns"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: columns")+ dumpBriDocIndent <- addSimpleBoolFlag "" ["dump-bridoc-indent"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: indent")+ dumpBriDocFinal <- addSimpleBoolFlag "" ["dump-bridoc-final"] (flagHelp $ parDoc "dump the post-transformation bridoc")++ outputOnErrors <- addSimpleBoolFlag "" ["output-on-errors"] (flagHelp $ parDoc "even when there are errors, produce output (or try to to the degree possible)")+ wError <- addSimpleBoolFlag "" ["werror"] (flagHelp $ parDoc "treat warnings as errors")+ omitValidCheck <- addSimpleBoolFlag "" ["omit-output-check"] (flagHelp $ parDoc "omit checking if the output is syntactically valid (debugging)")++ roundtripOnly <- addSimpleBoolFlag "" ["exactprint-only"] (flagHelp $ parDoc "do not reformat, but exclusively use exactprint to roundtrip (debugging)")++ optionsGhc <- addFlagStringParams "" ["ghc-options"] "STRING" (flagHelp $ parDoc "allows to define default language extensions. The parameter is forwarded to ghc.")+ disableFormatting <- addSimpleBoolFlag "" ["disable-formatting"] (flagHelp $ parDoc "parse, but don't transform the input at all. Useful for inline config for specific modules.")+ obfuscate <- addSimpleBoolFlag "" ["obfuscate"] (flagHelp $ parDoc "apply obfuscator to the output.")++ return $ Config+ { _conf_version = mempty+ , _conf_debug = DebugConfig+ { _dconf_dump_config = wrapLast $ falseToNothing dumpConfig+ , _dconf_dump_annotations = wrapLast $ falseToNothing dumpAnnotations+ , _dconf_dump_ast_unknown = wrapLast $ falseToNothing dumpUnknownAST+ , _dconf_dump_ast_full = wrapLast $ falseToNothing dumpCompleteAST+ , _dconf_dump_bridoc_raw = wrapLast $ falseToNothing dumpBriDocRaw+ , _dconf_dump_bridoc_simpl_alt = wrapLast $ falseToNothing dumpBriDocAlt+ , _dconf_dump_bridoc_simpl_par = wrapLast $ falseToNothing dumpBriDocPar+ , _dconf_dump_bridoc_simpl_floating = wrapLast $ falseToNothing dumpBriDocFloating+ , _dconf_dump_bridoc_simpl_columns = wrapLast $ falseToNothing dumpBriDocColumns+ , _dconf_dump_bridoc_simpl_indent = wrapLast $ falseToNothing dumpBriDocIndent+ , _dconf_dump_bridoc_final = wrapLast $ falseToNothing dumpBriDocFinal+ , _dconf_roundtrip_exactprint_only = mempty+ }+ , _conf_layout = LayoutConfig+ { _lconfig_cols = optionConcat cols+ , _lconfig_indentPolicy = mempty+ , _lconfig_indentAmount = optionConcat ind+ , _lconfig_indentWhereSpecial = mempty -- falseToNothing _+ , _lconfig_indentListSpecial = mempty -- falseToNothing _+ , _lconfig_importColumn = optionConcat importCol+ , _lconfig_importAsColumn = optionConcat importAsCol+ , _lconfig_altChooser = mempty+ , _lconfig_columnAlignMode = mempty+ , _lconfig_alignmentLimit = mempty+ , _lconfig_alignmentBreakOnMultiline = mempty+ , _lconfig_hangingTypeSignature = mempty+ , _lconfig_reformatModulePreamble = mempty+ , _lconfig_allowSingleLineExportList = mempty+ , _lconfig_allowHangingQuasiQuotes = mempty+ , _lconfig_experimentalSemicolonNewlines = mempty+ -- , _lconfig_allowSinglelineRecord = mempty+ }+ , _conf_errorHandling = ErrorHandlingConfig+ { _econf_produceOutputOnErrors = wrapLast $ falseToNothing outputOnErrors+ , _econf_Werror = wrapLast $ falseToNothing wError+ , _econf_ExactPrintFallback = mempty+ , _econf_omit_output_valid_check = wrapLast $ falseToNothing omitValidCheck+ }+ , _conf_preprocessor = PreProcessorConfig { _ppconf_CPPMode = mempty, _ppconf_hackAroundIncludes = mempty }+ , _conf_forward = ForwardOptions { _options_ghc = [ optionsGhc & List.unwords & CmdArgs.splitArgs | not $ null optionsGhc ] }+ , _conf_roundtrip_exactprint_only = wrapLast $ falseToNothing roundtripOnly+ , _conf_disable_formatting = wrapLast $ falseToNothing disableFormatting+ , _conf_obfuscate = wrapLast $ falseToNothing obfuscate+ }+ where+ falseToNothing = Bool.bool Nothing (Just True)+ wrapLast :: Maybe a -> Maybe (Semigroup.Last a)+ wrapLast = fmap Semigroup.Last+ optionConcat :: (Semigroup.Semigroup (f a), Applicative f) => [a] -> Maybe (f a)+ optionConcat = mconcat . fmap (pure . pure)++-- configParser :: Parser Config+-- configParser = Config+-- <$> option (eitherReader $ maybe (Left "required <int>!") Right . readMaybe)+-- (long "indent" <> value 2 <> metavar "AMOUNT" <> help "spaces per indentation level")+-- <*> (Bar+-- <$> switch (long "bara" <> help "bara help")+-- <*> switch (long "barb")+-- <*> flag 3 5 (long "barc")+-- )+--+-- configParserInfo :: ParserInfo Config+-- configParserInfo = ParserInfo+-- { infoParser = configParser+-- , infoFullDesc = True+-- , infoProgDesc = return $ PP.text "a haskell code formatting utility based on ghc-exactprint"+-- , infoHeader = return $ PP.text "brittany"+-- , infoFooter = empty+-- , infoFailureCode = (-55)+-- , infoIntersperse = True+-- }+++-- | 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 Maybe))+readConfig path = do+ -- TODO: probably should catch IOErrors and then omit the existence check.+ exists <- liftIO $ System.Directory.doesFileExist path+ 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 (Data.Yaml.prettyPrintParseException e)+ mzero+ Right x -> return x+ return $ Just fileConf+ else return $ Nothing++-- | Looks for a user-global config file and return its path.+-- If there is no global config in a system, one will be created.+userConfigPath :: IO System.IO.FilePath+userConfigPath = do+ userBritPathSimple <- Directory.getAppUserDataDirectory "brittany"+ userBritPathXdg <- Directory.getXdgDirectory Directory.XdgConfig "brittany"+ let searchDirs = [userBritPathSimple, userBritPathXdg]+ globalConfig <- Directory.findFileWith+ Directory.doesFileExist+ searchDirs+ "config.yaml"+ maybe (writeUserConfig userBritPathXdg) pure globalConfig+ where+ writeUserConfig dir = do+ let createConfPath = dir FilePath.</> "config.yaml"+ liftIO $ Directory.createDirectoryIfMissing True dir+ writeDefaultConfig $ createConfPath+ pure createConfPath++-- | Searches for a local (per-project) brittany config starting from a given directory+findLocalConfigPath :: System.IO.FilePath -> IO (Maybe System.IO.FilePath)+findLocalConfigPath dir = do+ let dirParts = FilePath.splitDirectories dir+ -- when provided dir is "a/b/c", searchDirs is ["a/b/c", "a/b", "a", "/"]+ let searchDirs = FilePath.joinPath <$> reverse (List.inits dirParts)+ Directory.findFileWith Directory.doesFileExist searchDirs "brittany.yaml"++-- | Reads specified configs.+readConfigs+ :: CConfig Maybe -- ^ Explicit options, take highest priority+ -> [System.IO.FilePath] -- ^ List of config files to load and merge, highest priority first+ -> MaybeT IO Config+readConfigs cmdlineConfig configPaths = do+ configs <- readConfig `mapM` configPaths+ let+ merged =+ Semigroup.sconcat $ NonEmpty.reverse (cmdlineConfig :| catMaybes configs)+ return $ cZipWith fromOptionIdentity staticDefaultConfig merged++-- | Reads provided configs+-- but also applies the user default configuration (with lowest priority)+readConfigsWithUserConfig+ :: CConfig Maybe -- ^ Explicit options, take highest priority+ -> [System.IO.FilePath] -- ^ List of config files to load and merge, highest priority first+ -> MaybeT IO Config+readConfigsWithUserConfig cmdlineConfig configPaths = do+ defaultPath <- liftIO $ userConfigPath+ readConfigs cmdlineConfig (configPaths ++ [defaultPath])++writeDefaultConfig :: MonadIO m => System.IO.FilePath -> m ()+writeDefaultConfig path =+ liftIO $ ByteString.writeFile path $ Data.Yaml.encode $ cMap+ (Just . runIdentity)+ staticDefaultConfig++showConfigYaml :: Config -> String+showConfigYaml = Data.ByteString.Char8.unpack . Data.Yaml.encode . cMap+ (\(Identity x) -> Just x)
+ source/library/Brittany/Internal/Config/Types.hs view
@@ -0,0 +1,338 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-}++module Brittany.Internal.Config.Types where++import Data.CZipWith+import Data.Coerce (Coercible, coerce)+import Data.Data (Data)+import qualified Data.Semigroup as Semigroup+import Data.Semigroup (Last)+import Data.Semigroup.Generic+import GHC.Generics+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils ()++++confUnpack :: Coercible a b => Identity a -> b+confUnpack (Identity x) = coerce x++data CDebugConfig f = DebugConfig+ { _dconf_dump_config :: f (Semigroup.Last Bool)+ , _dconf_dump_annotations :: f (Semigroup.Last Bool)+ , _dconf_dump_ast_unknown :: f (Semigroup.Last Bool)+ , _dconf_dump_ast_full :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_raw :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_simpl_alt :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_simpl_floating :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_simpl_par :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_simpl_columns :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_simpl_indent :: f (Semigroup.Last Bool)+ , _dconf_dump_bridoc_final :: f (Semigroup.Last Bool)+ , _dconf_roundtrip_exactprint_only :: f (Semigroup.Last Bool)+ }+ deriving Generic++data CLayoutConfig f = LayoutConfig+ { _lconfig_cols :: f (Last Int) -- the thing that has default 80.+ , _lconfig_indentPolicy :: f (Last IndentPolicy)+ , _lconfig_indentAmount :: f (Last Int)+ , _lconfig_indentWhereSpecial :: f (Last Bool) -- indent where only 1 sometimes (TODO).+ , _lconfig_indentListSpecial :: f (Last Bool) -- use some special indentation for ","+ -- when creating zero-indentation+ -- multi-line list literals.+ , _lconfig_importColumn :: f (Last Int)+ -- ^ for import statement layouting, column at which to align the+ -- elements to be imported from a module.+ -- It is expected that importAsColumn >= importCol.+ , _lconfig_importAsColumn :: f (Last Int)+ -- ^ for import statement layouting, column at which put the module's+ -- "as" name (which also affects the positioning of the "as" keyword).+ -- It is expected that importAsColumn >= importCol.+ , _lconfig_altChooser :: f (Last AltChooser)+ , _lconfig_columnAlignMode :: f (Last ColumnAlignMode)+ , _lconfig_alignmentLimit :: f (Last Int)+ -- roughly speaking, this sets an upper bound to the number of spaces+ -- inserted to create horizontal alignment.+ -- More specifically, if 'xs' are the widths of the columns in some+ -- alignment-block, then the block will be aligned with the width+ -- maximum [ x | x <- xs, x < minimum xs + alignmentLimit ].+ , _lconfig_alignmentBreakOnMultiline :: f (Last Bool)+ -- stops alignment between items that are not layouted as a single line.+ -- e.g. for single-line alignment, things remain unchanged:+ -- do+ -- short <- stuff+ -- loooooooong <- stuff+ -- but not in cases such as:+ -- do+ -- short <- some more stuff+ -- that requires two lines+ -- loooooooong <- stuff+ , _lconfig_hangingTypeSignature :: f (Last Bool)+ -- Do not put "::" in a new line, and use hanging indentation for the+ -- signature, i.e.:+ -- func :: SomeLongStuff+ -- -> SomeLongStuff+ -- instead of the usual+ -- func+ -- :: SomeLongStuff+ -- -> SomeLongStuff+ -- As usual for hanging indentation, the result will be+ -- context-sensitive (in the function name).+ , _lconfig_reformatModulePreamble :: f (Last Bool)+ -- whether the module preamble/header (module keyword, name, export list,+ -- import statements) are reformatted. If false, only the elements of the+ -- module (everything past the "where") are reformatted.+ , _lconfig_allowSingleLineExportList :: f (Last Bool)+ -- if true, and it fits in a single line, and there are no comments in the+ -- export list, the following layout will be used:+ -- > module MyModule (abc, def) where+ -- > [stuff]+ -- otherwise, the multi-line version is used:+ -- > module MyModule+ -- > ( abc+ -- > , def+ -- > )+ -- > where+ , _lconfig_allowHangingQuasiQuotes :: f (Last Bool)+ -- if false, the layouter sees any splices as infinitely big and places+ -- them accordingly (in newlines, most likely); This also influences+ -- parent nodes.+ -- if true, the layouter is free to start a quasi-quotation at the end+ -- of a line.+ --+ -- false:+ -- > let+ -- > body =+ -- > [json|+ -- > hello+ -- > |]+ --+ -- true:+ -- > let body = [json|+ -- > hello+ -- > |]+ , _lconfig_experimentalSemicolonNewlines :: f (Last Bool)+ -- enables an experimental feature to turn semicolons in brace notation+ -- into newlines when using layout:+ --+ -- > do { a ;; b }+ --+ -- turns into+ -- > do+ -- > a+ -- >+ -- > b+ --+ -- The implementation for this is a bit hacky and not tested; it might+ -- break output syntax or not work properly for every kind of brace. So+ -- far I have considered `do` and `case-of`.+ -- , _lconfig_allowSinglelineRecord :: f (Last Bool)+ -- -- if true, layouts record data decls as a single line when possible, e.g.+ -- -- > MyPoint { x :: Double, y :: Double }+ -- -- if false, always use the multi-line layout+ -- -- > MyPoint+ -- -- > { x :: Double+ -- -- > , y :: Double+ -- -- > }+ }+ deriving Generic++data CForwardOptions f = ForwardOptions+ { _options_ghc :: f [String]+ }+ deriving Generic++data CErrorHandlingConfig f = ErrorHandlingConfig+ { _econf_produceOutputOnErrors :: f (Semigroup.Last Bool)+ , _econf_Werror :: f (Semigroup.Last Bool)+ , _econf_ExactPrintFallback :: f (Semigroup.Last ExactPrintFallbackMode)+ -- ^ Determines when to fall back on the exactprint'ed output when+ -- syntactical constructs are encountered which are not yet handled by+ -- brittany.+ -- Note that the "risky" setting is risky because even with the check of+ -- the syntactic validity of the brittany output, at least in theory there+ -- may be cases where the output is syntactically/semantically valid but+ -- has different semantics than the code pre-transformation.+ , _econf_omit_output_valid_check :: f (Semigroup.Last Bool)+ }+ deriving Generic++data CPreProcessorConfig f = PreProcessorConfig+ { _ppconf_CPPMode :: f (Semigroup.Last CPPMode)+ , _ppconf_hackAroundIncludes :: f (Semigroup.Last Bool)+ }+ deriving Generic++data CConfig f = Config+ { _conf_version :: f (Semigroup.Last Int)+ , _conf_debug :: CDebugConfig f+ , _conf_layout :: CLayoutConfig f+ , _conf_errorHandling :: CErrorHandlingConfig f+ , _conf_forward :: CForwardOptions f+ , _conf_preprocessor :: CPreProcessorConfig f+ , _conf_roundtrip_exactprint_only :: f (Semigroup.Last Bool)+ -- ^ this field is somewhat of a duplicate of the one in DebugConfig.+ -- It is used for per-declaration disabling by the inline config+ -- implementation. Could have re-used the existing field, but felt risky+ -- to use a "debug" labeled field for non-debug functionality.+ , _conf_disable_formatting :: f (Semigroup.Last Bool)+ -- ^ Used for inline config that disables brittany entirely for this+ -- module. Useful for wildcard application+ -- (`find -name "*.hs" | xargs brittany --write-mode inplace` or something+ -- in that direction).+ , _conf_obfuscate :: f (Semigroup.Last Bool)+ }+ deriving Generic++type DebugConfig = CDebugConfig Identity+type LayoutConfig = CLayoutConfig Identity+type ForwardOptions = CForwardOptions Identity+type ErrorHandlingConfig = CErrorHandlingConfig Identity+type Config = CConfig Identity++-- i wonder if any Show1 stuff could be leveraged.+deriving instance Show (CDebugConfig Identity)+deriving instance Show (CLayoutConfig Identity)+deriving instance Show (CErrorHandlingConfig Identity)+deriving instance Show (CForwardOptions Identity)+deriving instance Show (CPreProcessorConfig Identity)+deriving instance Show (CConfig Identity)++deriving instance Show (CDebugConfig Maybe)+deriving instance Show (CLayoutConfig Maybe)+deriving instance Show (CErrorHandlingConfig Maybe)+deriving instance Show (CForwardOptions Maybe)+deriving instance Show (CPreProcessorConfig Maybe)+deriving instance Show (CConfig Maybe)++deriving instance Data (CDebugConfig Identity)+deriving instance Data (CLayoutConfig Identity)+deriving instance Data (CErrorHandlingConfig Identity)+deriving instance Data (CForwardOptions Identity)+deriving instance Data (CPreProcessorConfig Identity)+deriving instance Data (CConfig Identity)++deriving instance Data (CDebugConfig Maybe)+deriving instance Data (CLayoutConfig Maybe)+deriving instance Data (CErrorHandlingConfig Maybe)+deriving instance Data (CForwardOptions Maybe)+deriving instance Data (CPreProcessorConfig Maybe)+deriving instance Data (CConfig Maybe)++instance Semigroup.Semigroup (CDebugConfig Maybe) where+ (<>) = gmappend+instance Semigroup.Semigroup (CLayoutConfig Maybe) where+ (<>) = gmappend+instance Semigroup.Semigroup (CErrorHandlingConfig Maybe) where+ (<>) = gmappend+instance Semigroup.Semigroup (CForwardOptions Maybe) where+ (<>) = gmappend+instance Semigroup.Semigroup (CPreProcessorConfig Maybe) where+ (<>) = gmappend+instance Semigroup.Semigroup (CConfig Maybe) where+ (<>) = gmappend++instance Semigroup.Semigroup (CDebugConfig Identity) where+ (<>) = gmappend+instance Semigroup.Semigroup (CLayoutConfig Identity) where+ (<>) = gmappend+instance Semigroup.Semigroup (CErrorHandlingConfig Identity) where+ (<>) = gmappend+instance Semigroup.Semigroup (CForwardOptions Identity) where+ (<>) = gmappend+instance Semigroup.Semigroup (CPreProcessorConfig Identity) where+ (<>) = gmappend+instance Semigroup.Semigroup (CConfig Identity) where+ (<>) = gmappend++instance Monoid (CDebugConfig Maybe) where+ mempty = gmempty+instance Monoid (CLayoutConfig Maybe) where+ mempty = gmempty+instance Monoid (CErrorHandlingConfig Maybe) where+ mempty = gmempty+instance Monoid (CForwardOptions Maybe) where+ mempty = gmempty+instance Monoid (CPreProcessorConfig Maybe) where+ mempty = gmempty+instance Monoid (CConfig Maybe) where+ mempty = gmempty+++data IndentPolicy = IndentPolicyLeft -- never create a new indentation at more+ -- than old indentation + amount+ | IndentPolicyFree -- can create new indentations whereever+ | IndentPolicyMultiple -- can create indentations only+ -- at any n * amount.+ deriving (Eq, Show, Generic, Data)++data AltChooser = AltChooserSimpleQuick -- always choose last alternative.+ -- leads to tons of sparsely filled+ -- lines.+ | AltChooserShallowBest -- choose the first matching alternative+ -- using the simplest spacing+ -- information for the children.+ | AltChooserBoundedSearch Int+ -- choose the first matching alternative+ -- using a bounded list of recursive+ -- options having sufficient space.+ deriving (Show, Generic, Data)++data ColumnAlignMode+ = ColumnAlignModeDisabled+ -- ^ Make no column alignments whatsoever+ | ColumnAlignModeUnanimously+ -- ^ Make column alignments only if it does not cause overflow for any of+ -- the affected lines.+ | ColumnAlignModeMajority Float+ -- ^ If at least (ratio::Float) of the aligned elements have sufficient+ -- space for the alignment, act like ColumnAlignModeAnimously; otherwise+ -- act like ColumnAlignModeDisabled.+ | ColumnAlignModeAnimouslyScale Int+ -- ^ Scale back columns to some degree if their sum leads to overflow.+ -- This is done in a linear fashion.+ -- The Int specifies additional columns to be added to column maximum for+ -- scaling calculation purposes.+ | ColumnAlignModeAnimously+ -- ^ Decide on a case-by-case basis if alignment would cause overflow.+ -- If it does, cancel all alignments for this (nested) column description.+ -- ColumnAlignModeAnimouslySome -- potentially to implement+ | ColumnAlignModeAlways+ -- ^ Always respect column alignments, even if it makes stuff overflow.+ deriving (Show, Generic, Data)++data CPPMode = CPPModeAbort -- abort program on seeing -XCPP+ | CPPModeWarn -- warn about CPP and non-roundtripping in its+ -- presence.+ | CPPModeNowarn -- silently allow CPP, if possible (i.e. input is+ -- file.)+ deriving (Show, Generic, Data)++data ExactPrintFallbackMode+ = ExactPrintFallbackModeNever -- never fall back on exactprinting+ | ExactPrintFallbackModeInline -- fall back only if there are no newlines in+ -- the exactprint'ed output.+ | ExactPrintFallbackModeRisky -- fall back even in the presence of newlines.+ -- THIS MAY THEORETICALLY CHANGE SEMANTICS OF+ -- A PROGRAM BY TRANSFORMING IT.+ deriving (Show, Generic, Data)++deriveCZipWith ''CDebugConfig+deriveCZipWith ''CLayoutConfig+deriveCZipWith ''CErrorHandlingConfig+deriveCZipWith ''CForwardOptions+deriveCZipWith ''CPreProcessorConfig+deriveCZipWith ''CConfig++instance CFunctor CDebugConfig+instance CFunctor CLayoutConfig+instance CFunctor CErrorHandlingConfig+instance CFunctor CForwardOptions+instance CFunctor CPreProcessorConfig+instance CFunctor CConfig
+ source/library/Brittany/Internal/Config/Types/Instances.hs view
@@ -0,0 +1,131 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- These optimizations are disabled to improve compile times (and compilation+-- memory usage). When we do not disable them, the CI servers take more than+-- 10 minutes to compile this module alone.+-- Having looked into aeson and how the instances are written, I still do+-- not understand what makes GHC choke so much here. The size of the raw+-- expressions below looks fairly negligible, so there must be some expansion+-- due to inlining going on. But even disabling INLINE pragmas in aeson did+-- not seem to change anything.+-- Nonetheless, this solution works and has no downsides because the+-- instances defined here are not in any way performance-critical.+{-# OPTIONS_GHC -fno-pre-inlining #-}+{-# OPTIONS_GHC -fno-specialise #-}+{-# OPTIONS_GHC -fignore-interface-pragmas #-}++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Config.Types.Instances where++import qualified Data.Aeson.Key as Key+import qualified Data.Aeson.Types as Aeson+import Data.Yaml+import Brittany.Internal.Config.Types+import Brittany.Internal.Prelude++++aesonDecodeOptionsBrittany :: Aeson.Options+aesonDecodeOptionsBrittany = Aeson.defaultOptions+ { Aeson.omitNothingFields = True+ , Aeson.fieldLabelModifier = dropWhile (== '_')+ }++instance FromJSON (CDebugConfig Maybe) where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON (CDebugConfig Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++instance FromJSON IndentPolicy where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON IndentPolicy where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany+ toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany++instance FromJSON AltChooser where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON AltChooser where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany+ toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany++instance FromJSON ColumnAlignMode where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON ColumnAlignMode where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany+ toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany++instance FromJSON CPPMode where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON CPPMode where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany+ toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany++instance FromJSON ExactPrintFallbackMode where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON ExactPrintFallbackMode where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany+ toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany++instance FromJSON (CLayoutConfig Maybe) where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON (CLayoutConfig Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++instance FromJSON (CErrorHandlingConfig Maybe) where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON (CErrorHandlingConfig Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++instance FromJSON (CForwardOptions Maybe) where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON (CForwardOptions Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++instance FromJSON (CPreProcessorConfig Maybe) where+ parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany++instance ToJSON (CPreProcessorConfig Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++instance ToJSON (CConfig Maybe) where+ toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany++-- This custom instance ensures the "omitNothingFields" behaviour not only for+-- leafs, but for nodes of the config as well. This way e.g. "{}" is valid+-- config file content.+instance FromJSON (CConfig Maybe) where+ parseJSON (Object v) =+ Config+ <$> v+ .:? Key.fromString "conf_version"+ <*> v+ .:?= Key.fromString "conf_debug"+ <*> v+ .:?= Key.fromString "conf_layout"+ <*> v+ .:?= Key.fromString "conf_errorHandling"+ <*> v+ .:?= Key.fromString "conf_forward"+ <*> v+ .:?= Key.fromString "conf_preprocessor"+ <*> v+ .:? Key.fromString "conf_roundtrip_exactprint_only"+ <*> v+ .:? Key.fromString "conf_disable_formatting"+ <*> v+ .:? Key.fromString "conf_obfuscate"+ parseJSON invalid = Aeson.typeMismatch "Config" invalid++-- Pretends that the value is {} when the key is not present.+(.:?=) :: FromJSON a => Object -> Key.Key -> Parser a+o .:?= k = o .:? k >>= maybe (parseJSON (Aeson.object [])) pure
+ source/library/Brittany/Internal/ExactPrintUtils.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal.ExactPrintUtils where++import qualified Control.Monad.State.Class as State.Class+import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS+import Data.Data+import qualified Data.Foldable as Foldable+import qualified Data.Generics as SYB+import Data.HList.HList+import qualified Data.Map as Map+import qualified Data.Maybe+import qualified Data.Sequence as Seq+import qualified Data.Set as Set+import GHC (GenLocated(L))+import qualified GHC hiding (parseModule)+import qualified GHC.Driver.CmdLine as GHC+import GHC.Hs+import qualified GHC.Types.SrcLoc as GHC+import GHC.Types.SrcLoc (Located, SrcSpan)+import Brittany.Internal.Config.Types+import qualified Brittany.Internal.ParseModule as ParseModule+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint+import qualified System.IO++++parseModule+ :: [String]+ -> System.IO.FilePath+ -> (GHC.DynFlags -> IO (Either String a))+ -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))+parseModule args fp dynCheck = do+ str <- System.IO.readFile fp+ parseModuleFromString args fp dynCheck str++parseModuleFromString+ :: [String]+ -> System.IO.FilePath+ -> (GHC.DynFlags -> IO (Either String a))+ -> String+ -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))+parseModuleFromString = ParseModule.parseModule+++commentAnnFixTransformGlob :: SYB.Data ast => ast -> ExactPrint.Transform ()+commentAnnFixTransformGlob ast = do+ let+ extract :: forall a . SYB.Data a => a -> Seq (SrcSpan, ExactPrint.AnnKey)+ extract = -- traceFunctionWith "extract" (show . SYB.typeOf) show $+ const Seq.empty+ `SYB.ext1Q` (\l@(L span _) ->+ Seq.singleton (span, ExactPrint.mkAnnKey l)+ )+ let nodes = SYB.everything (<>) extract ast+ let+ annsMap :: Map GHC.RealSrcLoc ExactPrint.AnnKey+ annsMap = Map.fromListWith+ (const id)+ [ (GHC.realSrcSpanEnd span, annKey)+ | (GHC.RealSrcSpan span _, annKey) <- Foldable.toList nodes+ ]+ nodes `forM_` (snd .> processComs annsMap)+ where+ processComs annsMap annKey1 = do+ mAnn <- State.Class.gets fst <&> Map.lookup annKey1+ mAnn `forM_` \ann1 -> do+ let+ priors = ExactPrint.annPriorComments ann1+ follows = ExactPrint.annFollowingComments ann1+ assocs = ExactPrint.annsDP ann1+ let+ processCom+ :: (ExactPrint.Comment, ExactPrint.DeltaPos)+ -> ExactPrint.TransformT Identity Bool+ processCom comPair@(com, _) =+ case GHC.realSrcSpanStart $ ExactPrint.commentIdentifier com of+ comLoc -> case Map.lookupLE comLoc annsMap of+ Just (_, annKey2) | loc1 /= loc2 -> case (con1, con2) of+ (ExactPrint.CN "RecordCon", ExactPrint.CN "HsRecField") ->+ move $> False+ (x, y) | x == y -> move $> False+ _ -> return True+ where+ ExactPrint.AnnKey annKeyLoc1 con1 = annKey1+ ExactPrint.AnnKey annKeyLoc2 con2 = annKey2+ loc1 = GHC.realSrcSpanStart annKeyLoc1+ loc2 = GHC.realSrcSpanStart annKeyLoc2+ move = ExactPrint.modifyAnnsT $ \anns ->+ let+ ann2 = Data.Maybe.fromJust $ Map.lookup annKey2 anns+ ann2' = ann2+ { ExactPrint.annFollowingComments =+ ExactPrint.annFollowingComments ann2 ++ [comPair]+ }+ in Map.insert annKey2 ann2' anns+ _ -> return True -- retain comment at current node.+ priors' <- filterM processCom priors+ follows' <- filterM processCom follows+ assocs' <- flip filterM assocs $ \case+ (ExactPrint.AnnComment com, dp) -> processCom (com, dp)+ _ -> return True+ let+ ann1' = ann1+ { ExactPrint.annPriorComments = priors'+ , ExactPrint.annFollowingComments = follows'+ , ExactPrint.annsDP = assocs'+ }+ ExactPrint.modifyAnnsT $ \anns -> Map.insert annKey1 ann1' anns+++-- TODO: this is unused by now, but it contains one detail that+-- commentAnnFixTransformGlob does not include: Moving of comments for+-- "RecordUpd"s.+-- commentAnnFixTransform :: GHC.ParsedSource -> ExactPrint.Transform ()+-- commentAnnFixTransform modul = SYB.everything (>>) genF modul+-- where+-- genF :: Data.Data.Data a => a -> ExactPrint.Transform ()+-- genF = (\_ -> return ()) `SYB.extQ` exprF+-- exprF :: Located (HsExpr GhcPs) -> ExactPrint.Transform ()+-- exprF lexpr@(L _ expr) = case expr of+-- #if MIN_VERSION_ghc(8,6,0) /* ghc-8.6 */+-- RecordCon _ _ (HsRecFields fs@(_:_) Nothing) ->+-- #else+-- RecordCon _ _ _ (HsRecFields fs@(_:_) Nothing) ->+-- #endif+-- moveTrailingComments lexpr (List.last fs)+-- #if MIN_VERSION_ghc(8,6,0) /* ghc-8.6 */+-- RecordUpd _ _e fs@(_:_) ->+-- #else+-- RecordUpd _e fs@(_:_) _cons _ _ _ ->+-- #endif+-- moveTrailingComments lexpr (List.last fs)+-- _ -> return ()++-- commentAnnFixTransform :: GHC.ParsedSource -> ExactPrint.Transform ()+-- commentAnnFixTransform modul = SYB.everything (>>) genF modul+-- where+-- genF :: Data.Data.Data a => a -> ExactPrint.Transform ()+-- genF = (\_ -> return ()) `SYB.extQ` exprF+-- exprF :: Located (HsExpr GhcPs) -> ExactPrint.Transform ()+-- exprF lexpr@(L _ expr) = case expr of+-- RecordCon _ _ (HsRecFields fs@(_:_) Nothing) ->+-- moveTrailingComments lexpr (List.last fs)+-- RecordUpd _ _e fs@(_:_) ->+-- moveTrailingComments lexpr (List.last fs)+-- _ -> return ()++-- moveTrailingComments :: (Data.Data.Data a,Data.Data.Data b)+-- => GHC.Located a -> GHC.Located b -> ExactPrint.Transform ()+-- moveTrailingComments astFrom astTo = do+-- let+-- k1 = ExactPrint.mkAnnKey astFrom+-- k2 = ExactPrint.mkAnnKey astTo+-- moveComments ans = ans'+-- where+-- an1 = Data.Maybe.fromJust $ Map.lookup k1 ans+-- an2 = Data.Maybe.fromJust $ Map.lookup k2 ans+-- cs1f = ExactPrint.annFollowingComments an1+-- cs2f = ExactPrint.annFollowingComments an2+-- (comments, nonComments) = flip breakEither (ExactPrint.annsDP an1)+-- $ \case+-- (ExactPrint.AnnComment com, dp) -> Left (com, dp)+-- x -> Right x+-- an1' = an1+-- { ExactPrint.annsDP = nonComments+-- , ExactPrint.annFollowingComments = []+-- }+-- an2' = an2+-- { ExactPrint.annFollowingComments = cs1f ++ cs2f ++ comments+-- }+-- ans' = Map.insert k1 an1' $ Map.insert k2 an2' ans++-- ExactPrint.modifyAnnsT moveComments++-- | split a set of annotations in a module into a map from top-level module+-- elements to the relevant annotations. Avoids quadratic behaviour a trivial+-- implementation would have.+extractToplevelAnns+ :: Located HsModule+ -> ExactPrint.Anns+ -> Map ExactPrint.AnnKey ExactPrint.Anns+extractToplevelAnns lmod anns = output+ where+ (L _ (HsModule _ _ _ _ ldecls _ _)) = lmod+ declMap1 :: Map ExactPrint.AnnKey ExactPrint.AnnKey+ declMap1 = Map.unions $ ldecls <&> \ldecl ->+ Map.fromSet (const (ExactPrint.mkAnnKey ldecl)) (foldedAnnKeys ldecl)+ declMap2 :: Map ExactPrint.AnnKey ExactPrint.AnnKey+ declMap2 =+ Map.fromList+ $ [ (captured, declMap1 Map.! k)+ | (k, ExactPrint.Ann _ _ _ _ _ (Just captured)) <- Map.toList anns+ ]+ declMap = declMap1 `Map.union` declMap2+ modKey = ExactPrint.mkAnnKey lmod+ output = groupMap (\k _ -> Map.findWithDefault modKey k declMap) anns++groupMap :: (Ord k, Ord l) => (k -> a -> l) -> Map k a -> Map l (Map k a)+groupMap f = Map.foldlWithKey'+ (\m k a -> Map.alter (insert k a) (f k a) m)+ Map.empty+ where+ insert k a Nothing = Just (Map.singleton k a)+ insert k a (Just m) = Just (Map.insert k a m)++foldedAnnKeys :: Data.Data.Data ast => ast -> Set ExactPrint.AnnKey+foldedAnnKeys ast = SYB.everything+ Set.union+ (\x -> maybe+ Set.empty+ Set.singleton+ [ SYB.gmapQi 1 (ExactPrint.mkAnnKey . L l) x+ | locTyCon == SYB.typeRepTyCon (SYB.typeOf x)+ , l :: SrcSpan <- SYB.gmapQi 0 SYB.cast x+ ]+ -- for some reason, ghc-8.8 has forgotten how to infer the type of l,+ -- even though it is passed to mkAnnKey above, which only accepts+ -- SrcSpan.+ )+ ast+ where locTyCon = SYB.typeRepTyCon (SYB.typeOf (L () ()))+++withTransformedAnns+ :: Data ast+ => ast+ -> MultiRWSS.MultiRWS '[Config , ExactPrint.Anns] w s a+ -> MultiRWSS.MultiRWS '[Config , ExactPrint.Anns] w s a+withTransformedAnns ast m = MultiRWSS.mGetRawR >>= \case+ readers@(conf :+: anns :+: HNil) -> do+ -- TODO: implement `local` for MultiReader/MultiRWS+ MultiRWSS.mPutRawR (conf :+: f anns :+: HNil)+ x <- m+ MultiRWSS.mPutRawR readers+ pure x+ where+ f anns =+ let+ ((), (annsBalanced, _), _) =+ ExactPrint.runTransform anns (commentAnnFixTransformGlob ast)+ in annsBalanced+++warnExtractorCompat :: GHC.Warn -> String+warnExtractorCompat (GHC.Warn _ (L _ s)) = s
+ source/library/Brittany/Internal/LayouterBasics.hs view
@@ -0,0 +1,781 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.LayouterBasics where++import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS+import qualified Control.Monad.Writer.Strict as Writer+import qualified Data.Char as Char+import Data.Data+import qualified Data.Map as Map+import qualified Data.Semigroup as Semigroup+import qualified Data.Sequence as Seq+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified Data.Text.Lazy.Builder as Text.Builder+import DataTreePrint+import GHC (GenLocated(L), Located, moduleName, moduleNameString)+import qualified GHC.OldList as List+import GHC.Parser.Annotation (AnnKeywordId(..))+import GHC.Types.Name (getOccString)+import GHC.Types.Name.Occurrence (occNameString)+import GHC.Types.Name.Reader (RdrName(..))+import qualified GHC.Types.SrcLoc as GHC+import Brittany.Internal.Config.Types+import Brittany.Internal.ExactPrintUtils+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import qualified Language.Haskell.GHC.ExactPrint.Annotate as ExactPrint.Annotate+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint+import Language.Haskell.GHC.ExactPrint.Types (AnnKey, Annotation)+import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils++++processDefault+ :: ( ExactPrint.Annotate.Annotate ast+ , MonadMultiWriter Text.Builder.Builder m+ , MonadMultiReader ExactPrint.Types.Anns m+ )+ => Located ast+ -> m ()+processDefault x = do+ anns <- mAsk+ let str = ExactPrint.exactPrint x anns+ -- this hack is here so our print-empty-module trick does not add+ -- a newline at the start if there actually is no module header / imports+ -- / anything.+ -- TODO: instead the appropriate annotation could be removed when "cleaning"+ -- the module (header). This would remove the need for this hack!+ case str of+ "\n" -> return ()+ _ -> mTell $ Text.Builder.fromString str++-- | Use ExactPrint's output for this node; add a newly generated inline comment+-- at insertion position (meant to point out to the user that this node is+-- not handled by brittany yet). Useful when starting implementing new+-- syntactic constructs when children are not handled yet.+briDocByExact+ :: (ExactPrint.Annotate.Annotate ast)+ => Located ast+ -> ToBriDocM BriDocNumbered+briDocByExact ast = do+ anns <- mAsk+ traceIfDumpConf+ "ast"+ _dconf_dump_ast_unknown+ (printTreeWithCustom 100 (customLayouterF anns) ast)+ docExt ast anns True++-- | Use ExactPrint's output for this node.+-- Consider that for multi-line input, the indentation of the code produced+-- by ExactPrint might be different, and even incompatible with the indentation+-- of its surroundings as layouted by brittany. But there are safe uses of+-- this, e.g. for any top-level declarations.+briDocByExactNoComment+ :: (ExactPrint.Annotate.Annotate ast)+ => Located ast+ -> ToBriDocM BriDocNumbered+briDocByExactNoComment ast = do+ anns <- mAsk+ traceIfDumpConf+ "ast"+ _dconf_dump_ast_unknown+ (printTreeWithCustom 100 (customLayouterF anns) ast)+ docExt ast anns False++-- | Use ExactPrint's output for this node, presuming that this output does+-- not contain any newlines. If this property is not met, the semantics+-- depend on the @econf_AllowRiskyExactPrintUse@ config flag.+briDocByExactInlineOnly+ :: (ExactPrint.Annotate.Annotate ast)+ => String+ -> Located ast+ -> ToBriDocM BriDocNumbered+briDocByExactInlineOnly infoStr ast = do+ anns <- mAsk+ traceIfDumpConf+ "ast"+ _dconf_dump_ast_unknown+ (printTreeWithCustom 100 (customLayouterF anns) ast)+ let exactPrinted = Text.pack $ ExactPrint.exactPrint ast anns+ fallbackMode <-+ mAsk <&> _conf_errorHandling .> _econf_ExactPrintFallback .> confUnpack+ let+ exactPrintNode t = allocateNode $ BDFExternal+ (ExactPrint.Types.mkAnnKey ast)+ (foldedAnnKeys ast)+ False+ t+ let+ errorAction = do+ mTell [ErrorUnknownNode infoStr ast]+ docLit $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"+ case (fallbackMode, Text.lines exactPrinted) of+ (ExactPrintFallbackModeNever, _) -> 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+rdrNameToText (Unqual occname) = Text.pack $ occNameString occname+rdrNameToText (Qual mname occname) =+ Text.pack $ moduleNameString mname ++ "." ++ occNameString occname+rdrNameToText (Orig modul occname) =+ Text.pack $ moduleNameString (moduleName modul) ++ occNameString occname+rdrNameToText (Exact name) = Text.pack $ getOccString name++lrdrNameToText :: GenLocated l RdrName -> Text+lrdrNameToText (L _ n) = rdrNameToText n++lrdrNameToTextAnnGen+ :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)+ => (Text -> Text)+ -> Located RdrName+ -> m Text+lrdrNameToTextAnnGen f ast@(L _ n) = do+ anns <- mAsk+ let t = f $ rdrNameToText n+ let+ hasUni x (ExactPrint.Types.G y, _) = x == y+ hasUni _ _ = False+ -- TODO: in general: we should _always_ process all annotaiton stuff here.+ -- whatever we don't probably should have had some effect on the+ -- output. in such cases, resorting to byExact is probably the safe+ -- choice.+ return $ case Map.lookup (ExactPrint.Types.mkAnnKey ast) anns of+ Nothing -> t+ Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> case n of+ Exact{} | t == Text.pack "()" -> t+ _ | any (hasUni AnnBackquote) aks -> Text.pack "`" <> t <> Text.pack "`"+ _ | any (hasUni AnnCommaTuple) aks -> t+ _ | any (hasUni AnnOpenP) aks -> Text.pack "(" <> t <> Text.pack ")"+ _ | otherwise -> t++lrdrNameToTextAnn+ :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)+ => Located RdrName+ -> m Text+lrdrNameToTextAnn = lrdrNameToTextAnnGen id++lrdrNameToTextAnnTypeEqualityIsSpecial+ :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)+ => Located RdrName+ -> m Text+lrdrNameToTextAnnTypeEqualityIsSpecial ast = do+ let+ f x = if x == Text.pack "Data.Type.Equality~"+ then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh+ else x+ lrdrNameToTextAnnGen f ast++-- | Same as lrdrNameToTextAnnTypeEqualityIsSpecial, but also inspects+-- the annotations for a (parent) node for a tick to be added to the+-- literal.+-- Excessively long name to reflect on us having to work around such+-- excessively obscure special cases in the exactprint API.+lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick+ :: ( Data ast+ , MonadMultiReader Config m+ , MonadMultiReader (Map AnnKey Annotation) m+ )+ => Located ast+ -> Located RdrName+ -> m Text+lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick ast1 ast2 = do+ hasQuote <- hasAnnKeyword ast1 AnnSimpleQuote+ x <- lrdrNameToTextAnn ast2+ let+ lit = if x == Text.pack "Data.Type.Equality~"+ then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh+ else x+ return $ if hasQuote then Text.cons '\'' lit else lit++askIndent :: (MonadMultiReader Config m) => m Int+askIndent = confUnpack . _lconfig_indentAmount . _conf_layout <$> mAsk+++extractAllComments+ :: ExactPrint.Annotation -> [(ExactPrint.Comment, ExactPrint.DeltaPos)]+extractAllComments ann =+ ExactPrint.annPriorComments ann ++ extractRestComments ann++extractRestComments+ :: ExactPrint.Annotation -> [(ExactPrint.Comment, ExactPrint.DeltaPos)]+extractRestComments ann =+ ExactPrint.annFollowingComments ann+ ++ (ExactPrint.annsDP ann >>= \case+ (ExactPrint.AnnComment com, dp) -> [(com, dp)]+ _ -> []+ )++filterAnns :: Data.Data.Data ast => ast -> ExactPrint.Anns -> ExactPrint.Anns+filterAnns ast = Map.filterWithKey (\k _ -> k `Set.member` foldedAnnKeys ast)++-- | True if there are any comments that are+-- a) connected to any node below (in AST sense) the given node AND+-- b) after (in source code order) the node.+hasAnyCommentsBelow :: Data ast => GHC.Located ast -> ToBriDocM Bool+hasAnyCommentsBelow ast@(L l _) =+ List.any (\(c, _) -> ExactPrint.commentIdentifier c > ExactPrint.Utils.rs l)+ <$> astConnectedComments ast++hasCommentsBetween+ :: Data ast+ => GHC.Located ast+ -> AnnKeywordId+ -> AnnKeywordId+ -> ToBriDocM Bool+hasCommentsBetween ast leftKey rightKey = do+ mAnn <- astAnn ast+ let+ go1 [] = False+ go1 ((ExactPrint.G kw, _dp) : rest) | kw == leftKey = go2 rest+ go1 (_ : rest) = go1 rest+ go2 [] = False+ go2 ((ExactPrint.AnnComment _, _dp) : _rest) = True+ go2 ((ExactPrint.G kw, _dp) : _rest) | kw == rightKey = False+ go2 (_ : rest) = go2 rest+ case mAnn of+ Nothing -> pure False+ Just ann -> pure $ go1 $ ExactPrint.annsDP ann++-- | True if there are any comments that are connected to any node below (in AST+-- sense) the given node+hasAnyCommentsConnected :: Data ast => GHC.Located ast -> ToBriDocM Bool+hasAnyCommentsConnected ast = not . null <$> astConnectedComments ast++-- | True if there are any regular comments connected to any node below (in AST+-- sense) the given node+hasAnyRegularCommentsConnected :: Data ast => GHC.Located ast -> ToBriDocM Bool+hasAnyRegularCommentsConnected ast =+ any isRegularComment <$> astConnectedComments ast++-- | Regular comments are comments that are actually "source code comments",+-- i.e. things that start with "--" or "{-". In contrast to comment-annotations+-- used by ghc-exactprint for capturing symbols (and their exact positioning).+--+-- Only the type instance layouter makes use of this filter currently, but+-- it might make sense to apply it more aggressively or make it the default -+-- I believe that most of the time we branch on the existence of comments, we+-- only care about "regular" comments. We simply did not need the distinction+-- because "irregular" comments are not that common outside of type/data decls.+isRegularComment :: (ExactPrint.Comment, ExactPrint.DeltaPos) -> Bool+isRegularComment = (== Nothing) . ExactPrint.Types.commentOrigin . fst++astConnectedComments+ :: Data ast+ => GHC.Located ast+ -> ToBriDocM [(ExactPrint.Types.Comment, ExactPrint.Types.DeltaPos)]+astConnectedComments ast = do+ anns <- filterAnns ast <$> mAsk+ pure $ extractAllComments =<< Map.elems anns++hasAnyCommentsPrior :: Data ast => GHC.Located ast -> ToBriDocM Bool+hasAnyCommentsPrior ast = astAnn ast <&> \case+ Nothing -> False+ Just (ExactPrint.Types.Ann _ priors _ _ _ _) -> not $ null priors++hasAnyRegularCommentsRest :: Data ast => GHC.Located ast -> ToBriDocM Bool+hasAnyRegularCommentsRest ast = astAnn ast <&> \case+ Nothing -> False+ Just ann -> any isRegularComment (extractRestComments ann)++hasAnnKeywordComment+ :: Data ast => GHC.Located ast -> AnnKeywordId -> ToBriDocM Bool+hasAnnKeywordComment ast annKeyword = astAnn ast <&> \case+ Nothing -> False+ Just ann -> any hasK (extractAllComments ann)+ where hasK = (== Just annKeyword) . ExactPrint.Types.commentOrigin . fst++hasAnnKeyword+ :: (Data a, MonadMultiReader (Map AnnKey Annotation) m)+ => Located a+ -> AnnKeywordId+ -> m Bool+hasAnnKeyword ast annKeyword = astAnn ast <&> \case+ Nothing -> False+ Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> any hasK aks+ where+ hasK (ExactPrint.Types.G x, _) = x == annKeyword+ hasK _ = False++astAnn+ :: (Data ast, MonadMultiReader (Map AnnKey Annotation) m)+ => GHC.Located ast+ -> m (Maybe Annotation)+astAnn ast = Map.lookup (ExactPrint.Types.mkAnnKey ast) <$> mAsk++-- new BriDoc stuff++allocateNode+ :: MonadMultiState NodeAllocIndex m => BriDocFInt -> m BriDocNumbered+allocateNode bd = do+ i <- allocNodeIndex+ return (i, bd)++allocNodeIndex :: MonadMultiState NodeAllocIndex m => m Int+allocNodeIndex = do+ NodeAllocIndex i <- mGet+ mSet $ NodeAllocIndex (i + 1)+ return i++-- docEmpty :: MonadMultiState NodeAllocIndex m => m BriDocNumbered+-- docEmpty = allocateNode BDFEmpty+--+-- docLit :: MonadMultiState NodeAllocIndex m => Text -> m BriDocNumbered+-- docLit t = allocateNode $ BDFLit t+--+-- docExt :: (ExactPrint.Annotate.Annotate ast, MonadMultiState NodeAllocIndex m)+-- => Located ast -> ExactPrint.Types.Anns -> Bool -> m BriDocNumbered+-- docExt x anns shouldAddComment = allocateNode $ BDFExternal+-- (ExactPrint.Types.mkAnnKey x)+-- (foldedAnnKeys x)+-- shouldAddComment+-- (Text.pack $ ExactPrint.exactPrint x anns)+--+-- docAlt :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered+-- docAlt l = allocateNode . BDFAlt =<< sequence l+--+--+-- docSeq :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered+-- docSeq l = allocateNode . BDFSeq =<< sequence l+--+-- docLines :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered+-- docLines l = allocateNode . BDFLines =<< sequence l+--+-- docCols :: MonadMultiState NodeAllocIndex m => ColSig -> [m BriDocNumbered] -> m BriDocNumbered+-- docCols sig l = allocateNode . BDFCols sig =<< sequence l+--+-- docAddBaseY :: MonadMultiState NodeAllocIndex m => BrIndent -> m BriDocNumbered -> m BriDocNumbered+-- docAddBaseY ind bdm = allocateNode . BDFAddBaseY ind =<< bdm+--+-- docSetBaseY :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- docSetBaseY bdm = allocateNode . BDFSetBaseY =<< bdm+--+-- docSetIndentLevel :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- docSetIndentLevel bdm = allocateNode . BDFSetIndentLevel =<< bdm+--+-- docSeparator :: MonadMultiState NodeAllocIndex m => m BriDocNumbered+-- docSeparator = allocateNode BDFSeparator+--+-- docAnnotationPrior :: MonadMultiState NodeAllocIndex m => AnnKey -> m BriDocNumbered -> m BriDocNumbered+-- docAnnotationPrior annKey bdm = allocateNode . BDFAnnotationPrior annKey =<< bdm+--+-- docAnnotationPost :: MonadMultiState NodeAllocIndex m => AnnKey -> m BriDocNumbered -> m BriDocNumbered+-- docAnnotationPost annKey bdm = allocateNode . BDFAnnotationPost annKey =<< bdm+--+-- docNonBottomSpacing :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- docNonBottomSpacing bdm = allocateNode . BDFNonBottomSpacing =<< bdm+--+-- appSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- appSep x = docSeq [x, docSeparator]+--+-- docCommaSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered+-- docCommaSep = appSep $ docLit $ Text.pack ","+--+-- docParenLSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered+-- docParenLSep = appSep $ docLit $ Text.pack "("+--+--+-- docPostComment :: (Data.Data.Data ast, MonadMultiState NodeAllocIndex m)+-- => Located ast+-- -> m BriDocNumbered+-- -> m BriDocNumbered+-- docPostComment ast bdm = do+-- bd <- bdm+-- allocateNode $ BDFAnnotationPost (ExactPrint.Types.mkAnnKey ast) bd+--+-- docWrapNode :: ( Data.Data.Data ast, MonadMultiState NodeAllocIndex m)+-- => Located ast+-- -> m BriDocNumbered+-- -> m BriDocNumbered+-- docWrapNode ast bdm = do+-- bd <- bdm+-- i1 <- allocNodeIndex+-- i2 <- allocNodeIndex+-- return+-- $ (,) i1+-- $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast)+-- $ (,) i2+-- $ BDFAnnotationPost (ExactPrint.Types.mkAnnKey ast)+-- $ bd+--+-- docPar :: MonadMultiState NodeAllocIndex m+-- => m BriDocNumbered+-- -> m BriDocNumbered+-- -> m BriDocNumbered+-- docPar lineM indentedM = do+-- line <- lineM+-- indented <- indentedM+-- allocateNode $ BDFPar BrIndentNone line indented+--+-- docForceSingleline :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- docForceSingleline bdm = allocateNode . BDFForceSingleline =<< bdm+--+-- docForceMultiline :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered+-- docForceMultiline bdm = allocateNode . BDFForceMultiline =<< bdm+--+-- docEnsureIndent :: MonadMultiState NodeAllocIndex m => BrIndent -> m BriDocNumbered -> m BriDocNumbered+-- docEnsureIndent ind mbd = mbd >>= \bd -> allocateNode $ BDFEnsureIndent ind bd++docEmpty :: ToBriDocM BriDocNumbered+docEmpty = allocateNode BDFEmpty++docLit :: Text -> ToBriDocM BriDocNumbered+docLit t = allocateNode $ BDFLit t++docLitS :: String -> ToBriDocM BriDocNumbered+docLitS s = allocateNode $ BDFLit $ Text.pack s++docExt+ :: (ExactPrint.Annotate.Annotate ast)+ => Located ast+ -> ExactPrint.Types.Anns+ -> Bool+ -> ToBriDocM BriDocNumbered+docExt x anns shouldAddComment = allocateNode $ BDFExternal+ (ExactPrint.Types.mkAnnKey x)+ (foldedAnnKeys x)+ shouldAddComment+ (Text.pack $ ExactPrint.exactPrint x anns)++docAlt :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered+docAlt l = allocateNode . BDFAlt =<< sequence l++newtype CollectAltM a = CollectAltM (Writer.Writer [ToBriDocM BriDocNumbered] a)+ deriving (Functor, Applicative, Monad)++addAlternativeCond :: Bool -> ToBriDocM BriDocNumbered -> CollectAltM ()+addAlternativeCond cond doc = when cond (addAlternative doc)++addAlternative :: ToBriDocM BriDocNumbered -> CollectAltM ()+addAlternative = CollectAltM . Writer.tell . (: [])++runFilteredAlternative :: CollectAltM () -> ToBriDocM BriDocNumbered+runFilteredAlternative (CollectAltM action) = docAlt $ Writer.execWriter action+++docSeq :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered+docSeq [] = docEmpty+docSeq l = allocateNode . BDFSeq =<< sequence l++docLines :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered+docLines l = allocateNode . BDFLines =<< sequence l++docCols :: ColSig -> [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered+docCols sig l = allocateNode . BDFCols sig =<< sequence l++docAddBaseY :: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docAddBaseY ind bdm = allocateNode . BDFAddBaseY ind =<< bdm++docSetBaseY :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docSetBaseY bdm = do+ bd <- bdm+ -- the order here is important so that these two nodes can be treated+ -- properly over at `transformAlts`.+ n1 <- allocateNode $ BDFBaseYPushCur bd+ n2 <- allocateNode $ BDFBaseYPop n1+ return n2++docSetIndentLevel :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docSetIndentLevel bdm = do+ bd <- bdm+ n1 <- allocateNode $ BDFIndentLevelPushCur bd+ n2 <- allocateNode $ BDFIndentLevelPop n1+ return n2++docSetBaseAndIndent :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docSetBaseAndIndent = docSetBaseY . docSetIndentLevel++docSeparator :: ToBriDocM BriDocNumbered+docSeparator = allocateNode BDFSeparator++docAnnotationPrior+ :: AnnKey -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docAnnotationPrior annKey bdm =+ allocateNode . BDFAnnotationPrior annKey =<< bdm++docAnnotationKW+ :: AnnKey+ -> Maybe AnnKeywordId+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+docAnnotationKW annKey kw bdm =+ allocateNode . BDFAnnotationKW annKey kw =<< bdm++docMoveToKWDP+ :: AnnKey+ -> AnnKeywordId+ -> Bool+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+docMoveToKWDP annKey kw shouldRestoreIndent bdm =+ allocateNode . BDFMoveToKWDP annKey kw shouldRestoreIndent =<< bdm++docAnnotationRest+ :: AnnKey -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docAnnotationRest annKey bdm = allocateNode . BDFAnnotationRest annKey =<< bdm++docNonBottomSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docNonBottomSpacing bdm = allocateNode . BDFNonBottomSpacing False =<< bdm++docNonBottomSpacingS :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docNonBottomSpacingS bdm = allocateNode . BDFNonBottomSpacing True =<< bdm++docSetParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docSetParSpacing bdm = allocateNode . BDFSetParSpacing =<< bdm++docForceParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docForceParSpacing bdm = allocateNode . BDFForceParSpacing =<< bdm++docDebug :: String -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docDebug s bdm = allocateNode . BDFDebug s =<< bdm++appSep :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+appSep x = docSeq [x, docSeparator]++docCommaSep :: ToBriDocM BriDocNumbered+docCommaSep = appSep $ docLit $ Text.pack ","++docParenLSep :: ToBriDocM BriDocNumbered+docParenLSep = appSep docParenL++-- TODO: we don't make consistent use of these (yet). However, I think the+-- most readable approach overall might be something else: define+-- `lit = docLit . Text.pack` and `prepSep = docSeq [docSeparator, x]`.+-- I think those two would make the usage most readable.+-- lit "(" and appSep (lit "(") are understandable and short without+-- introducing a new top-level binding for all types of parentheses.+docParenL :: ToBriDocM BriDocNumbered+docParenL = docLit $ Text.pack "("++docParenR :: ToBriDocM BriDocNumbered+docParenR = docLit $ Text.pack ")"++docParenHashLSep :: ToBriDocM BriDocNumbered+docParenHashLSep = docSeq [docLit $ Text.pack "(#", docSeparator]++docParenHashRSep :: ToBriDocM BriDocNumbered+docParenHashRSep = docSeq [docSeparator, docLit $ Text.pack "#)"]++docBracketL :: ToBriDocM BriDocNumbered+docBracketL = docLit $ Text.pack "["++docBracketR :: ToBriDocM BriDocNumbered+docBracketR = docLit $ Text.pack "]"+++docTick :: ToBriDocM BriDocNumbered+docTick = docLit $ Text.pack "'"++docNodeAnnKW+ :: Data.Data.Data ast+ => Located ast+ -> Maybe AnnKeywordId+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+docNodeAnnKW ast kw bdm =+ docAnnotationKW (ExactPrint.Types.mkAnnKey ast) kw bdm++docNodeMoveToKWDP+ :: Data.Data.Data ast+ => Located ast+ -> AnnKeywordId+ -> Bool+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+docNodeMoveToKWDP ast kw shouldRestoreIndent bdm =+ docMoveToKWDP (ExactPrint.Types.mkAnnKey ast) kw shouldRestoreIndent bdm++class DocWrapable a where+ docWrapNode :: ( Data.Data.Data ast)+ => Located ast+ -> a+ -> a+ docWrapNodePrior :: ( Data.Data.Data ast)+ => Located ast+ -> a+ -> a+ docWrapNodeRest :: ( Data.Data.Data ast)+ => Located ast+ -> a+ -> a++instance DocWrapable (ToBriDocM BriDocNumbered) where+ docWrapNode ast bdm = do+ bd <- bdm+ i1 <- allocNodeIndex+ i2 <- allocNodeIndex+ return+ $ (,) i1+ $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast)+ $ (,) i2+ $ BDFAnnotationRest (ExactPrint.Types.mkAnnKey ast)+ $ bd+ docWrapNodePrior ast bdm = do+ bd <- bdm+ i1 <- allocNodeIndex+ return $ (,) i1 $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast) $ bd+ docWrapNodeRest ast bdm = do+ bd <- bdm+ i2 <- allocNodeIndex+ return $ (,) i2 $ BDFAnnotationRest (ExactPrint.Types.mkAnnKey ast) $ bd++instance DocWrapable (ToBriDocM a) => DocWrapable [ToBriDocM a] where+ docWrapNode ast bdms = case bdms of+ [] -> []+ [bd] -> [docWrapNode ast bd]+ (bd1 : bdR) | (bdN : bdM) <- reverse bdR ->+ [docWrapNodePrior ast bd1] ++ reverse bdM ++ [docWrapNodeRest ast bdN]+ _ -> error "cannot happen (TM)"+ docWrapNodePrior ast bdms = case bdms of+ [] -> []+ [bd] -> [docWrapNodePrior ast bd]+ (bd1 : bdR) -> docWrapNodePrior ast bd1 : bdR+ docWrapNodeRest ast bdms = case reverse bdms of+ [] -> []+ (bdN : bdR) -> reverse $ docWrapNodeRest ast bdN : bdR++instance DocWrapable (ToBriDocM a) => DocWrapable (ToBriDocM [a]) where+ docWrapNode ast bdsm = do+ bds <- bdsm+ case bds of+ [] -> return [] -- TODO: this might be bad. maybe. then again, not really. well.+ [bd] -> do+ bd' <- docWrapNode ast (return bd)+ return [bd']+ (bd1 : bdR) | (bdN : bdM) <- reverse bdR -> do+ bd1' <- docWrapNodePrior ast (return bd1)+ bdN' <- docWrapNodeRest ast (return bdN)+ return $ [bd1'] ++ reverse bdM ++ [bdN']+ _ -> error "cannot happen (TM)"+ docWrapNodePrior ast bdsm = do+ bds <- bdsm+ case bds of+ [] -> return []+ (bd1 : bdR) -> do+ bd1' <- docWrapNodePrior ast (return bd1)+ return (bd1' : bdR)+ docWrapNodeRest ast bdsm = do+ bds <- bdsm+ case reverse bds of+ [] -> return []+ (bdN : bdR) -> do+ bdN' <- docWrapNodeRest ast (return bdN)+ return $ reverse (bdN' : bdR)++instance DocWrapable (ToBriDocM a) => DocWrapable (ToBriDocM (Seq a)) where+ docWrapNode ast bdsm = do+ bds <- bdsm+ case Seq.viewl bds of+ Seq.EmptyL -> return Seq.empty -- TODO: this might be bad. maybe. then again, not really. well.+ bd1 Seq.:< rest -> case Seq.viewr rest of+ Seq.EmptyR -> do+ bd1' <- docWrapNode ast (return bd1)+ return $ Seq.singleton bd1'+ bdM Seq.:> bdN -> do+ bd1' <- docWrapNodePrior ast (return bd1)+ bdN' <- docWrapNodeRest ast (return bdN)+ return $ (bd1' Seq.<| bdM) Seq.|> bdN'+ docWrapNodePrior ast bdsm = do+ bds <- bdsm+ case Seq.viewl bds of+ Seq.EmptyL -> return Seq.empty+ bd1 Seq.:< bdR -> do+ bd1' <- docWrapNodePrior ast (return bd1)+ return $ bd1' Seq.<| bdR+ docWrapNodeRest ast bdsm = do+ bds <- bdsm+ case Seq.viewr bds of+ Seq.EmptyR -> return Seq.empty+ bdR Seq.:> bdN -> do+ bdN' <- docWrapNodeRest ast (return bdN)+ return $ bdR Seq.|> bdN'++instance DocWrapable (ToBriDocM ([BriDocNumbered], BriDocNumbered, a)) where+ docWrapNode ast stuffM = do+ (bds, bd, x) <- stuffM+ if null bds+ then do+ bd' <- docWrapNode ast (return bd)+ return (bds, bd', x)+ else do+ bds' <- docWrapNodePrior ast (return bds)+ bd' <- docWrapNodeRest ast (return bd)+ return (bds', bd', x)+ docWrapNodePrior ast stuffM = do+ (bds, bd, x) <- stuffM+ bds' <- docWrapNodePrior ast (return bds)+ return (bds', bd, x)+ docWrapNodeRest ast stuffM = do+ (bds, bd, x) <- stuffM+ bd' <- docWrapNodeRest ast (return bd)+ return (bds, bd', x)++++docPar+ :: ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+docPar lineM indentedM = do+ line <- lineM+ indented <- indentedM+ allocateNode $ BDFPar BrIndentNone line indented++docForceSingleline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docForceSingleline bdm = allocateNode . BDFForceSingleline =<< bdm++docForceMultiline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docForceMultiline bdm = allocateNode . BDFForceMultiline =<< bdm++docEnsureIndent+ :: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+docEnsureIndent ind mbd = mbd >>= \bd -> allocateNode $ BDFEnsureIndent ind bd++unknownNodeError+ :: Data.Data.Data ast+ => String+ -> GenLocated GHC.SrcSpan ast+ -> ToBriDocM BriDocNumbered+unknownNodeError infoStr ast = do+ mTell [ErrorUnknownNode infoStr ast]+ docLit $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"++spacifyDocs :: [ToBriDocM BriDocNumbered] -> [ToBriDocM BriDocNumbered]+spacifyDocs [] = []+spacifyDocs ds = fmap appSep (List.init ds) ++ [List.last ds]++briDocMToPPM :: ToBriDocM a -> PPMLocal a+briDocMToPPM m = do+ (x, errs, debugs) <- briDocMToPPMInner m+ mTell debugs+ mTell errs+ return x++briDocMToPPMInner :: ToBriDocM a -> PPMLocal (a, [BrittanyError], Seq String)+briDocMToPPMInner m = do+ readers <- MultiRWSS.mGetRawR+ let+ ((x, errs), debugs) =+ runIdentity+ $ MultiRWSS.runMultiRWSTNil+ $ MultiRWSS.withMultiStateA (NodeAllocIndex 1)+ $ MultiRWSS.withMultiReaders readers+ $ MultiRWSS.withMultiWriterAW+ $ MultiRWSS.withMultiWriterAW+ $ m+ pure (x, errs, debugs)++docSharedWrapper :: Monad m => (x -> m y) -> x -> m (m y)+docSharedWrapper f x = return <$> f x
+ source/library/Brittany/Internal/Layouters/DataDecl.hs view
@@ -0,0 +1,434 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.DataDecl where++import qualified Data.Data+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import GHC (GenLocated(L), Located)+import qualified GHC+import GHC.Hs+import qualified GHC.OldList as List+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.Type+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types++++layoutDataDecl+ :: Located (TyClDecl GhcPs)+ -> Located RdrName+ -> LHsQTyVars GhcPs+ -> HsDataDefn GhcPs+ -> ToBriDocM BriDocNumbered+layoutDataDecl ltycl name (HsQTvs _ bndrs) defn = case defn of+ -- newtype MyType a b = MyType ..+ HsDataDefn _ext NewType (L _ []) _ctype Nothing [cons] mDerivs ->+ case cons of+ (L _ (ConDeclH98 _ext consName (L _ False) _qvars (Just (L _ [])) details _conDoc))+ -> docWrapNode ltycl $ do+ nameStr <- lrdrNameToTextAnn name+ consNameStr <- lrdrNameToTextAnn consName+ tyVarLine <- return <$> createBndrDoc bndrs+ -- headDoc <- fmap return $ docSeq+ -- [ appSep $ docLitS "newtype")+ -- , appSep $ docLit nameStr+ -- , appSep tyVarLine+ -- ]+ rhsDoc <- return <$> createDetailsDoc consNameStr details+ createDerivingPar mDerivs $ docSeq+ [ appSep $ docLitS "newtype"+ , appSep $ docLit nameStr+ , appSep tyVarLine+ , docSeparator+ , docLitS "="+ , docSeparator+ , rhsDoc+ ]+ _ -> briDocByExactNoComment ltycl+++ -- data MyData a b+ -- (zero constructors)+ HsDataDefn _ext DataType (L _ lhsContext) _ctype Nothing [] mDerivs ->+ docWrapNode ltycl $ do+ lhsContextDoc <- docSharedWrapper createContextDoc lhsContext+ nameStr <- lrdrNameToTextAnn name+ tyVarLine <- return <$> createBndrDoc bndrs+ createDerivingPar mDerivs $ docSeq+ [ appSep $ docLitS "data"+ , lhsContextDoc+ , appSep $ docLit nameStr+ , appSep tyVarLine+ ]++ -- data MyData = MyData ..+ -- data MyData = MyData { .. }+ HsDataDefn _ext DataType (L _ lhsContext) _ctype Nothing [cons] mDerivs ->+ case cons of+ (L _ (ConDeclH98 _ext consName (L _ _hasExt) qvars mRhsContext details _conDoc))+ -> docWrapNode ltycl $ do+ lhsContextDoc <- docSharedWrapper createContextDoc lhsContext+ nameStr <- lrdrNameToTextAnn name+ consNameStr <- lrdrNameToTextAnn consName+ tyVarLine <- return <$> createBndrDoc bndrs+ forallDocMay <- case createForallDoc qvars of+ Nothing -> pure Nothing+ Just x -> Just . pure <$> x+ rhsContextDocMay <- case mRhsContext of+ Nothing -> pure Nothing+ Just (L _ ctxt) -> Just . pure <$> createContextDoc ctxt+ rhsDoc <- return <$> createDetailsDoc consNameStr details+ consDoc <-+ fmap pure+ $ docNonBottomSpacing+ $ case (forallDocMay, rhsContextDocMay) of+ (Just forallDoc, Just rhsContextDoc) -> docLines+ [ docSeq+ [docLitS "=", docSeparator, docForceSingleline forallDoc]+ , docSeq+ [ docLitS "."+ , docSeparator+ , docSetBaseY $ docLines [rhsContextDoc, docSetBaseY rhsDoc]+ ]+ ]+ (Just forallDoc, Nothing) -> docLines+ [ docSeq+ [docLitS "=", docSeparator, docForceSingleline forallDoc]+ , docSeq [docLitS ".", docSeparator, rhsDoc]+ ]+ (Nothing, Just rhsContextDoc) -> docSeq+ [ docLitS "="+ , docSeparator+ , docSetBaseY $ docLines [rhsContextDoc, docSetBaseY rhsDoc]+ ]+ (Nothing, Nothing) ->+ docSeq [docLitS "=", docSeparator, rhsDoc]+ createDerivingPar mDerivs $ docAlt+ [ -- data D = forall a . Show a => D a+ docSeq+ [ docNodeAnnKW ltycl (Just GHC.AnnData) $ docSeq+ [ appSep $ docLitS "data"+ , docForceSingleline $ lhsContextDoc+ , appSep $ docLit nameStr+ , appSep tyVarLine+ , docSeparator+ ]+ , docLitS "="+ , docSeparator+ , docSetIndentLevel $ docSeq+ [ case forallDocMay of+ Nothing -> docEmpty+ Just forallDoc ->+ docSeq+ [ docForceSingleline forallDoc+ , docSeparator+ , docLitS "."+ , docSeparator+ ]+ , maybe docEmpty docForceSingleline rhsContextDocMay+ , rhsDoc+ ]+ ]+ , -- data D+ -- = forall a . Show a => D a+ docAddBaseY BrIndentRegular $ docPar+ (docNodeAnnKW ltycl (Just GHC.AnnData) $ docSeq+ [ appSep $ docLitS "data"+ , docForceSingleline lhsContextDoc+ , appSep $ docLit nameStr+ , tyVarLine+ ]+ )+ (docSeq+ [ docLitS "="+ , docSeparator+ , docSetIndentLevel $ docSeq+ [ case forallDocMay of+ Nothing -> docEmpty+ Just forallDoc ->+ docSeq+ [ docForceSingleline forallDoc+ , docSeparator+ , docLitS "."+ , docSeparator+ ]+ , maybe docEmpty docForceSingleline rhsContextDocMay+ , rhsDoc+ ]+ ]+ )+ , -- data D+ -- = forall a+ -- . Show a =>+ -- D a+ docAddBaseY BrIndentRegular $ docPar+ (docNodeAnnKW ltycl (Just GHC.AnnData) $ docSeq+ [ appSep $ docLitS "data"+ , docForceSingleline lhsContextDoc+ , appSep $ docLit nameStr+ , tyVarLine+ ]+ )+ consDoc+ , -- data+ -- Show a =>+ -- D+ -- = forall a+ -- . Show a =>+ -- D a+ -- This alternative is only for -XDatatypeContexts.+ -- But I think it is rather unlikely this will trigger without+ -- -XDataTypeContexts, especially with the `docNonBottomSpacing`+ -- above, so while not strictly necessary, this should not+ -- hurt.+ docAddBaseY BrIndentRegular $ docPar+ (docLitS "data")+ (docLines+ [ lhsContextDoc+ , docNodeAnnKW ltycl (Just GHC.AnnData)+ $ docSeq [appSep $ docLit nameStr, tyVarLine]+ , consDoc+ ]+ )+ ]+ _ -> briDocByExactNoComment ltycl++ _ -> briDocByExactNoComment ltycl++createContextDoc :: HsContext GhcPs -> ToBriDocM BriDocNumbered+createContextDoc [] = docEmpty+createContextDoc [t] =+ docSeq [layoutType t, docSeparator, docLitS "=>", docSeparator]+createContextDoc (t1 : tR) = do+ t1Doc <- docSharedWrapper layoutType t1+ tRDocs <- tR `forM` docSharedWrapper layoutType+ docAlt+ [ docSeq+ [ docLitS "("+ , docForceSingleline $ docSeq $ List.intersperse+ docCommaSep+ (t1Doc : tRDocs)+ , docLitS ") =>"+ , docSeparator+ ]+ , docLines $ join+ [ [docSeq [docLitS "(", docSeparator, t1Doc]]+ , tRDocs <&> \tRDoc -> docSeq [docLitS ",", docSeparator, tRDoc]+ , [docLitS ") =>", docSeparator]+ ]+ ]++createBndrDoc :: [LHsTyVarBndr flag GhcPs] -> ToBriDocM BriDocNumbered+createBndrDoc bs = do+ tyVarDocs <- bs `forM` \case+ (L _ (UserTyVar _ _ext vname)) -> return $ (lrdrNameToText vname, Nothing)+ (L _ (KindedTyVar _ _ext lrdrName kind)) -> do+ d <- docSharedWrapper layoutType kind+ return $ (lrdrNameToText lrdrName, Just $ d)+ docSeq $ List.intersperse docSeparator $ tyVarDocs <&> \(vname, mKind) ->+ case mKind of+ Nothing -> docLit vname+ Just kind -> docSeq+ [ docLitS "("+ , docLit vname+ , docSeparator+ , docLitS "::"+ , docSeparator+ , kind+ , docLitS ")"+ ]++createDerivingPar+ :: HsDeriving GhcPs -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered+createDerivingPar derivs mainDoc = do+ case derivs of+ (L _ []) -> mainDoc+ (L _ types) ->+ docPar mainDoc+ $ docEnsureIndent BrIndentRegular+ $ docLines+ $ docWrapNode derivs+ $ derivingClauseDoc+ <$> types++derivingClauseDoc :: LHsDerivingClause GhcPs -> ToBriDocM BriDocNumbered+derivingClauseDoc (L _ (HsDerivingClause _ext mStrategy types)) = case types of+ (L _ []) -> docSeq []+ (L _ ts) ->+ let+ tsLength = length ts+ whenMoreThan1Type val = if tsLength > 1 then docLitS val else docLitS ""+ (lhsStrategy, rhsStrategy) =+ maybe (docEmpty, docEmpty) strategyLeftRight mStrategy+ in docSeq+ [ docDeriving+ , docWrapNodePrior types $ lhsStrategy+ , docSeparator+ , whenMoreThan1Type "("+ , docWrapNodeRest types+ $ docSeq+ $ List.intersperse docCommaSep+ $ ts+ <&> \case+ HsIB _ t -> layoutType t+ , whenMoreThan1Type ")"+ , rhsStrategy+ ]+ where+ strategyLeftRight = \case+ (L _ StockStrategy) -> (docLitS " stock", docEmpty)+ (L _ AnyclassStrategy) -> (docLitS " anyclass", docEmpty)+ (L _ NewtypeStrategy) -> (docLitS " newtype", docEmpty)+ lVia@(L _ (ViaStrategy viaTypes)) ->+ ( docEmpty+ , case viaTypes of+ HsIB _ext t ->+ docSeq [docWrapNode lVia $ docLitS " via", docSeparator, layoutType t]+ )++docDeriving :: ToBriDocM BriDocNumbered+docDeriving = docLitS "deriving"++createDetailsDoc+ :: Text -> HsConDeclDetails GhcPs -> (ToBriDocM BriDocNumbered)+createDetailsDoc consNameStr details = case details of+ PrefixCon args -> do+ indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack+ let+ singleLine = docSeq+ [ docLit consNameStr+ , docSeparator+ , docForceSingleline+ $ docSeq+ $ List.intersperse docSeparator+ $ fmap hsScaledThing args+ <&> layoutType+ ]+ leftIndented =+ docSetParSpacing+ . docAddBaseY BrIndentRegular+ . docPar (docLit consNameStr)+ . docLines+ $ layoutType+ <$> fmap hsScaledThing args+ multiAppended = docSeq+ [ docLit consNameStr+ , docSeparator+ , docSetBaseY $ docLines $ layoutType <$> fmap hsScaledThing args+ ]+ multiIndented = docSetBaseY $ docAddBaseY BrIndentRegular $ docPar+ (docLit consNameStr)+ (docLines $ layoutType <$> fmap hsScaledThing args)+ case indentPolicy of+ IndentPolicyLeft -> docAlt [singleLine, leftIndented]+ IndentPolicyMultiple -> docAlt [singleLine, multiAppended, leftIndented]+ IndentPolicyFree ->+ docAlt [singleLine, multiAppended, multiIndented, leftIndented]+ RecCon (L _ []) ->+ docSeq [docLit consNameStr, docSeparator, docLit $ Text.pack "{}"]+ RecCon lRec@(L _ fields@(_ : _)) -> do+ let ((fName1, fType1) : fDocR) = mkFieldDocs fields+ -- allowSingleline <- mAsk <&> _conf_layout .> _lconfig_allowSinglelineRecord .> confUnpack+ let allowSingleline = False+ docAddBaseY BrIndentRegular $ runFilteredAlternative $ do+ -- single-line: { i :: Int, b :: Bool }+ addAlternativeCond allowSingleline $ docSeq+ [ docLit consNameStr+ , docSeparator+ , docWrapNodePrior lRec $ docLitS "{"+ , docSeparator+ , docWrapNodeRest lRec+ $ docForceSingleline+ $ docSeq+ $ join+ $ [fName1, docSeparator, docLitS "::", docSeparator, fType1]+ : [ [ docLitS ","+ , docSeparator+ , fName+ , docSeparator+ , docLitS "::"+ , docSeparator+ , fType+ ]+ | (fName, fType) <- fDocR+ ]+ , docSeparator+ , docLitS "}"+ ]+ addAlternative $ docPar+ (docLit consNameStr)+ (docWrapNodePrior lRec $ docNonBottomSpacingS $ docLines+ [ docAlt+ [ docCols+ ColRecDecl+ [ appSep (docLitS "{")+ , appSep $ docForceSingleline fName1+ , docSeq [docLitS "::", docSeparator]+ , docForceSingleline $ fType1+ ]+ , docSeq+ [ docLitS "{"+ , docSeparator+ , docSetBaseY $ docAddBaseY BrIndentRegular $ docPar+ fName1+ (docSeq [docLitS "::", docSeparator, fType1])+ ]+ ]+ , docWrapNodeRest lRec $ docLines $ fDocR <&> \(fName, fType) ->+ docAlt+ [ docCols+ ColRecDecl+ [ docCommaSep+ , appSep $ docForceSingleline fName+ , docSeq [docLitS "::", docSeparator]+ , docForceSingleline fType+ ]+ , docSeq+ [ docLitS ","+ , docSeparator+ , docSetBaseY $ docAddBaseY BrIndentRegular $ docPar+ fName+ (docSeq [docLitS "::", docSeparator, fType])+ ]+ ]+ , docLitS "}"+ ]+ )+ InfixCon arg1 arg2 -> docSeq+ [ layoutType $ hsScaledThing arg1+ , docSeparator+ , docLit consNameStr+ , docSeparator+ , layoutType $ hsScaledThing arg2+ ]+ where+ mkFieldDocs+ :: [LConDeclField GhcPs]+ -> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]+ mkFieldDocs = fmap $ \lField -> case lField of+ L _ (ConDeclField _ext names t _) -> createNamesAndTypeDoc lField names t++createForallDoc+ :: [LHsTyVarBndr flag GhcPs] -> Maybe (ToBriDocM BriDocNumbered)+createForallDoc [] = Nothing+createForallDoc lhsTyVarBndrs =+ Just $ docSeq [docLitS "forall ", createBndrDoc lhsTyVarBndrs]++createNamesAndTypeDoc+ :: Data.Data.Data ast+ => Located ast+ -> [GenLocated t (FieldOcc GhcPs)]+ -> Located (HsType GhcPs)+ -> (ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)+createNamesAndTypeDoc lField names t =+ ( docNodeAnnKW lField Nothing $ docWrapNodePrior lField $ docSeq+ [ docSeq $ List.intersperse docCommaSep $ names <&> \case+ L _ (FieldOcc _ fieldName) -> docLit =<< lrdrNameToTextAnn fieldName+ ]+ , docWrapNodeRest lField $ layoutType t+ )
+ source/library/Brittany/Internal/Layouters/Decl.hs view
@@ -0,0 +1,998 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal.Layouters.Decl where++import qualified Data.Data+import qualified Data.Foldable+import qualified Data.Maybe+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import GHC (AnnKeywordId(..), GenLocated(L))+import GHC.Data.Bag (bagToList, emptyBag)+import qualified GHC.Data.FastString as FastString+import GHC.Hs+import qualified GHC.OldList as List+import GHC.Types.Basic+ ( Activation(..)+ , InlinePragma(..)+ , InlineSpec(..)+ , LexicalFixity(..)+ , RuleMatchInfo(..)+ )+import GHC.Types.SrcLoc (Located, SrcSpan, getLoc, unLoc)+import Brittany.Internal.Config.Types+import Brittany.Internal.ExactPrintUtils+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.DataDecl+import {-# SOURCE #-} Brittany.Internal.Layouters.Expr+import Brittany.Internal.Layouters.Pattern+import {-# SOURCE #-} Brittany.Internal.Layouters.Stmt+import Brittany.Internal.Layouters.Type+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import Language.Haskell.GHC.ExactPrint.Types (mkAnnKey)+import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint++++layoutDecl :: ToBriDoc HsDecl+layoutDecl d@(L loc decl) = case decl of+ SigD _ sig -> withTransformedAnns d $ layoutSig (L loc sig)+ ValD _ bind -> withTransformedAnns d $ layoutBind (L loc bind) >>= \case+ Left ns -> docLines $ return <$> ns+ Right n -> return n+ TyClD _ tycl -> withTransformedAnns d $ layoutTyCl (L loc tycl)+ InstD _ (TyFamInstD _ tfid) ->+ withTransformedAnns d $ layoutTyFamInstDecl False d tfid+ InstD _ (ClsInstD _ inst) ->+ withTransformedAnns d $ layoutClsInst (L loc inst)+ _ -> briDocByExactNoComment d++--------------------------------------------------------------------------------+-- Sig+--------------------------------------------------------------------------------++layoutSig :: ToBriDoc Sig+layoutSig lsig@(L _loc sig) = case sig of+ TypeSig _ names (HsWC _ (HsIB _ typ)) -> layoutNamesAndType Nothing names typ+ InlineSig _ name (InlinePragma _ spec _arity phaseAct conlike) ->+ docWrapNode lsig $ do+ nameStr <- lrdrNameToTextAnn name+ specStr <- specStringCompat lsig spec+ let+ phaseStr = case phaseAct of+ NeverActive -> "" -- not [] - for NOINLINE NeverActive is+ -- in fact the default+ AlwaysActive -> ""+ ActiveBefore _ i -> "[~" ++ show i ++ "] "+ ActiveAfter _ i -> "[" ++ show i ++ "] "+ FinalActive -> error "brittany internal error: FinalActive"+ let+ conlikeStr = case conlike of+ FunLike -> ""+ ConLike -> "CONLIKE "+ docLit+ $ Text.pack ("{-# " ++ specStr ++ conlikeStr ++ phaseStr)+ <> nameStr+ <> Text.pack " #-}"+ ClassOpSig _ False names (HsIB _ typ) -> layoutNamesAndType Nothing names typ+ PatSynSig _ names (HsIB _ typ) ->+ layoutNamesAndType (Just "pattern") names typ+ _ -> briDocByExactNoComment lsig -- TODO+ where+ layoutNamesAndType mKeyword names typ = docWrapNode lsig $ do+ let+ keyDoc = case mKeyword of+ Just key -> [appSep . docLit $ Text.pack key]+ Nothing -> []+ nameStrs <- names `forM` lrdrNameToTextAnn+ let nameStr = Text.intercalate (Text.pack ", ") $ nameStrs+ typeDoc <- docSharedWrapper layoutType typ+ hasComments <- hasAnyCommentsBelow lsig+ shouldBeHanging <-+ mAsk <&> _conf_layout .> _lconfig_hangingTypeSignature .> confUnpack+ if shouldBeHanging+ then+ docSeq+ $ [ appSep+ $ docWrapNodeRest lsig+ $ docSeq+ $ keyDoc+ <> [docLit nameStr]+ , docSetBaseY $ docLines+ [ docCols+ ColTyOpPrefix+ [ docLit $ Text.pack ":: "+ , docAddBaseY (BrIndentSpecial 3) $ typeDoc+ ]+ ]+ ]+ else layoutLhsAndType+ hasComments+ (appSep . docWrapNodeRest lsig . docSeq $ keyDoc <> [docLit nameStr])+ "::"+ typeDoc++specStringCompat+ :: MonadMultiWriter [BrittanyError] m => LSig GhcPs -> InlineSpec -> m String+specStringCompat ast = \case+ NoUserInline -> mTell [ErrorUnknownNode "NoUserInline" ast] $> ""+ Inline -> pure "INLINE "+ Inlinable -> pure "INLINABLE "+ NoInline -> pure "NOINLINE "++layoutGuardLStmt :: ToBriDoc' (Stmt GhcPs (LHsExpr GhcPs))+layoutGuardLStmt lgstmt@(L _ stmtLR) = docWrapNode lgstmt $ case stmtLR of+ BodyStmt _ body _ _ -> layoutExpr body+ BindStmt _ lPat expr -> do+ patDoc <- docSharedWrapper layoutPat lPat+ expDoc <- docSharedWrapper layoutExpr expr+ docCols+ ColBindStmt+ [ appSep $ colsWrapPat =<< patDoc+ , docSeq [appSep $ docLit $ Text.pack "<-", expDoc]+ ]+ _ -> unknownNodeError "" lgstmt -- TODO+++--------------------------------------------------------------------------------+-- HsBind+--------------------------------------------------------------------------------++layoutBind+ :: ToBriDocC (HsBindLR GhcPs GhcPs) (Either [BriDocNumbered] BriDocNumbered)+layoutBind lbind@(L _ bind) = case bind of+ FunBind _ fId (MG _ lmatches@(L _ matches) _) [] -> do+ idStr <- lrdrNameToTextAnn fId+ binderDoc <- docLit $ Text.pack "="+ funcPatDocs <-+ docWrapNode lbind+ $ docWrapNode lmatches+ $ layoutPatternBind (Just idStr) binderDoc+ `mapM` matches+ return $ Left $ funcPatDocs+ PatBind _ pat (GRHSs _ grhss whereBinds) ([], []) -> do+ patDocs <- colsWrapPat =<< layoutPat pat+ clauseDocs <- layoutGrhs `mapM` grhss+ mWhereDocs <- layoutLocalBinds whereBinds+ let mWhereArg = mWhereDocs <&> (,) (mkAnnKey lbind) -- TODO: is this the right AnnKey?+ binderDoc <- docLit $ Text.pack "="+ hasComments <- hasAnyCommentsBelow lbind+ fmap Right $ docWrapNode lbind $ layoutPatternBindFinal+ Nothing+ binderDoc+ (Just patDocs)+ clauseDocs+ mWhereArg+ hasComments+ PatSynBind _ (PSB _ patID lpat rpat dir) -> do+ fmap Right $ docWrapNode lbind $ layoutPatSynBind patID lpat dir rpat+ _ -> Right <$> unknownNodeError "" lbind+layoutIPBind :: ToBriDoc IPBind+layoutIPBind lipbind@(L _ bind) = case bind of+ IPBind _ (Right _) _ -> error "brittany internal error: IPBind Right"+ IPBind _ (Left (L _ (HsIPName name))) expr -> do+ ipName <- docLit $ Text.pack $ '?' : FastString.unpackFS name+ binderDoc <- docLit $ Text.pack "="+ exprDoc <- layoutExpr expr+ hasComments <- hasAnyCommentsBelow lipbind+ layoutPatternBindFinal+ Nothing+ binderDoc+ (Just ipName)+ [([], exprDoc, expr)]+ Nothing+ hasComments+++data BagBindOrSig = BagBind (LHsBindLR GhcPs GhcPs)+ | BagSig (LSig GhcPs)++bindOrSigtoSrcSpan :: BagBindOrSig -> SrcSpan+bindOrSigtoSrcSpan (BagBind (L l _)) = l+bindOrSigtoSrcSpan (BagSig (L l _)) = l++layoutLocalBinds+ :: ToBriDocC (HsLocalBindsLR GhcPs GhcPs) (Maybe [BriDocNumbered])+layoutLocalBinds lbinds@(L _ binds) = case binds of+ -- HsValBinds (ValBindsIn lhsBindsLR []) ->+ -- Just . (>>= either id return) . Data.Foldable.toList <$> mapBagM layoutBind lhsBindsLR -- TODO: fix ordering+ -- x@(HsValBinds (ValBindsIn{})) ->+ -- Just . (:[]) <$> unknownNodeError "HsValBinds (ValBindsIn _ (_:_))" x+ HsValBinds _ (ValBinds _ bindlrs sigs) -> do+ let+ unordered =+ [ BagBind b | b <- Data.Foldable.toList bindlrs ]+ ++ [ BagSig s | s <- sigs ]+ ordered = List.sortOn (ExactPrint.rs . bindOrSigtoSrcSpan) unordered+ docs <- docWrapNode lbinds $ join <$> ordered `forM` \case+ BagBind b -> either id return <$> layoutBind b+ BagSig s -> return <$> layoutSig s+ return $ Just $ docs+-- x@(HsValBinds (ValBindsOut _binds _lsigs)) ->+ HsValBinds _ (XValBindsLR{}) -> error "brittany internal error: XValBindsLR"+ HsIPBinds _ (IPBinds _ bb) -> Just <$> mapM layoutIPBind bb+ EmptyLocalBinds{} -> return $ Nothing++-- TODO: we don't need the `LHsExpr GhcPs` anymore, now that there is+-- parSpacing stuff.B+layoutGrhs+ :: LGRHS GhcPs (LHsExpr GhcPs)+ -> ToBriDocM ([BriDocNumbered], BriDocNumbered, LHsExpr GhcPs)+layoutGrhs lgrhs@(L _ (GRHS _ guards body)) = do+ guardDocs <- docWrapNode lgrhs $ layoutStmt `mapM` guards+ bodyDoc <- layoutExpr body+ return (guardDocs, bodyDoc, body)++layoutPatternBind+ :: Maybe Text+ -> BriDocNumbered+ -> LMatch GhcPs (LHsExpr GhcPs)+ -> ToBriDocM BriDocNumbered+layoutPatternBind funId binderDoc lmatch@(L _ match) = do+ let pats = m_pats match+ let (GRHSs _ grhss whereBinds) = m_grhss match+ patDocs <- pats `forM` \p -> fmap return $ colsWrapPat =<< layoutPat p+ let isInfix = isInfixMatch match+ mIdStr <- case match of+ Match _ (FunRhs matchId _ _) _ _ -> Just <$> lrdrNameToTextAnn matchId+ _ -> pure Nothing+ let mIdStr' = fixPatternBindIdentifier match <$> mIdStr+ patDoc <- docWrapNodePrior lmatch $ case (mIdStr', patDocs) of+ (Just idStr, p1 : p2 : pr) | isInfix -> if null pr+ then docCols+ ColPatternsFuncInfix+ [ appSep $ docForceSingleline p1+ , appSep $ docLit $ idStr+ , docForceSingleline p2+ ]+ else docCols+ ColPatternsFuncInfix+ ([ docCols+ ColPatterns+ [ docParenL+ , appSep $ docForceSingleline p1+ , appSep $ docLit $ idStr+ , docForceSingleline p2+ , appSep $ docParenR+ ]+ ]+ ++ (spacifyDocs $ docForceSingleline <$> pr)+ )+ (Just idStr, []) -> docLit idStr+ (Just idStr, ps) ->+ docCols ColPatternsFuncPrefix+ $ appSep (docLit $ idStr)+ : (spacifyDocs $ docForceSingleline <$> ps)+ (Nothing, ps) ->+ docCols ColPatterns+ $ (List.intersperse docSeparator $ docForceSingleline <$> ps)+ clauseDocs <- docWrapNodeRest lmatch $ layoutGrhs `mapM` grhss+ mWhereDocs <- layoutLocalBinds whereBinds+ let mWhereArg = mWhereDocs <&> (,) (mkAnnKey lmatch)+ let alignmentToken = if null pats then Nothing else funId+ hasComments <- hasAnyCommentsBelow lmatch+ layoutPatternBindFinal+ alignmentToken+ binderDoc+ (Just patDoc)+ clauseDocs+ mWhereArg+ hasComments++fixPatternBindIdentifier :: Match GhcPs (LHsExpr GhcPs) -> Text -> Text+fixPatternBindIdentifier match idStr = go $ m_ctxt match+ where+ go = \case+ (FunRhs _ _ SrcLazy) -> Text.cons '~' idStr+ (FunRhs _ _ SrcStrict) -> Text.cons '!' idStr+ (FunRhs _ _ NoSrcStrict) -> idStr+ (StmtCtxt ctx1) -> goInner ctx1+ _ -> idStr+ -- I have really no idea if this path ever occurs, but better safe than+ -- risking another "drop bangpatterns" bugs.+ goInner = \case+ (PatGuard ctx1) -> go ctx1+ (ParStmtCtxt ctx1) -> goInner ctx1+ (TransStmtCtxt ctx1) -> goInner ctx1+ _ -> idStr++layoutPatternBindFinal+ :: Maybe Text+ -> BriDocNumbered+ -> Maybe BriDocNumbered+ -> [([BriDocNumbered], BriDocNumbered, LHsExpr GhcPs)]+ -> Maybe (ExactPrint.AnnKey, [BriDocNumbered])+ -- ^ AnnKey for the node that contains the AnnWhere position annotation+ -> Bool+ -> ToBriDocM BriDocNumbered+layoutPatternBindFinal alignmentToken binderDoc mPatDoc clauseDocs mWhereDocs hasComments+ = do+ let+ patPartInline = case mPatDoc of+ Nothing -> []+ Just patDoc -> [appSep $ docForceSingleline $ return patDoc]+ patPartParWrap = case mPatDoc of+ Nothing -> id+ Just patDoc -> docPar (return patDoc)+ whereIndent <- do+ shouldSpecial <-+ mAsk <&> _conf_layout .> _lconfig_indentWhereSpecial .> confUnpack+ regularIndentAmount <-+ mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ pure $ if shouldSpecial+ then BrIndentSpecial (max 1 (regularIndentAmount `div` 2))+ else BrIndentRegular+ -- TODO: apart from this, there probably are more nodes below which could+ -- be shared between alternatives.+ wherePartMultiLine :: [ToBriDocM BriDocNumbered] <- case mWhereDocs of+ Nothing -> return $ []+ Just (annKeyWhere, [w]) -> pure . pure <$> docAlt+ [ docEnsureIndent BrIndentRegular+ $ docSeq+ [ docLit $ Text.pack "where"+ , docSeparator+ , docForceSingleline $ return w+ ]+ , docMoveToKWDP annKeyWhere AnnWhere False+ $ docEnsureIndent whereIndent+ $ docLines+ [ docLit $ Text.pack "where"+ , docEnsureIndent whereIndent+ $ docSetIndentLevel+ $ docNonBottomSpacing+ $ return w+ ]+ ]+ Just (annKeyWhere, ws) ->+ fmap (pure . pure)+ $ docMoveToKWDP annKeyWhere AnnWhere False+ $ docEnsureIndent whereIndent+ $ docLines+ [ docLit $ Text.pack "where"+ , docEnsureIndent whereIndent+ $ docSetIndentLevel+ $ docNonBottomSpacing+ $ docLines+ $ return+ <$> ws+ ]+ let+ singleLineGuardsDoc guards = appSep $ case guards of+ [] -> docEmpty+ [g] -> docSeq+ [appSep $ docLit $ Text.pack "|", docForceSingleline $ return g]+ gs ->+ docSeq+ $ [appSep $ docLit $ Text.pack "|"]+ ++ (List.intersperse+ docCommaSep+ (docForceSingleline . return <$> gs)+ )+ wherePart = case mWhereDocs of+ Nothing -> Just docEmpty+ Just (_, [w]) -> Just $ docSeq+ [ docSeparator+ , appSep $ docLit $ Text.pack "where"+ , docSetIndentLevel $ docForceSingleline $ return w+ ]+ _ -> Nothing++ indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack++ runFilteredAlternative $ do++ case clauseDocs of+ [(guards, body, _bodyRaw)] -> do+ let guardPart = singleLineGuardsDoc guards+ forM_ wherePart $ \wherePart' ->+ -- one-line solution+ addAlternativeCond (not hasComments) $ docCols+ (ColBindingLine alignmentToken)+ [ docSeq (patPartInline ++ [guardPart])+ , docSeq+ [ appSep $ return binderDoc+ , docForceSingleline $ return body+ , wherePart'+ ]+ ]+ -- one-line solution + where in next line(s)+ addAlternativeCond (Data.Maybe.isJust mWhereDocs)+ $ docLines+ $ [ docCols+ (ColBindingLine alignmentToken)+ [ docSeq (patPartInline ++ [guardPart])+ , docSeq+ [ appSep $ return binderDoc+ , docForceParSpacing $ docAddBaseY BrIndentRegular $ return+ body+ ]+ ]+ ]+ ++ wherePartMultiLine+ -- two-line solution + where in next line(s)+ addAlternative+ $ docLines+ $ [ docForceSingleline+ $ docSeq (patPartInline ++ [guardPart, return binderDoc])+ , docEnsureIndent BrIndentRegular $ docForceSingleline $ return+ body+ ]+ ++ wherePartMultiLine+ -- pattern and exactly one clause in single line, body as par;+ -- where in following lines+ addAlternative+ $ docLines+ $ [ docCols+ (ColBindingLine alignmentToken)+ [ docSeq (patPartInline ++ [guardPart])+ , docSeq+ [ appSep $ return binderDoc+ , docForceParSpacing $ docAddBaseY BrIndentRegular $ return+ body+ ]+ ]+ ]+ -- , lineMod $ docAlt+ -- [ docSetBaseY $ return body+ -- , docAddBaseY BrIndentRegular $ return body+ -- ]+ ++ wherePartMultiLine+ -- pattern and exactly one clause in single line, body in new line.+ addAlternative+ $ docLines+ $ [ docSeq (patPartInline ++ [guardPart, return binderDoc])+ , docNonBottomSpacing+ $ docEnsureIndent BrIndentRegular+ $ docAddBaseY BrIndentRegular+ $ return body+ ]+ ++ wherePartMultiLine++ _ -> return () -- no alternatives exclusively when `length clauseDocs /= 1`++ case mPatDoc of+ Nothing -> return ()+ Just patDoc ->+ -- multiple clauses added in-paragraph, each in a single line+ -- example: foo | bar = baz+ -- | lll = asd+ addAlternativeCond (indentPolicy == IndentPolicyFree)+ $ docLines+ $ [ docSeq+ [ appSep $ docForceSingleline $ return patDoc+ , docSetBaseY+ $ docLines+ $ clauseDocs+ <&> \(guardDocs, bodyDoc, _) -> do+ let guardPart = singleLineGuardsDoc guardDocs+ -- the docForceSingleline might seems superflous, but it+ -- helps the alternative resolving impl.+ docForceSingleline $ docCols+ ColGuardedBody+ [ guardPart+ , docSeq+ [ appSep $ return binderDoc+ , docForceSingleline $ return bodyDoc+ -- i am not sure if there is a benefit to using+ -- docForceParSpacing additionally here:+ -- , docAddBaseY BrIndentRegular $ return bodyDoc+ ]+ ]+ ]+ ]+ ++ wherePartMultiLine+ -- multiple clauses, each in a separate, single line+ addAlternative+ $ docLines+ $ [ docAddBaseY BrIndentRegular+ $ patPartParWrap+ $ docLines+ $ map docSetBaseY+ $ clauseDocs+ <&> \(guardDocs, bodyDoc, _) -> do+ let guardPart = singleLineGuardsDoc guardDocs+ -- the docForceSingleline might seems superflous, but it+ -- helps the alternative resolving impl.+ docForceSingleline $ docCols+ ColGuardedBody+ [ guardPart+ , docSeq+ [ appSep $ return binderDoc+ , docForceSingleline $ return bodyDoc+ -- i am not sure if there is a benefit to using+ -- docForceParSpacing additionally here:+ -- , docAddBaseY BrIndentRegular $ return bodyDoc+ ]+ ]+ ]+ ++ wherePartMultiLine+ -- multiple clauses, each with the guard(s) in a single line, body+ -- as a paragraph+ addAlternative+ $ docLines+ $ [ docAddBaseY BrIndentRegular+ $ patPartParWrap+ $ docLines+ $ map docSetBaseY+ $ clauseDocs+ <&> \(guardDocs, bodyDoc, _) ->+ docSeq+ $ (case guardDocs of+ [] -> []+ [g] ->+ [ docForceSingleline $ docSeq+ [appSep $ docLit $ Text.pack "|", return g]+ ]+ gs ->+ [ docForceSingleline+ $ docSeq+ $ [appSep $ docLit $ Text.pack "|"]+ ++ List.intersperse docCommaSep (return <$> gs)+ ]+ )+ ++ [ docSeparator+ , docCols+ ColOpPrefix+ [ appSep $ return binderDoc+ , docAddBaseY BrIndentRegular+ $ docForceParSpacing+ $ return bodyDoc+ ]+ ]+ ]+ ++ wherePartMultiLine+ -- multiple clauses, each with the guard(s) in a single line, body+ -- in a new line as a paragraph+ addAlternative+ $ docLines+ $ [ docAddBaseY BrIndentRegular+ $ patPartParWrap+ $ docLines+ $ map docSetBaseY+ $ clauseDocs+ >>= \(guardDocs, bodyDoc, _) ->+ (case guardDocs of+ [] -> []+ [g] ->+ [ docForceSingleline+ $ docSeq [appSep $ docLit $ Text.pack "|", return g]+ ]+ gs ->+ [ docForceSingleline+ $ docSeq+ $ [appSep $ docLit $ Text.pack "|"]+ ++ List.intersperse docCommaSep (return <$> gs)+ ]+ )+ ++ [ docCols+ ColOpPrefix+ [ appSep $ return binderDoc+ , docAddBaseY BrIndentRegular+ $ docForceParSpacing+ $ return bodyDoc+ ]+ ]+ ]+ ++ wherePartMultiLine+ -- conservative approach: everything starts on the left.+ addAlternative+ $ docLines+ $ [ docAddBaseY BrIndentRegular+ $ patPartParWrap+ $ docLines+ $ map docSetBaseY+ $ clauseDocs+ >>= \(guardDocs, bodyDoc, _) ->+ (case guardDocs of+ [] -> []+ [g] -> [docSeq [appSep $ docLit $ Text.pack "|", return g]]+ (g1 : gr) ->+ (docSeq [appSep $ docLit $ Text.pack "|", return g1]+ : (gr <&> \g ->+ docSeq [appSep $ docLit $ Text.pack ",", return g]+ )+ )+ )+ ++ [ docCols+ ColOpPrefix+ [ appSep $ return binderDoc+ , docAddBaseY BrIndentRegular $ return bodyDoc+ ]+ ]+ ]+ ++ wherePartMultiLine++-- | Layout a pattern synonym binding+layoutPatSynBind+ :: Located (IdP GhcPs)+ -> HsPatSynDetails (Located (IdP GhcPs))+ -> HsPatSynDir GhcPs+ -> LPat GhcPs+ -> ToBriDocM BriDocNumbered+layoutPatSynBind name patSynDetails patDir rpat = do+ let+ patDoc = docLit $ Text.pack "pattern"+ binderDoc = case patDir of+ ImplicitBidirectional -> docLit $ Text.pack "="+ _ -> docLit $ Text.pack "<-"+ body = colsWrapPat =<< layoutPat rpat+ whereDoc = docLit $ Text.pack "where"+ mWhereDocs <- layoutPatSynWhere patDir+ headDoc <-+ fmap pure+ $ docSeq+ $ [ patDoc+ , docSeparator+ , layoutLPatSyn name patSynDetails+ , docSeparator+ , binderDoc+ ]+ runFilteredAlternative $ do+ addAlternative+ $+ -- pattern .. where+ -- ..+ -- ..+ docAddBaseY BrIndentRegular+ $ docSeq+ ([headDoc, docSeparator, body] ++ case mWhereDocs of+ Just ds -> [docSeparator, docPar whereDoc (docLines ds)]+ Nothing -> []+ )+ addAlternative+ $+ -- pattern .. =+ -- ..+ -- pattern .. <-+ -- .. where+ -- ..+ -- ..+ docAddBaseY BrIndentRegular+ $ docPar+ headDoc+ (case mWhereDocs of+ Nothing -> body+ Just ds -> docLines ([docSeq [body, docSeparator, whereDoc]] ++ ds)+ )++-- | Helper method for the left hand side of a pattern synonym+layoutLPatSyn+ :: Located (IdP GhcPs)+ -> HsPatSynDetails (Located (IdP GhcPs))+ -> ToBriDocM BriDocNumbered+layoutLPatSyn name (PrefixCon vars) = do+ docName <- lrdrNameToTextAnn name+ names <- mapM lrdrNameToTextAnn vars+ docSeq . fmap appSep $ docLit docName : (docLit <$> names)+layoutLPatSyn name (InfixCon left right) = do+ leftDoc <- lrdrNameToTextAnn left+ docName <- lrdrNameToTextAnn name+ rightDoc <- lrdrNameToTextAnn right+ docSeq . fmap (appSep . docLit) $ [leftDoc, docName, rightDoc]+layoutLPatSyn name (RecCon recArgs) = do+ docName <- lrdrNameToTextAnn name+ args <- mapM (lrdrNameToTextAnn . recordPatSynSelectorId) recArgs+ docSeq+ . fmap docLit+ $ [docName, Text.pack " { "]+ <> intersperse (Text.pack ", ") args+ <> [Text.pack " }"]++-- | Helper method to get the where clause from of explicitly bidirectional+-- pattern synonyms+layoutPatSynWhere+ :: HsPatSynDir GhcPs -> ToBriDocM (Maybe [ToBriDocM BriDocNumbered])+layoutPatSynWhere hs = case hs of+ ExplicitBidirectional (MG _ (L _ lbinds) _) -> do+ binderDoc <- docLit $ Text.pack "="+ Just+ <$> mapM (docSharedWrapper $ layoutPatternBind Nothing binderDoc) lbinds+ _ -> pure Nothing++--------------------------------------------------------------------------------+-- TyClDecl+--------------------------------------------------------------------------------++layoutTyCl :: ToBriDoc TyClDecl+layoutTyCl ltycl@(L _loc tycl) = case tycl of+ SynDecl _ name vars fixity typ -> do+ let+ isInfix = case fixity of+ Prefix -> False+ Infix -> True+ -- hasTrailingParen <- hasAnnKeywordComment ltycl AnnCloseP+ -- let parenWrapper = if hasTrailingParen+ -- then appSep . docWrapNodeRest ltycl+ -- else id+ let wrapNodeRest = docWrapNodeRest ltycl+ docWrapNodePrior ltycl+ $ layoutSynDecl isInfix wrapNodeRest name (hsq_explicit vars) typ+ DataDecl _ext name tyVars _ dataDefn ->+ layoutDataDecl ltycl name tyVars dataDefn+ _ -> briDocByExactNoComment ltycl++layoutSynDecl+ :: Bool+ -> (ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered)+ -> Located (IdP GhcPs)+ -> [LHsTyVarBndr () GhcPs]+ -> LHsType GhcPs+ -> ToBriDocM BriDocNumbered+layoutSynDecl isInfix wrapNodeRest name vars typ = do+ nameStr <- lrdrNameToTextAnn name+ let+ lhs = appSep . wrapNodeRest $ if isInfix+ then do+ let (a : b : rest) = vars+ hasOwnParens <- hasAnnKeywordComment a AnnOpenP+ -- This isn't quite right, but does give syntactically valid results+ let needsParens = not (null rest) || hasOwnParens+ docSeq+ $ [docLit $ Text.pack "type", docSeparator]+ ++ [ docParenL | needsParens ]+ ++ [ layoutTyVarBndr False a+ , docSeparator+ , docLit nameStr+ , docSeparator+ , layoutTyVarBndr False b+ ]+ ++ [ docParenR | needsParens ]+ ++ fmap (layoutTyVarBndr True) rest+ else+ docSeq+ $ [ docLit $ Text.pack "type"+ , docSeparator+ , docWrapNode name $ docLit nameStr+ ]+ ++ fmap (layoutTyVarBndr True) vars+ sharedLhs <- docSharedWrapper id lhs+ typeDoc <- docSharedWrapper layoutType typ+ hasComments <- hasAnyCommentsConnected typ+ layoutLhsAndType hasComments sharedLhs "=" typeDoc++layoutTyVarBndr :: Bool -> ToBriDoc (HsTyVarBndr ())+layoutTyVarBndr needsSep lbndr@(L _ bndr) = do+ docWrapNodePrior lbndr $ case bndr of+ UserTyVar _ _ name -> do+ nameStr <- lrdrNameToTextAnn name+ docSeq $ [ docSeparator | needsSep ] ++ [docLit nameStr]+ KindedTyVar _ _ name kind -> do+ nameStr <- lrdrNameToTextAnn name+ docSeq+ $ [ docSeparator | needsSep ]+ ++ [ docLit $ Text.pack "("+ , appSep $ docLit nameStr+ , appSep . docLit $ Text.pack "::"+ , docForceSingleline $ layoutType kind+ , docLit $ Text.pack ")"+ ]+++--------------------------------------------------------------------------------+-- TyFamInstDecl+--------------------------------------------------------------------------------++++layoutTyFamInstDecl+ :: Data.Data.Data a+ => Bool+ -> Located a+ -> TyFamInstDecl GhcPs+ -> ToBriDocM BriDocNumbered+layoutTyFamInstDecl inClass outerNode tfid = do+ let+ FamEqn _ name bndrsMay pats _fixity typ = hsib_body $ tfid_eqn tfid+ -- bndrsMay isJust e.g. with+ -- type instance forall a . MyType (Maybe a) = Either () a+ innerNode = outerNode+ docWrapNodePrior outerNode $ do+ nameStr <- lrdrNameToTextAnn name+ needsParens <- hasAnnKeyword outerNode AnnOpenP+ let+ instanceDoc = if inClass+ then docLit $ Text.pack "type"+ else docSeq+ [appSep . docLit $ Text.pack "type", docLit $ Text.pack "instance"]+ makeForallDoc :: [LHsTyVarBndr () GhcPs] -> ToBriDocM BriDocNumbered+ makeForallDoc bndrs = do+ bndrDocs <- layoutTyVarBndrs bndrs+ docSeq+ ([docLit (Text.pack "forall")] ++ processTyVarBndrsSingleline bndrDocs+ )+ lhs =+ docWrapNode innerNode+ . docSeq+ $ [appSep instanceDoc]+ ++ [ makeForallDoc foralls | Just foralls <- [bndrsMay] ]+ ++ [ docParenL | needsParens ]+ ++ [appSep $ docWrapNode name $ docLit nameStr]+ ++ intersperse docSeparator (layoutHsTyPats pats)+ ++ [ docParenR | needsParens ]+ hasComments <-+ (||)+ <$> hasAnyRegularCommentsConnected outerNode+ <*> hasAnyRegularCommentsRest innerNode+ typeDoc <- docSharedWrapper layoutType typ+ layoutLhsAndType hasComments lhs "=" typeDoc+++layoutHsTyPats+ :: [HsArg (LHsType GhcPs) (LHsKind GhcPs)] -> [ToBriDocM BriDocNumbered]+layoutHsTyPats pats = pats <&> \case+ HsValArg tm -> layoutType tm+ HsTypeArg _l ty -> docSeq [docLit $ Text.pack "@", layoutType ty]+ -- we ignore the SourceLoc here.. this LPat not being (L _ Pat{}) change+ -- is a bit strange. Hopefully this does not ignore any important+ -- annotations.+ HsArgPar _l -> error "brittany internal error: HsArgPar{}"++--------------------------------------------------------------------------------+-- ClsInstDecl+--------------------------------------------------------------------------------++-- | Layout an @instance@ declaration+--+-- Layout signatures and bindings using the corresponding layouters from the+-- top-level. Layout the instance head, type family instances, and data family+-- instances using ExactPrint.+layoutClsInst :: ToBriDoc ClsInstDecl+layoutClsInst lcid@(L _ cid) = docLines+ [ layoutInstanceHead+ , docEnsureIndent BrIndentRegular+ $ docSetIndentLevel+ $ docSortedLines+ $ fmap layoutAndLocateSig (cid_sigs cid)+ ++ fmap layoutAndLocateBind (bagToList $ cid_binds cid)+ ++ fmap layoutAndLocateTyFamInsts (cid_tyfam_insts cid)+ ++ fmap layoutAndLocateDataFamInsts (cid_datafam_insts cid)+ ]+ where+ layoutInstanceHead :: ToBriDocM BriDocNumbered+ layoutInstanceHead =+ briDocByExactNoComment+ $ InstD NoExtField+ . ClsInstD NoExtField+ . removeChildren+ <$> lcid++ removeChildren :: ClsInstDecl GhcPs -> ClsInstDecl GhcPs+ removeChildren c = c+ { cid_binds = emptyBag+ , cid_sigs = []+ , cid_tyfam_insts = []+ , cid_datafam_insts = []+ }++ -- | Like 'docLines', but sorts the lines based on location+ docSortedLines+ :: [ToBriDocM (Located BriDocNumbered)] -> ToBriDocM BriDocNumbered+ docSortedLines l =+ allocateNode+ . BDFLines+ . fmap unLoc+ . List.sortOn (ExactPrint.rs . getLoc)+ =<< sequence l++ layoutAndLocateSig :: ToBriDocC (Sig GhcPs) (Located BriDocNumbered)+ layoutAndLocateSig lsig@(L loc _) = L loc <$> layoutSig lsig++ layoutAndLocateBind :: ToBriDocC (HsBind GhcPs) (Located BriDocNumbered)+ layoutAndLocateBind lbind@(L loc _) =+ L loc <$> (joinBinds =<< layoutBind lbind)++ joinBinds+ :: Either [BriDocNumbered] BriDocNumbered -> ToBriDocM BriDocNumbered+ joinBinds = \case+ Left ns -> docLines $ return <$> ns+ Right n -> return n++ layoutAndLocateTyFamInsts+ :: ToBriDocC (TyFamInstDecl GhcPs) (Located BriDocNumbered)+ layoutAndLocateTyFamInsts ltfid@(L loc tfid) =+ L loc <$> layoutTyFamInstDecl True ltfid tfid++ layoutAndLocateDataFamInsts+ :: ToBriDocC (DataFamInstDecl GhcPs) (Located BriDocNumbered)+ layoutAndLocateDataFamInsts ldfid@(L loc _) =+ L loc <$> layoutDataFamInstDecl ldfid++ -- | Send to ExactPrint then remove unecessary whitespace+ layoutDataFamInstDecl :: ToBriDoc DataFamInstDecl+ layoutDataFamInstDecl ldfid =+ fmap stripWhitespace <$> briDocByExactNoComment ldfid++ -- | ExactPrint adds indentation/newlines to @data@/@type@ declarations+ stripWhitespace :: BriDocF f -> BriDocF f+ stripWhitespace (BDFExternal ann anns b t) =+ BDFExternal ann anns b $ stripWhitespace' t+ stripWhitespace b = b++ -- | This fixes two issues of output coming from Exactprinting+ -- associated (data) type decls. Firstly we place the output into docLines,+ -- so one newline coming from Exactprint is superfluous, so we drop the+ -- first (empty) line. The second issue is Exactprint indents the first+ -- member in a strange fashion:+ --+ -- input:+ --+ -- > instance MyClass Int where+ -- > -- | This data is very important+ -- > data MyData = IntData+ -- > { intData :: String+ -- > , intData2 :: Int+ -- > }+ --+ -- output of just exactprinting the associated data type syntax node+ --+ -- >+ -- > -- | This data is very important+ -- > data MyData = IntData+ -- > { intData :: String+ -- > , intData2 :: Int+ -- > }+ --+ -- To fix this, we strip whitespace from the start of the comments and the+ -- first line of the declaration, stopping when we see "data" or "type" at+ -- the start of a line. I.e., this function yields+ --+ -- > -- | This data is very important+ -- > data MyData = IntData+ -- > { intData :: String+ -- > , intData2 :: Int+ -- > }+ --+ -- Downside apart from being a hacky and brittle fix is that this removes+ -- possible additional indentation from comments before the first member.+ --+ -- But the whole thing is just a temporary measure until brittany learns+ -- to layout data/type decls.+ stripWhitespace' :: Text -> Text+ stripWhitespace' t =+ Text.intercalate (Text.pack "\n") $ go $ List.drop 1 $ Text.lines t+ where+ go [] = []+ go (line1 : lineR) = case Text.stripStart line1 of+ st+ | isTypeOrData st -> st : lineR+ | otherwise -> st : go lineR+ isTypeOrData t' =+ (Text.pack "type" `Text.isPrefixOf` t')+ || (Text.pack "newtype" `Text.isPrefixOf` t')+ || (Text.pack "data" `Text.isPrefixOf` t')+++--------------------------------------------------------------------------------+-- Common Helpers+--------------------------------------------------------------------------------++layoutLhsAndType+ :: Bool+ -> ToBriDocM BriDocNumbered+ -> String+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+layoutLhsAndType hasComments lhs sep typeDoc = do+ runFilteredAlternative $ do+ -- (separators probably are "=" or "::")+ -- lhs = type+ -- lhs :: type+ addAlternativeCond (not hasComments) $ docSeq+ [lhs, docSeparator, docLitS sep, docSeparator, docForceSingleline typeDoc]+ -- lhs+ -- :: typeA+ -- -> typeB+ -- lhs+ -- = typeA+ -- -> typeB+ addAlternative $ docAddBaseY BrIndentRegular $ docPar lhs $ docCols+ ColTyOpPrefix+ [ appSep $ docLitS sep+ , docAddBaseY (BrIndentSpecial (length sep + 1)) typeDoc+ ]
+ source/library/Brittany/Internal/Layouters/Expr.hs view
@@ -0,0 +1,1086 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Expr where++import qualified Data.Data+import qualified Data.Semigroup as Semigroup+import qualified Data.Sequence as Seq+import qualified Data.Text as Text+import GHC (AnnKeywordId(..), GenLocated(L), RdrName(..), SrcSpan)+import qualified GHC.Data.FastString as FastString+import GHC.Hs+import qualified GHC.OldList as List+import GHC.Types.Basic+import GHC.Types.Name+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.Decl+import Brittany.Internal.Layouters.Pattern+import Brittany.Internal.Layouters.Stmt+import Brittany.Internal.Layouters.Type+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils++++layoutExpr :: ToBriDoc HsExpr+layoutExpr lexpr@(L _ expr) = do+ indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack+ let allowFreeIndent = indentPolicy == IndentPolicyFree+ docWrapNode lexpr $ case expr of+ HsVar _ vname -> do+ docLit =<< lrdrNameToTextAnn vname+ HsUnboundVar _ oname -> docLit $ Text.pack $ occNameString oname+ HsRecFld{} -> do+ -- TODO+ briDocByExactInlineOnly "HsRecFld" lexpr+ HsOverLabel _ext _reboundFromLabel name ->+ let label = FastString.unpackFS name in docLit . Text.pack $ '#' : label+ HsIPVar _ext (HsIPName name) ->+ let label = FastString.unpackFS name in docLit . Text.pack $ '?' : label+ HsOverLit _ olit -> do+ allocateNode $ overLitValBriDoc $ ol_val olit+ HsLit _ lit -> do+ allocateNode $ litBriDoc lit+ HsLam _ (MG _ (L _ [lmatch@(L _ match)]) _)+ | pats <- m_pats match+ , GRHSs _ [lgrhs] llocals <- m_grhss match+ , L _ EmptyLocalBinds{} <- llocals+ , L _ (GRHS _ [] body) <- lgrhs+ -> do+ patDocs <- zip (True : repeat False) pats `forM` \(isFirst, p) ->+ fmap return $ do+ -- this code could be as simple as `colsWrapPat =<< layoutPat p`+ -- if it was not for the following two cases:+ -- \ !x -> x+ -- \ ~x -> x+ -- These make it necessary to special-case an additional separator.+ -- (TODO: we create a BDCols here, but then make it ineffective+ -- by wrapping it in docSeq below. We _could_ add alignments for+ -- stuff like lists-of-lambdas. Nothing terribly important..)+ let+ shouldPrefixSeparator = case p of+ L _ LazyPat{} -> isFirst+ L _ BangPat{} -> isFirst+ _ -> False+ patDocSeq <- layoutPat p+ fixed <- case Seq.viewl patDocSeq of+ p1 Seq.:< pr | shouldPrefixSeparator -> do+ p1' <- docSeq [docSeparator, pure p1]+ pure (p1' Seq.<| pr)+ _ -> pure patDocSeq+ colsWrapPat fixed+ bodyDoc <-+ docAddBaseY BrIndentRegular <$> docSharedWrapper layoutExpr body+ let+ funcPatternPartLine = docCols+ ColCasePattern+ (patDocs <&> (\p -> docSeq [docForceSingleline p, docSeparator]))+ docAlt+ [ -- single line+ docSeq+ [ docLit $ Text.pack "\\"+ , docWrapNode lmatch $ docForceSingleline funcPatternPartLine+ , appSep $ docLit $ Text.pack "->"+ , docWrapNode lgrhs $ docForceSingleline bodyDoc+ ]+ -- double line+ , docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docSeq+ [ docLit $ Text.pack "\\"+ , docWrapNode lmatch $ appSep $ docForceSingleline+ funcPatternPartLine+ , docLit $ Text.pack "->"+ ]+ )+ (docWrapNode lgrhs $ docForceSingleline bodyDoc)+ -- wrapped par spacing+ , docSetParSpacing $ docSeq+ [ docLit $ Text.pack "\\"+ , docWrapNode lmatch $ docForceSingleline funcPatternPartLine+ , appSep $ docLit $ Text.pack "->"+ , docWrapNode lgrhs $ docForceParSpacing bodyDoc+ ]+ -- conservative+ , docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docSeq+ [ docLit $ Text.pack "\\"+ , docWrapNode lmatch $ appSep $ docForceSingleline+ funcPatternPartLine+ , docLit $ Text.pack "->"+ ]+ )+ (docWrapNode lgrhs $ docNonBottomSpacing bodyDoc)+ ]+ HsLam{} -> unknownNodeError "HsLam too complex" lexpr+ HsLamCase _ (MG _ (L _ []) _) -> do+ docSetParSpacing+ $ docAddBaseY BrIndentRegular+ $ (docLit $ Text.pack "\\case {}")+ HsLamCase _ (MG _ lmatches@(L _ matches) _) -> do+ binderDoc <- docLit $ Text.pack "->"+ funcPatDocs <-+ docWrapNode lmatches+ $ layoutPatternBind Nothing binderDoc+ `mapM` matches+ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "\\case")+ (docSetBaseAndIndent+ $ docNonBottomSpacing+ $ docLines+ $ return+ <$> funcPatDocs+ )+ HsApp _ exp1@(L _ HsApp{}) exp2 -> do+ let+ gather+ :: [LHsExpr GhcPs]+ -> LHsExpr GhcPs+ -> (LHsExpr GhcPs, [LHsExpr GhcPs])+ gather list = \case+ L _ (HsApp _ l r) -> gather (r : list) l+ x -> (x, list)+ let (headE, paramEs) = gather [exp2] exp1+ let+ colsOrSequence = case headE of+ L _ (HsVar _ (L _ (Unqual occname))) ->+ docCols (ColApp $ Text.pack $ occNameString occname)+ _ -> docSeq+ headDoc <- docSharedWrapper layoutExpr headE+ paramDocs <- docSharedWrapper layoutExpr `mapM` paramEs+ hasComments <- hasAnyCommentsConnected exp2+ runFilteredAlternative $ do+ -- foo x y+ addAlternativeCond (not hasComments)+ $ colsOrSequence+ $ appSep (docForceSingleline headDoc)+ : spacifyDocs (docForceSingleline <$> paramDocs)+ -- foo x+ -- y+ addAlternativeCond allowFreeIndent $ docSeq+ [ appSep (docForceSingleline headDoc)+ , docSetBaseY+ $ docAddBaseY BrIndentRegular+ $ docLines+ $ docForceSingleline+ <$> paramDocs+ ]+ -- foo+ -- x+ -- y+ addAlternative $ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docForceSingleline headDoc)+ (docNonBottomSpacing $ docLines paramDocs)+ -- ( multi+ -- line+ -- function+ -- )+ -- x+ -- y+ addAlternative $ docAddBaseY BrIndentRegular $ docPar+ headDoc+ (docNonBottomSpacing $ docLines paramDocs)+ HsApp _ exp1 exp2 -> do+ -- TODO: if expDoc1 is some literal, we may want to create a docCols here.+ expDoc1 <- docSharedWrapper layoutExpr exp1+ expDoc2 <- docSharedWrapper layoutExpr exp2+ docAlt+ [ -- func arg+ docSeq+ [appSep $ docForceSingleline expDoc1, docForceSingleline expDoc2]+ , -- func argline1+ -- arglines+ -- e.g.+ -- func Abc+ -- { member1 = True+ -- , member2 = 13+ -- }+ docSetParSpacing -- this is most likely superfluous because+ -- this is a sequence of a one-line and a par-space+ -- anyways, so it is _always_ par-spaced.+ $ docAddBaseY BrIndentRegular+ $ docSeq+ [appSep $ docForceSingleline expDoc1, docForceParSpacing expDoc2]+ , -- func+ -- arg+ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docForceSingleline expDoc1)+ (docNonBottomSpacing expDoc2)+ , -- fu+ -- nc+ -- ar+ -- gument+ docAddBaseY BrIndentRegular $ docPar expDoc1 expDoc2+ ]+ HsAppType _ exp1 (HsWC _ ty1) -> do+ t <- docSharedWrapper layoutType ty1+ e <- docSharedWrapper layoutExpr exp1+ docAlt+ [ docSeq+ [ docForceSingleline e+ , docSeparator+ , docLit $ Text.pack "@"+ , docForceSingleline t+ ]+ , docPar e (docSeq [docLit $ Text.pack "@", t])+ ]+ OpApp _ expLeft@(L _ OpApp{}) expOp expRight -> do+ let+ gather+ :: [(LHsExpr GhcPs, LHsExpr GhcPs)]+ -> LHsExpr GhcPs+ -> (LHsExpr GhcPs, [(LHsExpr GhcPs, LHsExpr GhcPs)])+ gather opExprList = \case+ (L _ (OpApp _ l1 op1 r1)) -> gather ((op1, r1) : opExprList) l1+ final -> (final, opExprList)+ (leftOperand, appList) = gather [] expLeft+ leftOperandDoc <- docSharedWrapper layoutExpr leftOperand+ appListDocs <- appList `forM` \(x, y) ->+ [ (xD, yD)+ | xD <- docSharedWrapper layoutExpr x+ , yD <- docSharedWrapper layoutExpr y+ ]+ opLastDoc <- docSharedWrapper layoutExpr expOp+ expLastDoc <- docSharedWrapper layoutExpr expRight+ allowSinglelinePar <- do+ hasComLeft <- hasAnyCommentsConnected expLeft+ hasComOp <- hasAnyCommentsConnected expOp+ pure $ not hasComLeft && not hasComOp+ let+ allowPar = case (expOp, expRight) of+ (L _ (HsVar _ (L _ (Unqual occname))), _)+ | occNameString occname == "$" -> True+ (_, L _ (HsApp _ _ (L _ HsVar{}))) -> False+ _ -> True+ runFilteredAlternative $ do+ -- > one + two + three+ -- or+ -- > one + two + case x of+ -- > _ -> three+ addAlternativeCond allowSinglelinePar $ docSeq+ [ appSep $ docForceSingleline leftOperandDoc+ , docSeq $ appListDocs <&> \(od, ed) -> docSeq+ [appSep $ docForceSingleline od, appSep $ docForceSingleline ed]+ , appSep $ docForceSingleline opLastDoc+ , (if allowPar then docForceParSpacing else docForceSingleline)+ expLastDoc+ ]+ -- this case rather leads to some unfortunate layouting than to anything+ -- useful; disabling for now. (it interfers with cols stuff.)+ -- addAlternative+ -- $ docSetBaseY+ -- $ docPar+ -- leftOperandDoc+ -- ( docLines+ -- $ (appListDocs <&> \(od, ed) -> docCols ColOpPrefix [appSep od, docSetBaseY ed])+ -- ++ [docCols ColOpPrefix [appSep opLastDoc, docSetBaseY expLastDoc]]+ -- )+ -- > one+ -- > + two+ -- > + three+ addAlternative $ docPar+ leftOperandDoc+ (docLines+ $ (appListDocs <&> \(od, ed) ->+ docCols ColOpPrefix [appSep od, docSetBaseY ed]+ )+ ++ [docCols ColOpPrefix [appSep opLastDoc, docSetBaseY expLastDoc]]+ )+ OpApp _ expLeft expOp expRight -> do+ expDocLeft <- docSharedWrapper layoutExpr expLeft+ expDocOp <- docSharedWrapper layoutExpr expOp+ expDocRight <- docSharedWrapper layoutExpr expRight+ let+ allowPar = case (expOp, expRight) of+ (L _ (HsVar _ (L _ (Unqual occname))), _)+ | occNameString occname == "$" -> True+ (_, L _ (HsApp _ _ (L _ HsVar{}))) -> False+ _ -> True+ let+ leftIsDoBlock = case expLeft of+ L _ HsDo{} -> True+ _ -> False+ runFilteredAlternative $ do+ -- one-line+ addAlternative $ docSeq+ [ appSep $ docForceSingleline expDocLeft+ , appSep $ docForceSingleline expDocOp+ , docForceSingleline expDocRight+ ]+ -- -- line + freely indented block for right expression+ -- addAlternative+ -- $ docSeq+ -- [ appSep $ docForceSingleline expDocLeft+ -- , appSep $ docForceSingleline expDocOp+ -- , docSetBaseY $ docAddBaseY BrIndentRegular expDocRight+ -- ]+ -- two-line+ addAlternative $ do+ let+ expDocOpAndRight = docForceSingleline $ docCols+ ColOpPrefix+ [appSep $ expDocOp, docSetBaseY expDocRight]+ if leftIsDoBlock+ then docLines [expDocLeft, expDocOpAndRight]+ else docAddBaseY BrIndentRegular+ $ docPar expDocLeft expDocOpAndRight+ -- TODO: in both cases, we don't force expDocLeft to be+ -- single-line, which has certain.. interesting consequences.+ -- At least, the "two-line" label is not entirely+ -- accurate.+ -- one-line + par+ addAlternativeCond allowPar $ docSeq+ [ appSep $ docForceSingleline expDocLeft+ , appSep $ docForceSingleline expDocOp+ , docForceParSpacing expDocRight+ ]+ -- more lines+ addAlternative $ do+ let+ expDocOpAndRight =+ docCols ColOpPrefix [appSep expDocOp, docSetBaseY expDocRight]+ if leftIsDoBlock+ then docLines [expDocLeft, expDocOpAndRight]+ else docAddBaseY BrIndentRegular+ $ docPar expDocLeft expDocOpAndRight+ NegApp _ op _ -> do+ opDoc <- docSharedWrapper layoutExpr op+ docSeq [docLit $ Text.pack "-", opDoc]+ HsPar _ innerExp -> do+ innerExpDoc <- docSharedWrapper (docWrapNode lexpr . layoutExpr) innerExp+ docAlt+ [ docSeq+ [ docLit $ Text.pack "("+ , docForceSingleline innerExpDoc+ , docLit $ Text.pack ")"+ ]+ , docSetBaseY $ docLines+ [ docCols+ ColOpPrefix+ [ docLit $ Text.pack "("+ , docAddBaseY (BrIndentSpecial 2) innerExpDoc+ ]+ , docLit $ Text.pack ")"+ ]+ ]+ SectionL _ left op -> do -- TODO: add to testsuite+ leftDoc <- docSharedWrapper layoutExpr left+ opDoc <- docSharedWrapper layoutExpr op+ docSeq [leftDoc, docSeparator, opDoc]+ SectionR _ op right -> do -- TODO: add to testsuite+ opDoc <- docSharedWrapper layoutExpr op+ rightDoc <- docSharedWrapper layoutExpr right+ docSeq [opDoc, docSeparator, rightDoc]+ ExplicitTuple _ args boxity -> do+ let+ argExprs = args <&> \arg -> case arg of+ (L _ (Present _ e)) -> (arg, Just e)+ (L _ (Missing NoExtField)) -> (arg, Nothing)+ argDocs <- forM argExprs $ docSharedWrapper $ \(arg, exprM) ->+ docWrapNode arg $ maybe docEmpty layoutExpr exprM+ hasComments <-+ orM+ (hasCommentsBetween lexpr AnnOpenP AnnCloseP+ : map hasAnyCommentsBelow args+ )+ let+ (openLit, closeLit) = case boxity of+ Boxed -> (docLit $ Text.pack "(", docLit $ Text.pack ")")+ Unboxed -> (docParenHashLSep, docParenHashRSep)+ case splitFirstLast argDocs of+ FirstLastEmpty ->+ docSeq [openLit, docNodeAnnKW lexpr (Just AnnOpenP) closeLit]+ FirstLastSingleton e -> docAlt+ [ docCols+ ColTuple+ [ openLit+ , docNodeAnnKW lexpr (Just AnnOpenP) $ docForceSingleline e+ , closeLit+ ]+ , docSetBaseY $ docLines+ [ docSeq+ [ openLit+ , docNodeAnnKW lexpr (Just AnnOpenP) $ docForceSingleline e+ ]+ , closeLit+ ]+ ]+ FirstLast e1 ems eN -> runFilteredAlternative $ do+ addAlternativeCond (not hasComments)+ $ docCols ColTuple+ $ [docSeq [openLit, docForceSingleline e1]]+ ++ (ems <&> \e -> docSeq [docCommaSep, docForceSingleline e])+ ++ [ docSeq+ [ docCommaSep+ , docNodeAnnKW lexpr (Just AnnOpenP) (docForceSingleline eN)+ , closeLit+ ]+ ]+ addAlternative+ $ let+ start = docCols ColTuples [appSep openLit, e1]+ linesM = ems <&> \d -> docCols ColTuples [docCommaSep, d]+ lineN = docCols+ ColTuples+ [docCommaSep, docNodeAnnKW lexpr (Just AnnOpenP) eN]+ end = closeLit+ in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN, end]+ HsCase _ cExp (MG _ (L _ []) _) -> do+ cExpDoc <- docSharedWrapper layoutExpr cExp+ docAlt+ [ docAddBaseY BrIndentRegular $ docSeq+ [ appSep $ docLit $ Text.pack "case"+ , appSep $ docForceSingleline cExpDoc+ , docLit $ Text.pack "of {}"+ ]+ , docPar+ (docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "case") cExpDoc+ )+ (docLit $ Text.pack "of {}")+ ]+ HsCase _ cExp (MG _ lmatches@(L _ matches) _) -> do+ cExpDoc <- docSharedWrapper layoutExpr cExp+ binderDoc <- docLit $ Text.pack "->"+ funcPatDocs <-+ docWrapNode lmatches+ $ layoutPatternBind Nothing binderDoc+ `mapM` matches+ docAlt+ [ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docSeq+ [ appSep $ docLit $ Text.pack "case"+ , appSep $ docForceSingleline cExpDoc+ , docLit $ Text.pack "of"+ ]+ )+ (docSetBaseAndIndent+ $ docNonBottomSpacing+ $ docLines+ $ return+ <$> funcPatDocs+ )+ , docPar+ (docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "case") cExpDoc+ )+ (docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "of")+ (docSetBaseAndIndent+ $ docNonBottomSpacing+ $ docLines+ $ return+ <$> funcPatDocs+ )+ )+ ]+ HsIf _ ifExpr thenExpr elseExpr -> do+ ifExprDoc <- docSharedWrapper layoutExpr ifExpr+ thenExprDoc <- docSharedWrapper layoutExpr thenExpr+ elseExprDoc <- docSharedWrapper layoutExpr elseExpr+ hasComments <- hasAnyCommentsBelow lexpr+ let+ maySpecialIndent = case indentPolicy of+ IndentPolicyLeft -> BrIndentRegular+ IndentPolicyMultiple -> BrIndentRegular+ IndentPolicyFree -> BrIndentSpecial 3+ -- TODO: some of the alternatives (especially last and last-but-one)+ -- overlap.+ docSetIndentLevel $ runFilteredAlternative $ do+ -- if _ then _ else _+ addAlternativeCond (not hasComments) $ docSeq+ [ appSep $ docLit $ Text.pack "if"+ , appSep $ docForceSingleline ifExprDoc+ , appSep $ docLit $ Text.pack "then"+ , appSep $ docForceSingleline thenExprDoc+ , appSep $ docLit $ Text.pack "else"+ , docForceSingleline elseExprDoc+ ]+ -- either+ -- if expr+ -- then foo+ -- bar+ -- else foo+ -- bar+ -- or+ -- if expr+ -- then+ -- stuff+ -- else+ -- stuff+ -- note that this has par-spacing+ addAlternative $ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docSeq+ [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"+ , docNodeAnnKW lexpr (Just AnnIf) $ docForceSingleline ifExprDoc+ ]+ )+ (docLines+ [ docAddBaseY BrIndentRegular+ $ docNodeAnnKW lexpr (Just AnnThen)+ $ docNonBottomSpacing+ $ docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack "then"+ , docForceParSpacing thenExprDoc+ ]+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "then") thenExprDoc+ ]+ , docAddBaseY BrIndentRegular $ docNonBottomSpacing $ docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack "else"+ , docForceParSpacing elseExprDoc+ ]+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "else") elseExprDoc+ ]+ ]+ )+ -- either+ -- if multi+ -- line+ -- condition+ -- then foo+ -- bar+ -- else foo+ -- bar+ -- or+ -- if multi+ -- line+ -- condition+ -- then+ -- stuff+ -- else+ -- stuff+ -- note that this does _not_ have par-spacing+ addAlternative $ docAddBaseY BrIndentRegular $ docPar+ (docAddBaseY maySpecialIndent $ docSeq+ [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"+ , docNodeAnnKW lexpr (Just AnnIf) $ ifExprDoc+ ]+ )+ (docLines+ [ docAddBaseY BrIndentRegular+ $ docNodeAnnKW lexpr (Just AnnThen)+ $ docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack "then"+ , docForceParSpacing thenExprDoc+ ]+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "then") thenExprDoc+ ]+ , docAddBaseY BrIndentRegular $ docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack "else"+ , docForceParSpacing elseExprDoc+ ]+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "else") elseExprDoc+ ]+ ]+ )+ addAlternative $ docSetBaseY $ docLines+ [ docAddBaseY maySpecialIndent $ docSeq+ [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"+ , docNodeAnnKW lexpr (Just AnnIf) $ ifExprDoc+ ]+ , docNodeAnnKW lexpr (Just AnnThen)+ $ docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "then") thenExprDoc+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "else") elseExprDoc+ ]+ HsMultiIf _ cases -> do+ clauseDocs <- cases `forM` layoutGrhs+ binderDoc <- docLit $ Text.pack "->"+ hasComments <- hasAnyCommentsBelow lexpr+ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "if")+ (layoutPatternBindFinal+ Nothing+ binderDoc+ Nothing+ clauseDocs+ Nothing+ hasComments+ )+ HsLet _ binds exp1 -> do+ expDoc1 <- docSharedWrapper layoutExpr exp1+ -- We jump through some ugly hoops here to ensure proper sharing.+ hasComments <- hasAnyCommentsBelow lexpr+ mBindDocs <- fmap (fmap pure) <$> layoutLocalBinds binds+ let+ ifIndentFreeElse :: a -> a -> a+ ifIndentFreeElse x y = case indentPolicy of+ IndentPolicyLeft -> y+ IndentPolicyMultiple -> y+ IndentPolicyFree -> x+ -- this `docSetBaseAndIndent` might seem out of place (especially the+ -- Indent part; setBase is necessary due to the use of docLines below),+ -- but is here due to ghc-exactprint's DP handling of "let" in+ -- particular.+ -- Just pushing another indentation level is a straightforward approach+ -- to making brittany idempotent, even though the result is non-optimal+ -- if "let" is moved horizontally as part of the transformation, as the+ -- comments before the first let item are moved horizontally with it.+ docSetBaseAndIndent $ case mBindDocs of+ Just [bindDoc] -> runFilteredAlternative $ do+ addAlternativeCond (not hasComments) $ docSeq+ [ appSep $ docLit $ Text.pack "let"+ , docNodeAnnKW lexpr (Just AnnLet) $ appSep $ docForceSingleline+ bindDoc+ , appSep $ docLit $ Text.pack "in"+ , docForceSingleline expDoc1+ ]+ addAlternative $ docLines+ [ docNodeAnnKW lexpr (Just AnnLet) $ docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack "let"+ , ifIndentFreeElse docSetBaseAndIndent docForceSingleline+ $ bindDoc+ ]+ , docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "let")+ (docSetBaseAndIndent bindDoc)+ ]+ , docAlt+ [ docSeq+ [ appSep $ docLit $ Text.pack $ ifIndentFreeElse "in " "in"+ , ifIndentFreeElse+ docSetBaseAndIndent+ docForceSingleline+ expDoc1+ ]+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "in") (docSetBaseY expDoc1)+ ]+ ]+ Just bindDocs@(_ : _) -> runFilteredAlternative $ do+ --either+ -- let+ -- a = b+ -- c = d+ -- in foo+ -- bar+ -- baz+ --or+ -- let+ -- a = b+ -- c = d+ -- in+ -- fooooooooooooooooooo+ let+ noHangingBinds =+ [ docNonBottomSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "let")+ (docSetBaseAndIndent $ docLines bindDocs)+ , docSeq+ [ docLit $ Text.pack "in "+ , docAddBaseY BrIndentRegular $ docForceParSpacing expDoc1+ ]+ ]+ addAlternative $ case indentPolicy of+ IndentPolicyLeft -> docLines noHangingBinds+ IndentPolicyMultiple -> docLines noHangingBinds+ IndentPolicyFree -> docLines+ [ docNodeAnnKW lexpr (Just AnnLet) $ docSeq+ [ appSep $ docLit $ Text.pack "let"+ , docSetBaseAndIndent $ docLines bindDocs+ ]+ , docSeq [appSep $ docLit $ Text.pack "in ", docSetBaseY expDoc1]+ ]+ addAlternative $ docLines+ [ docNodeAnnKW lexpr (Just AnnLet)+ $ docAddBaseY BrIndentRegular+ $ docPar+ (docLit $ Text.pack "let")+ (docSetBaseAndIndent $ docLines $ bindDocs)+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "in") (docSetBaseY $ expDoc1)+ ]+ _ -> docSeq [appSep $ docLit $ Text.pack "let in", expDoc1]+ -- docSeq [appSep $ docLit "let in", expDoc1]+ HsDo _ stmtCtx (L _ stmts) -> case stmtCtx of+ DoExpr _ -> do+ stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts+ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "do")+ (docSetBaseAndIndent $ docNonBottomSpacing $ docLines stmtDocs)+ MDoExpr _ -> do+ stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts+ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "mdo")+ (docSetBaseAndIndent $ docNonBottomSpacing $ docLines stmtDocs)+ x+ | case x of+ ListComp -> True+ MonadComp -> True+ _ -> False+ -> do+ stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts+ hasComments <- hasAnyCommentsBelow lexpr+ runFilteredAlternative $ do+ addAlternativeCond (not hasComments) $ docSeq+ [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "["+ , docNodeAnnKW lexpr (Just AnnOpenS)+ $ appSep+ $ docForceSingleline+ $ List.last stmtDocs+ , appSep $ docLit $ Text.pack "|"+ , docSeq+ $ List.intersperse docCommaSep+ $ docForceSingleline+ <$> List.init stmtDocs+ , docLit $ Text.pack " ]"+ ]+ addAlternative+ $ let+ start = docCols+ ColListComp+ [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack+ "["+ , docSetBaseY+ $ docNodeAnnKW lexpr (Just AnnOpenS)+ $ List.last stmtDocs+ ]+ (s1 : sM) = List.init stmtDocs+ line1 =+ docCols ColListComp [appSep $ docLit $ Text.pack "|", s1]+ lineM = sM <&> \d -> docCols ColListComp [docCommaSep, d]+ end = docLit $ Text.pack "]"+ in docSetBaseY $ docLines $ [start, line1] ++ lineM ++ [end]+ _ -> do+ -- TODO+ unknownNodeError "HsDo{} unknown stmtCtx" lexpr+ ExplicitList _ _ elems@(_ : _) -> do+ elemDocs <- elems `forM` docSharedWrapper layoutExpr+ hasComments <- hasAnyCommentsBelow lexpr+ case splitFirstLast elemDocs of+ FirstLastEmpty -> docSeq+ [ docLit $ Text.pack "["+ , docNodeAnnKW lexpr (Just AnnOpenS) $ docLit $ Text.pack "]"+ ]+ FirstLastSingleton e -> docAlt+ [ docSeq+ [ docLit $ Text.pack "["+ , docNodeAnnKW lexpr (Just AnnOpenS) $ docForceSingleline e+ , docLit $ Text.pack "]"+ ]+ , docSetBaseY $ docLines+ [ docSeq+ [ docLit $ Text.pack "["+ , docSeparator+ , docSetBaseY $ docNodeAnnKW lexpr (Just AnnOpenS) e+ ]+ , docLit $ Text.pack "]"+ ]+ ]+ FirstLast e1 ems eN -> runFilteredAlternative $ do+ addAlternativeCond (not hasComments)+ $ docSeq+ $ [docLit $ Text.pack "["]+ ++ List.intersperse+ docCommaSep+ (docForceSingleline+ <$> (e1 : ems ++ [docNodeAnnKW lexpr (Just AnnOpenS) eN])+ )+ ++ [docLit $ Text.pack "]"]+ addAlternative+ $ let+ start = docCols ColList [appSep $ docLit $ Text.pack "[", e1]+ linesM = ems <&> \d -> docCols ColList [docCommaSep, d]+ lineN = docCols+ ColList+ [docCommaSep, docNodeAnnKW lexpr (Just AnnOpenS) eN]+ end = docLit $ Text.pack "]"+ in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN] ++ [end]+ ExplicitList _ _ [] -> docLit $ Text.pack "[]"+ RecordCon _ lname fields -> case fields of+ HsRecFields fs Nothing -> do+ let nameDoc = docWrapNode lname $ docLit $ lrdrNameToText lname+ rFs <-+ fs `forM` \lfield@(L _ (HsRecField (L _ fieldOcc) rFExpr pun)) -> do+ let FieldOcc _ lnameF = fieldOcc+ rFExpDoc <- if pun+ then return Nothing+ else Just <$> docSharedWrapper layoutExpr rFExpr+ return $ (lfield, lrdrNameToText lnameF, rFExpDoc)+ recordExpression False indentPolicy lexpr nameDoc rFs+ HsRecFields [] (Just (L _ 0)) -> do+ let t = lrdrNameToText lname+ docWrapNode lname $ docLit $ t <> Text.pack " { .. }"+ HsRecFields fs@(_ : _) (Just (L _ dotdoti)) | dotdoti == length fs -> do+ let nameDoc = docWrapNode lname $ docLit $ lrdrNameToText lname+ fieldDocs <-+ fs `forM` \fieldl@(L _ (HsRecField (L _ fieldOcc) fExpr pun)) -> do+ let FieldOcc _ lnameF = fieldOcc+ fExpDoc <- if pun+ then return Nothing+ else Just <$> docSharedWrapper layoutExpr fExpr+ return (fieldl, lrdrNameToText lnameF, fExpDoc)+ recordExpression True indentPolicy lexpr nameDoc fieldDocs+ _ -> unknownNodeError "RecordCon with puns" lexpr+ RecordUpd _ rExpr fields -> do+ rExprDoc <- docSharedWrapper layoutExpr rExpr+ rFs <-+ fields `forM` \lfield@(L _ (HsRecField (L _ ambName) rFExpr pun)) -> do+ rFExpDoc <- if pun+ then return Nothing+ else Just <$> docSharedWrapper layoutExpr rFExpr+ return $ case ambName of+ Unambiguous _ n -> (lfield, lrdrNameToText n, rFExpDoc)+ Ambiguous _ n -> (lfield, lrdrNameToText n, rFExpDoc)+ recordExpression False indentPolicy lexpr rExprDoc rFs+ ExprWithTySig _ exp1 (HsWC _ (HsIB _ typ1)) -> do+ expDoc <- docSharedWrapper layoutExpr exp1+ typDoc <- docSharedWrapper layoutType typ1+ docSeq [appSep expDoc, appSep $ docLit $ Text.pack "::", typDoc]+ ArithSeq _ Nothing info -> case info of+ From e1 -> do+ e1Doc <- docSharedWrapper layoutExpr e1+ docSeq+ [ docLit $ Text.pack "["+ , appSep $ docForceSingleline e1Doc+ , docLit $ Text.pack "..]"+ ]+ FromThen e1 e2 -> do+ e1Doc <- docSharedWrapper layoutExpr e1+ e2Doc <- docSharedWrapper layoutExpr e2+ docSeq+ [ docLit $ Text.pack "["+ , docForceSingleline e1Doc+ , appSep $ docLit $ Text.pack ","+ , appSep $ docForceSingleline e2Doc+ , docLit $ Text.pack "..]"+ ]+ FromTo e1 eN -> do+ e1Doc <- docSharedWrapper layoutExpr e1+ eNDoc <- docSharedWrapper layoutExpr eN+ docSeq+ [ docLit $ Text.pack "["+ , appSep $ docForceSingleline e1Doc+ , appSep $ docLit $ Text.pack ".."+ , docForceSingleline eNDoc+ , docLit $ Text.pack "]"+ ]+ FromThenTo e1 e2 eN -> do+ e1Doc <- docSharedWrapper layoutExpr e1+ e2Doc <- docSharedWrapper layoutExpr e2+ eNDoc <- docSharedWrapper layoutExpr eN+ docSeq+ [ docLit $ Text.pack "["+ , docForceSingleline e1Doc+ , appSep $ docLit $ Text.pack ","+ , appSep $ docForceSingleline e2Doc+ , appSep $ docLit $ Text.pack ".."+ , docForceSingleline eNDoc+ , docLit $ Text.pack "]"+ ]+ ArithSeq{} -> briDocByExactInlineOnly "ArithSeq" lexpr+ HsBracket{} -> do+ -- TODO+ briDocByExactInlineOnly "HsBracket{}" lexpr+ HsRnBracketOut{} -> do+ -- TODO+ briDocByExactInlineOnly "HsRnBracketOut{}" lexpr+ HsTcBracketOut{} -> do+ -- TODO+ briDocByExactInlineOnly "HsTcBracketOut{}" lexpr+ HsSpliceE _ (HsQuasiQuote _ _ quoter _loc content) -> do+ allocateNode $ BDFPlain+ (Text.pack+ $ "["+ ++ showOutputable quoter+ ++ "|"+ ++ showOutputable content+ ++ "|]"+ )+ HsSpliceE{} -> do+ -- TODO+ briDocByExactInlineOnly "HsSpliceE{}" lexpr+ HsProc{} -> do+ -- TODO+ briDocByExactInlineOnly "HsProc{}" lexpr+ HsStatic{} -> do+ -- TODO+ briDocByExactInlineOnly "HsStatic{}" lexpr+ HsTick{} -> do+ -- TODO+ briDocByExactInlineOnly "HsTick{}" lexpr+ HsBinTick{} -> do+ -- TODO+ briDocByExactInlineOnly "HsBinTick{}" lexpr+ HsConLikeOut{} -> do+ -- TODO+ briDocByExactInlineOnly "HsWrap{}" lexpr+ ExplicitSum{} -> do+ -- TODO+ briDocByExactInlineOnly "ExplicitSum{}" lexpr+ HsPragE{} -> do+ -- TODO+ briDocByExactInlineOnly "HsPragE{}" lexpr++recordExpression+ :: (Data.Data.Data lExpr, Data.Data.Data name)+ => Bool+ -> IndentPolicy+ -> GenLocated SrcSpan lExpr+ -> ToBriDocM BriDocNumbered+ -> [ ( GenLocated SrcSpan name+ , Text+ , Maybe (ToBriDocM BriDocNumbered)+ )+ ]+ -> ToBriDocM BriDocNumbered+recordExpression False _ lexpr nameDoc [] = docSeq+ [ docNodeAnnKW lexpr (Just AnnOpenC)+ $ docSeq [nameDoc, docLit $ Text.pack "{"]+ , docLit $ Text.pack "}"+ ]+recordExpression True _ lexpr nameDoc [] = docSeq -- this case might still be incomplete, and is probably not used+ -- atm anyway.+ [ docNodeAnnKW lexpr (Just AnnOpenC)+ $ docSeq [nameDoc, docLit $ Text.pack "{"]+ , docLit $ Text.pack " .. }"+ ]+recordExpression dotdot indentPolicy lexpr nameDoc rFs@(rF1 : rFr) = do+ let (rF1f, rF1n, rF1e) = rF1+ runFilteredAlternative $ do+ -- container { fieldA = blub, fieldB = blub }+ addAlternative $ docSeq+ [ docNodeAnnKW lexpr Nothing $ appSep $ docForceSingleline nameDoc+ , appSep $ docLit $ Text.pack "{"+ , docSeq $ List.intersperse docCommaSep $ rFs <&> \case+ (lfield, fieldStr, Just fieldDoc) -> docWrapNode lfield $ docSeq+ [ appSep $ docLit fieldStr+ , appSep $ docLit $ Text.pack "="+ , docForceSingleline fieldDoc+ ]+ (lfield, fieldStr, Nothing) -> docWrapNode lfield $ docLit fieldStr+ , if dotdot+ then docSeq [docCommaSep, docLit $ Text.pack "..", docSeparator]+ else docSeparator+ , docLit $ Text.pack "}"+ ]+ -- hanging single-line fields+ -- container { fieldA = blub+ -- , fieldB = blub+ -- }+ addAlternativeCond (indentPolicy == IndentPolicyFree) $ docSeq+ [ docNodeAnnKW lexpr Nothing $ docForceSingleline $ appSep nameDoc+ , docSetBaseY+ $ docLines+ $ let+ line1 = docCols+ ColRec+ [ appSep $ docLit $ Text.pack "{"+ , docWrapNodePrior rF1f $ appSep $ docLit rF1n+ , case rF1e of+ Just x -> docWrapNodeRest rF1f $ docSeq+ [appSep $ docLit $ Text.pack "=", docForceSingleline x]+ Nothing -> docEmpty+ ]+ lineR = rFr <&> \(lfield, fText, fDoc) ->+ docWrapNode lfield $ docCols+ ColRec+ [ docCommaSep+ , appSep $ docLit fText+ , case fDoc of+ Just x ->+ docSeq [appSep $ docLit $ Text.pack "=", docForceSingleline x]+ Nothing -> docEmpty+ ]+ dotdotLine = if dotdot+ then docCols+ ColRec+ [ docNodeAnnKW lexpr (Just AnnOpenC) docCommaSep+ , docNodeAnnKW lexpr (Just AnnDotdot) $ docLit $ Text.pack ".."+ ]+ else docNodeAnnKW lexpr (Just AnnOpenC) docEmpty+ lineN = docLit $ Text.pack "}"+ in [line1] ++ lineR ++ [dotdotLine, lineN]+ ]+ -- non-hanging with expressions placed to the right of the names+ -- container+ -- { fieldA = blub+ -- , fieldB = potentially+ -- multiline+ -- }+ addAlternative $ docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar+ (docNodeAnnKW lexpr Nothing nameDoc)+ (docNonBottomSpacing+ $ docLines+ $ let+ line1 = docCols+ ColRec+ [ appSep $ docLit $ Text.pack "{"+ , docWrapNodePrior rF1f $ appSep $ docLit rF1n+ , docWrapNodeRest rF1f $ case rF1e of+ Just x -> runFilteredAlternative $ do+ addAlternativeCond (indentPolicy == IndentPolicyFree) $ do+ docSeq [appSep $ docLit $ Text.pack "=", docSetBaseY x]+ addAlternative $ do+ docSeq+ [appSep $ docLit $ Text.pack "=", docForceParSpacing x]+ addAlternative $ do+ docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "=") x+ Nothing -> docEmpty+ ]+ lineR = rFr <&> \(lfield, fText, fDoc) ->+ docWrapNode lfield $ docCols+ ColRec+ [ docCommaSep+ , appSep $ docLit fText+ , case fDoc of+ Just x -> runFilteredAlternative $ do+ addAlternativeCond (indentPolicy == IndentPolicyFree) $ do+ docSeq [appSep $ docLit $ Text.pack "=", docSetBaseY x]+ addAlternative $ do+ docSeq+ [appSep $ docLit $ Text.pack "=", docForceParSpacing x]+ addAlternative $ do+ docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "=") x+ Nothing -> docEmpty+ ]+ dotdotLine = if dotdot+ then docCols+ ColRec+ [ docNodeAnnKW lexpr (Just AnnOpenC) docCommaSep+ , docNodeAnnKW lexpr (Just AnnDotdot) $ docLit $ Text.pack ".."+ ]+ else docNodeAnnKW lexpr (Just AnnOpenC) docEmpty+ lineN = docLit $ Text.pack "}"+ in [line1] ++ lineR ++ [dotdotLine, lineN]+ )++litBriDoc :: HsLit GhcPs -> BriDocFInt+litBriDoc = \case+ HsChar (SourceText t) _c -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ ['\'', c, '\'']+ HsCharPrim (SourceText t) _c -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ ['\'', c, '\'']+ HsString (SourceText t) _fastString -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ FastString.unpackFS fastString+ HsStringPrim (SourceText t) _byteString -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ Data.ByteString.Char8.unpack byteString+ HsInt _ (IL (SourceText t) _ _) -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsIntPrim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsWordPrim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsInt64Prim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsWord64Prim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsInteger (SourceText t) _i _type -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i+ HsRat _ (FL (SourceText t) _ _) _type -> BDFLit $ Text.pack t+ HsFloatPrim _ (FL (SourceText t) _ _) -> BDFLit $ Text.pack t+ HsDoublePrim _ (FL (SourceText t) _ _) -> BDFLit $ Text.pack t+ _ -> error "litBriDoc: literal with no SourceText"++overLitValBriDoc :: OverLitVal -> BriDocFInt+overLitValBriDoc = \case+ HsIntegral (IL (SourceText t) _ _) -> BDFLit $ Text.pack t+ HsFractional (FL (SourceText t) _ _) -> BDFLit $ Text.pack t+ HsIsString (SourceText t) _ -> BDFLit $ Text.pack t+ _ -> error "overLitValBriDoc: literal with no SourceText"
+ source/library/Brittany/Internal/Layouters/Expr.hs-boot view
@@ -0,0 +1,16 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Expr where++import GHC.Hs+import Brittany.Internal.Types++++layoutExpr :: ToBriDoc HsExpr++-- layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))++litBriDoc :: HsLit GhcPs -> BriDocFInt++overLitValBriDoc :: OverLitVal -> BriDocFInt
+ source/library/Brittany/Internal/Layouters/IE.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.IE where++import qualified Data.List.Extra+import qualified Data.Text as Text+import GHC+ ( AnnKeywordId(..)+ , GenLocated(L)+ , Located+ , ModuleName+ , moduleNameString+ , unLoc+ )+import GHC.Hs+import qualified GHC.OldList as List+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Prelude+import Brittany.Internal.Types+import Brittany.Internal.Utils++++prepareName :: LIEWrappedName name -> Located name+prepareName = ieLWrappedName++layoutIE :: ToBriDoc IE+layoutIE lie@(L _ ie) = docWrapNode lie $ case ie of+ IEVar _ x -> layoutWrapped lie x+ IEThingAbs _ x -> layoutWrapped lie x+ IEThingAll _ x -> docSeq [layoutWrapped lie x, docLit $ Text.pack "(..)"]+ IEThingWith _ x (IEWildcard _) _ _ ->+ docSeq [layoutWrapped lie x, docLit $ Text.pack "(..)"]+ IEThingWith _ x _ ns _ -> do+ hasComments <- orM+ (hasCommentsBetween lie AnnOpenP AnnCloseP+ : hasAnyCommentsBelow x+ : map hasAnyCommentsBelow ns+ )+ let sortedNs = List.sortOn wrappedNameToText ns+ runFilteredAlternative $ do+ addAlternativeCond (not hasComments)+ $ docSeq+ $ [layoutWrapped lie x, docLit $ Text.pack "("]+ ++ intersperse docCommaSep (map nameDoc sortedNs)+ ++ [docParenR]+ addAlternative+ $ docWrapNodeRest lie+ $ docAddBaseY BrIndentRegular+ $ docPar (layoutWrapped lie x) (layoutItems (splitFirstLast sortedNs))+ where+ nameDoc = docLit <=< lrdrNameToTextAnn . prepareName+ layoutItem n = docSeq [docCommaSep, docWrapNode n $ nameDoc n]+ layoutItems FirstLastEmpty = docSetBaseY $ docLines+ [ docSeq [docParenLSep, docNodeAnnKW lie (Just AnnOpenP) docEmpty]+ , docParenR+ ]+ layoutItems (FirstLastSingleton n) = docSetBaseY $ docLines+ [ docSeq [docParenLSep, docNodeAnnKW lie (Just AnnOpenP) $ nameDoc n]+ , docParenR+ ]+ layoutItems (FirstLast n1 nMs nN) =+ docSetBaseY+ $ docLines+ $ [docSeq [docParenLSep, docWrapNode n1 $ nameDoc n1]]+ ++ map layoutItem nMs+ ++ [ docSeq [docCommaSep, docNodeAnnKW lie (Just AnnOpenP) $ nameDoc nN]+ , docParenR+ ]+ IEModuleContents _ n -> docSeq+ [ docLit $ Text.pack "module"+ , docSeparator+ , docLit . Text.pack . moduleNameString $ unLoc n+ ]+ _ -> docEmpty+ where+ layoutWrapped _ = \case+ L _ (IEName n) -> docLit =<< lrdrNameToTextAnn n+ L _ (IEPattern n) -> do+ name <- lrdrNameToTextAnn n+ docLit $ Text.pack "pattern " <> name+ L _ (IEType n) -> do+ name <- lrdrNameToTextAnn n+ docLit $ Text.pack "type " <> name++data SortItemsFlag = ShouldSortItems | KeepItemsUnsorted+-- Helper function to deal with Located lists of LIEs.+-- In particular this will also associate documentation+-- from the located list that actually belongs to the last IE.+-- It also adds docCommaSep to all but the first element+-- This configuration allows both vertical and horizontal+-- handling of the resulting list. Adding parens is+-- left to the caller since that is context sensitive+layoutAnnAndSepLLIEs+ :: SortItemsFlag+ -> Located [LIE GhcPs]+ -> ToBriDocM [ToBriDocM BriDocNumbered]+layoutAnnAndSepLLIEs shouldSort llies@(L _ lies) = do+ let makeIENode ie = docSeq [docCommaSep, ie]+ let+ sortedLies =+ [ items+ | group <- Data.List.Extra.groupOn lieToText $ List.sortOn lieToText lies+ , items <- mergeGroup group+ ]+ let+ ieDocs = fmap layoutIE $ case shouldSort of+ ShouldSortItems -> sortedLies+ KeepItemsUnsorted -> lies+ ieCommaDocs <-+ docWrapNodeRest llies $ sequence $ case splitFirstLast ieDocs of+ FirstLastEmpty -> []+ FirstLastSingleton ie -> [ie]+ FirstLast ie1 ieMs ieN ->+ [ie1] ++ map makeIENode ieMs ++ [makeIENode ieN]+ pure $ fmap pure ieCommaDocs -- returned shared nodes+ where+ mergeGroup :: [LIE GhcPs] -> [LIE GhcPs]+ mergeGroup [] = []+ mergeGroup items@[_] = items+ mergeGroup items = if+ | all isProperIEThing items -> [List.foldl1' thingFolder items]+ | all isIEVar items -> [List.foldl1' thingFolder items]+ | otherwise -> items+ -- proper means that if it is a ThingWith, it does not contain a wildcard+ -- (because I don't know what a wildcard means if it is not already a+ -- IEThingAll).+ isProperIEThing :: LIE GhcPs -> Bool+ isProperIEThing = \case+ L _ (IEThingAbs _ _wn) -> True+ L _ (IEThingAll _ _wn) -> True+ L _ (IEThingWith _ _wn NoIEWildcard _ _) -> True+ _ -> False+ isIEVar :: LIE GhcPs -> Bool+ isIEVar = \case+ L _ IEVar{} -> True+ _ -> False+ thingFolder :: LIE GhcPs -> LIE GhcPs -> LIE GhcPs+ thingFolder l1@(L _ IEVar{}) _ = l1+ thingFolder l1@(L _ IEThingAll{}) _ = l1+ thingFolder _ l2@(L _ IEThingAll{}) = l2+ thingFolder l1 (L _ IEThingAbs{}) = l1+ thingFolder (L _ IEThingAbs{}) l2 = l2+ thingFolder (L l (IEThingWith x wn _ consItems1 fieldLbls1)) (L _ (IEThingWith _ _ _ consItems2 fieldLbls2))+ = L+ l+ (IEThingWith+ x+ wn+ NoIEWildcard+ (consItems1 ++ consItems2)+ (fieldLbls1 ++ fieldLbls2)+ )+ thingFolder _ _ =+ error "thingFolder should be exhaustive because we have a guard above"+++-- Builds a complete layout for the given located+-- list of LIEs. The layout provides two alternatives:+-- (item, item, ..., item)+-- ( item+-- , item+-- ...+-- , item+-- )+-- If the llies contains comments the list will+-- always expand over multiple lines, even when empty:+-- () -- no comments+-- ( -- a comment+-- )+layoutLLIEs+ :: Bool -> SortItemsFlag -> Located [LIE GhcPs] -> ToBriDocM BriDocNumbered+layoutLLIEs enableSingleline shouldSort llies = do+ ieDs <- layoutAnnAndSepLLIEs shouldSort llies+ hasComments <- hasAnyCommentsBelow llies+ runFilteredAlternative $ case ieDs of+ [] -> do+ addAlternativeCond (not hasComments) $ docLit $ Text.pack "()"+ addAlternativeCond hasComments $ docPar+ (docSeq [docParenLSep, docWrapNodeRest llies docEmpty])+ docParenR+ (ieDsH : ieDsT) -> do+ addAlternativeCond (not hasComments && enableSingleline)+ $ docSeq+ $ [docLit (Text.pack "(")]+ ++ (docForceSingleline <$> ieDs)+ ++ [docParenR]+ addAlternative+ $ docPar (docSetBaseY $ docSeq [docParenLSep, ieDsH])+ $ docLines+ $ ieDsT+ ++ [docParenR]++-- | Returns a "fingerprint string", not a full text representation, nor even+-- a source code representation of this syntax node.+-- Used for sorting, not for printing the formatter's output source code.+wrappedNameToText :: LIEWrappedName RdrName -> Text+wrappedNameToText = \case+ L _ (IEName n) -> lrdrNameToText n+ L _ (IEPattern n) -> lrdrNameToText n+ L _ (IEType n) -> lrdrNameToText n++-- | Returns a "fingerprint string", not a full text representation, nor even+-- a source code representation of this syntax node.+-- Used for sorting, not for printing the formatter's output source code.+lieToText :: LIE GhcPs -> Text+lieToText = \case+ L _ (IEVar _ wn) -> wrappedNameToText wn+ L _ (IEThingAbs _ wn) -> wrappedNameToText wn+ L _ (IEThingAll _ wn) -> wrappedNameToText wn+ L _ (IEThingWith _ wn _ _ _) -> wrappedNameToText wn+ -- TODO: These _may_ appear in exports!+ -- Need to check, and either put them at the top (for module) or do some+ -- other clever thing.+ L _ (IEModuleContents _ n) -> moduleNameToText n+ L _ IEGroup{} -> Text.pack "@IEGroup"+ L _ IEDoc{} -> Text.pack "@IEDoc"+ L _ IEDocNamed{} -> Text.pack "@IEDocNamed"+ where+ moduleNameToText :: Located ModuleName -> Text+ moduleNameToText (L _ name) =+ Text.pack ("@IEModuleContents" ++ moduleNameString name)
+ source/library/Brittany/Internal/Layouters/Import.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Import where++import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import GHC (GenLocated(L), Located, moduleNameString, unLoc)+import GHC.Hs+import GHC.Types.Basic+import GHC.Unit.Types (IsBootInterface(..))+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.IE+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types++++prepPkg :: SourceText -> String+prepPkg rawN = case rawN of+ SourceText n -> n+ -- This would be odd to encounter and the+ -- result will most certainly be wrong+ NoSourceText -> ""+prepModName :: Located e -> e+prepModName = unLoc++layoutImport :: ImportDecl GhcPs -> ToBriDocM BriDocNumbered+layoutImport importD = case importD of+ ImportDecl _ _ (L _ modName) pkg src safe q False mas mllies -> do+ importCol <- mAsk <&> _conf_layout .> _lconfig_importColumn .> confUnpack+ importAsCol <-+ mAsk <&> _conf_layout .> _lconfig_importAsColumn .> confUnpack+ indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack+ let+ compact = indentPolicy /= IndentPolicyFree+ modNameT = Text.pack $ moduleNameString modName+ pkgNameT = Text.pack . prepPkg . sl_st <$> pkg+ masT = Text.pack . moduleNameString . prepModName <$> mas+ hiding = maybe False fst mllies+ minQLength = length "import qualified "+ qLengthReal =+ let+ qualifiedPart = if q /= NotQualified then length "qualified " else 0+ safePart = if safe then length "safe " else 0+ pkgPart = maybe 0 ((+ 1) . Text.length) pkgNameT+ srcPart = case src of+ IsBoot -> length "{-# SOURCE #-} "+ NotBoot -> 0+ in length "import " + srcPart + safePart + qualifiedPart + pkgPart+ qLength = max minQLength qLengthReal+ -- Cost in columns of importColumn+ asCost = length "as "+ hidingParenCost = if hiding then length "hiding ( " else length "( "+ nameCost = Text.length modNameT + qLength+ importQualifiers = docSeq+ [ appSep $ docLit $ Text.pack "import"+ , case src of+ IsBoot -> appSep $ docLit $ Text.pack "{-# SOURCE #-}"+ NotBoot -> docEmpty+ , if safe then appSep $ docLit $ Text.pack "safe" else docEmpty+ , if q /= NotQualified+ then appSep $ docLit $ Text.pack "qualified"+ else docEmpty+ , maybe docEmpty (appSep . docLit) pkgNameT+ ]+ indentName =+ if compact then id else docEnsureIndent (BrIndentSpecial qLength)+ modNameD = indentName $ appSep $ docLit modNameT+ hidDocCol = if hiding then importCol - hidingParenCost else importCol - 2+ hidDocColDiff = importCol - 2 - hidDocCol+ hidDoc =+ if hiding then appSep $ docLit $ Text.pack "hiding" else docEmpty+ importHead = docSeq [importQualifiers, modNameD]+ bindingsD = case mllies of+ Nothing -> docEmpty+ Just (_, llies) -> do+ hasComments <- hasAnyCommentsBelow llies+ if compact+ then docAlt+ [ docSeq+ [ hidDoc+ , docForceSingleline $ layoutLLIEs True ShouldSortItems llies+ ]+ , let+ makeParIfHiding = if hiding+ then docAddBaseY BrIndentRegular . docPar hidDoc+ else id+ in makeParIfHiding (layoutLLIEs True ShouldSortItems llies)+ ]+ else do+ ieDs <- layoutAnnAndSepLLIEs ShouldSortItems llies+ docWrapNodeRest llies+ $ docEnsureIndent (BrIndentSpecial hidDocCol)+ $ case ieDs of+ -- ..[hiding].( )+ [] -> if hasComments+ then docPar+ (docSeq+ [hidDoc, docParenLSep, docWrapNode llies docEmpty]+ )+ (docEnsureIndent+ (BrIndentSpecial hidDocColDiff)+ docParenR+ )+ else docSeq+ [hidDoc, docParenLSep, docSeparator, docParenR]+ -- ..[hiding].( b )+ [ieD] -> runFilteredAlternative $ do+ addAlternativeCond (not hasComments)+ $ docSeq+ [ hidDoc+ , docParenLSep+ , docForceSingleline ieD+ , docSeparator+ , docParenR+ ]+ addAlternative $ docPar+ (docSeq [hidDoc, docParenLSep, docNonBottomSpacing ieD])+ (docEnsureIndent+ (BrIndentSpecial hidDocColDiff)+ docParenR+ )+ -- ..[hiding].( b+ -- , b'+ -- )+ (ieD : ieDs') -> docPar+ (docSeq [hidDoc, docSetBaseY $ docSeq [docParenLSep, ieD]]+ )+ (docEnsureIndent (BrIndentSpecial hidDocColDiff)+ $ docLines+ $ ieDs'+ ++ [docParenR]+ )+ makeAsDoc asT =+ docSeq [appSep $ docLit $ Text.pack "as", appSep $ docLit asT]+ if compact+ then+ let asDoc = maybe docEmpty makeAsDoc masT+ in+ docAlt+ [ docForceSingleline $ docSeq [importHead, asDoc, bindingsD]+ , docAddBaseY BrIndentRegular+ $ docPar (docSeq [importHead, asDoc]) bindingsD+ ]+ else case masT of+ Just n -> if enoughRoom+ then docLines [docSeq [importHead, asDoc], bindingsD]+ else docLines [importHead, asDoc, bindingsD]+ where+ enoughRoom = nameCost < importAsCol - asCost+ asDoc = docEnsureIndent (BrIndentSpecial (importAsCol - asCost))+ $ makeAsDoc n+ Nothing -> if enoughRoom+ then docSeq [importHead, bindingsD]+ else docLines [importHead, bindingsD]+ where enoughRoom = nameCost < importCol - hidingParenCost+ _ -> docEmpty
+ source/library/Brittany/Internal/Layouters/Module.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Module where++import qualified Data.Maybe+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import GHC (AnnKeywordId(..), GenLocated(L), moduleNameString, unLoc)+import GHC.Hs+import qualified GHC.OldList as List+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.IE+import Brittany.Internal.Layouters.Import+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Language.Haskell.GHC.ExactPrint as ExactPrint+import Language.Haskell.GHC.ExactPrint.Types+ (DeltaPos(..), commentContents, deltaRow)++++layoutModule :: ToBriDoc' HsModule+layoutModule lmod@(L _ mod') = case mod' of+ -- Implicit module Main+ HsModule _ Nothing _ imports _ _ _ -> do+ commentedImports <- transformToCommentedImport imports+ -- groupify commentedImports `forM_` tellDebugMessShow+ docLines (commentedImportsToDoc <$> sortCommentedImports commentedImports)+ -- sortedImports <- sortImports imports+ -- docLines $ [layoutImport y i | (y, i) <- sortedImports]+ HsModule _ (Just n) les imports _ _ _ -> do+ commentedImports <- transformToCommentedImport imports+ -- groupify commentedImports `forM_` tellDebugMessShow+ -- sortedImports <- sortImports imports+ let tn = Text.pack $ moduleNameString $ unLoc n+ allowSingleLineExportList <-+ mAsk <&> _conf_layout .> _lconfig_allowSingleLineExportList .> confUnpack+ -- the config should not prevent single-line layout when there is no+ -- export list+ let allowSingleLine = allowSingleLineExportList || Data.Maybe.isNothing les+ docLines+ $ docSeq+ [ docNodeAnnKW lmod Nothing docEmpty+ -- A pseudo node that serves merely to force documentation+ -- before the node+ , docNodeMoveToKWDP lmod AnnModule True $ runFilteredAlternative $ do+ addAlternativeCond allowSingleLine $ docForceSingleline $ docSeq+ [ appSep $ docLit $ Text.pack "module"+ , appSep $ docLit tn+ , docWrapNode lmod $ appSep $ case les of+ Nothing -> docEmpty+ Just x -> layoutLLIEs True KeepItemsUnsorted x+ , docSeparator+ , docLit $ Text.pack "where"+ ]+ addAlternative $ docLines+ [ docAddBaseY BrIndentRegular $ docPar+ (docSeq [appSep $ docLit $ Text.pack "module", docLit tn])+ (docSeq+ [ docWrapNode lmod $ case les of+ Nothing -> docEmpty+ Just x -> layoutLLIEs False KeepItemsUnsorted x+ , docSeparator+ , docLit $ Text.pack "where"+ ]+ )+ ]+ ]+ : (commentedImportsToDoc <$> sortCommentedImports commentedImports) -- [layoutImport y i | (y, i) <- sortedImports]++data CommentedImport+ = EmptyLine+ | IndependentComment (Comment, DeltaPos)+ | ImportStatement ImportStatementRecord++instance Show CommentedImport where+ show = \case+ EmptyLine -> "EmptyLine"+ IndependentComment _ -> "IndependentComment"+ ImportStatement r ->+ "ImportStatement " ++ show (length $ commentsBefore r) ++ " " ++ show+ (length $ commentsAfter r)++data ImportStatementRecord = ImportStatementRecord+ { commentsBefore :: [(Comment, DeltaPos)]+ , commentsAfter :: [(Comment, DeltaPos)]+ , importStatement :: ImportDecl GhcPs+ }++instance Show ImportStatementRecord where+ show r =+ "ImportStatement " ++ show (length $ commentsBefore r) ++ " " ++ show+ (length $ commentsAfter r)++transformToCommentedImport+ :: [LImportDecl GhcPs] -> ToBriDocM [CommentedImport]+transformToCommentedImport is = do+ nodeWithAnnotations <- is `forM` \i@(L _ rawImport) -> do+ annotionMay <- astAnn i+ pure (annotionMay, rawImport)+ let+ convertComment (c, DP (y, x)) =+ replicate (y - 1) EmptyLine ++ [IndependentComment (c, DP (1, x))]+ accumF+ :: [(Comment, DeltaPos)]+ -> (Maybe Annotation, ImportDecl GhcPs)+ -> ([(Comment, DeltaPos)], [CommentedImport])+ accumF accConnectedComm (annMay, decl) = case annMay of+ Nothing ->+ ( []+ , [ ImportStatement ImportStatementRecord+ { commentsBefore = []+ , commentsAfter = []+ , importStatement = decl+ }+ ]+ )+ Just ann ->+ let+ blanksBeforeImportDecl = deltaRow (annEntryDelta ann) - 1+ (newAccumulator, priorComments') =+ List.span ((== 0) . deltaRow . snd) (annPriorComments ann)+ go+ :: [(Comment, DeltaPos)]+ -> [(Comment, DeltaPos)]+ -> ([CommentedImport], [(Comment, DeltaPos)], Int)+ go acc [] = ([], acc, 0)+ go acc [c1@(_, DP (y, _))] = ([], c1 : acc, y - 1)+ go acc (c1@(_, DP (1, _)) : xs) = go (c1 : acc) xs+ go acc ((c1, DP (y, x)) : xs) =+ ( (convertComment =<< xs) ++ replicate (y - 1) EmptyLine+ , (c1, DP (1, x)) : acc+ , 0+ )+ (convertedIndependentComments, beforeComments, initialBlanks) =+ if blanksBeforeImportDecl /= 0+ then (convertComment =<< priorComments', [], 0)+ else go [] (reverse priorComments')+ in+ ( newAccumulator+ , convertedIndependentComments+ ++ replicate (blanksBeforeImportDecl + initialBlanks) EmptyLine+ ++ [ ImportStatement ImportStatementRecord+ { commentsBefore = beforeComments+ , commentsAfter = accConnectedComm+ , importStatement = decl+ }+ ]+ )+ let (finalAcc, finalList) = mapAccumR accumF [] nodeWithAnnotations+ pure $ join $ (convertComment =<< finalAcc) : finalList++sortCommentedImports :: [CommentedImport] -> [CommentedImport]+sortCommentedImports =+ unpackImports . mergeGroups . map (fmap (sortGroups)) . groupify+ where+ unpackImports :: [CommentedImport] -> [CommentedImport]+ unpackImports xs = xs >>= \case+ l@EmptyLine -> [l]+ l@IndependentComment{} -> [l]+ ImportStatement r ->+ map IndependentComment (commentsBefore r) ++ [ImportStatement r]+ mergeGroups+ :: [Either CommentedImport [ImportStatementRecord]] -> [CommentedImport]+ mergeGroups xs = xs >>= \case+ Left x -> [x]+ Right y -> ImportStatement <$> y+ sortGroups :: [ImportStatementRecord] -> [ImportStatementRecord]+ sortGroups =+ List.sortOn (moduleNameString . unLoc . ideclName . importStatement)+ groupify+ :: [CommentedImport] -> [Either CommentedImport [ImportStatementRecord]]+ groupify cs = go [] cs+ where+ go [] = \case+ (l@EmptyLine : rest) -> Left l : go [] rest+ (l@IndependentComment{} : rest) -> Left l : go [] rest+ (ImportStatement r : rest) -> go [r] rest+ [] -> []+ go acc = \case+ (l@EmptyLine : rest) -> Right (reverse acc) : Left l : go [] rest+ (l@IndependentComment{} : rest) ->+ Left l : Right (reverse acc) : go [] rest+ (ImportStatement r : rest) -> go (r : acc) rest+ [] -> [Right (reverse acc)]++commentedImportsToDoc :: CommentedImport -> ToBriDocM BriDocNumbered+commentedImportsToDoc = \case+ EmptyLine -> docLitS ""+ IndependentComment c -> commentToDoc c+ ImportStatement r -> docSeq+ (layoutImport (importStatement r) : map commentToDoc (commentsAfter r))+ where+ commentToDoc (c, DP (_y, x)) = docLitS (replicate x ' ' ++ commentContents c)
+ source/library/Brittany/Internal/Layouters/Pattern.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Pattern where++import qualified Data.Foldable as Foldable+import qualified Data.Sequence as Seq+import qualified Data.Text as Text+import GHC (GenLocated(L), ol_val)+import GHC.Hs+import qualified GHC.OldList as List+import GHC.Types.Basic+import Brittany.Internal.LayouterBasics+import {-# SOURCE #-} Brittany.Internal.Layouters.Expr+import Brittany.Internal.Layouters.Type+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types++++-- | layouts patterns (inside function bindings, case alternatives, let+-- bindings or do notation). E.g. for input+-- > case computation of+-- > (warnings, Success a b) -> ..+-- This part ^^^^^^^^^^^^^^^^^^^^^^^ of the syntax tree is layouted by+-- 'layoutPat'. Similarly for+-- > func abc True 0 = []+-- ^^^^^^^^^^ this part+-- We will use `case .. of` as the imagined prefix to the examples used in+-- the different cases below.+layoutPat :: LPat GhcPs -> ToBriDocM (Seq BriDocNumbered)+layoutPat lpat@(L _ pat) = docWrapNode lpat $ case pat of+ WildPat _ -> fmap Seq.singleton $ docLit $ Text.pack "_"+ -- _ -> expr+ VarPat _ n -> fmap Seq.singleton $ docLit $ lrdrNameToText n+ -- abc -> expr+ LitPat _ lit -> fmap Seq.singleton $ allocateNode $ litBriDoc lit+ -- 0 -> expr+ ParPat _ inner -> do+ -- (nestedpat) -> expr+ left <- docLit $ Text.pack "("+ right <- docLit $ Text.pack ")"+ innerDocs <- colsWrapPat =<< layoutPat inner+ return $ Seq.empty Seq.|> left Seq.|> innerDocs Seq.|> right+ -- return $ (left Seq.<| innerDocs) Seq.|> right+ -- case Seq.viewl innerDocs of+ -- Seq.EmptyL -> fmap return $ docLit $ Text.pack "()" -- this should never occur..+ -- x1 Seq.:< rest -> case Seq.viewr rest of+ -- Seq.EmptyR ->+ -- fmap return $ docSeq+ -- [ docLit $ Text.pack "("+ -- , return x1+ -- , docLit $ Text.pack ")"+ -- ]+ -- middle Seq.:> xN -> do+ -- x1' <- docSeq [docLit $ Text.pack "(", return x1]+ -- xN' <- docSeq [return xN, docLit $ Text.pack ")"]+ -- return $ (x1' Seq.<| middle) Seq.|> xN'+ ConPat _ lname (PrefixCon args) -> do+ -- Abc a b c -> expr+ nameDoc <- lrdrNameToTextAnn lname+ argDocs <- layoutPat `mapM` args+ if null argDocs+ then return <$> docLit nameDoc+ else do+ x1 <- appSep (docLit nameDoc)+ xR <- fmap Seq.fromList $ sequence $ spacifyDocs $ fmap+ colsWrapPat+ argDocs+ return $ x1 Seq.<| xR+ ConPat _ lname (InfixCon left right) -> do+ -- a :< b -> expr+ nameDoc <- lrdrNameToTextAnn lname+ leftDoc <- appSep . colsWrapPat =<< layoutPat left+ rightDoc <- colsWrapPat =<< layoutPat right+ middle <- appSep $ docLit nameDoc+ return $ Seq.empty Seq.|> leftDoc Seq.|> middle Seq.|> rightDoc+ ConPat _ lname (RecCon (HsRecFields [] Nothing)) -> do+ -- Abc{} -> expr+ let t = lrdrNameToText lname+ fmap Seq.singleton $ docLit $ t <> Text.pack "{}"+ ConPat _ lname (RecCon (HsRecFields fs@(_ : _) Nothing)) -> do+ -- Abc { a = locA, b = locB, c = locC } -> expr1+ -- Abc { a, b, c } -> expr2+ let t = lrdrNameToText lname+ fds <- fs `forM` \(L _ (HsRecField (L _ fieldOcc) fPat pun)) -> do+ let FieldOcc _ lnameF = fieldOcc+ fExpDoc <- if pun+ then return Nothing+ else Just <$> docSharedWrapper layoutPat fPat+ return (lrdrNameToText lnameF, fExpDoc)+ Seq.singleton <$> docSeq+ [ appSep $ docLit t+ , appSep $ docLit $ Text.pack "{"+ , docSeq $ List.intersperse docCommaSep $ fds <&> \case+ (fieldName, Just fieldDoc) -> docSeq+ [ appSep $ docLit fieldName+ , appSep $ docLit $ Text.pack "="+ , fieldDoc >>= colsWrapPat+ ]+ (fieldName, Nothing) -> docLit fieldName+ , docSeparator+ , docLit $ Text.pack "}"+ ]+ ConPat _ lname (RecCon (HsRecFields [] (Just (L _ 0)))) -> do+ -- Abc { .. } -> expr+ let t = lrdrNameToText lname+ Seq.singleton <$> docSeq [appSep $ docLit t, docLit $ Text.pack "{..}"]+ ConPat _ lname (RecCon (HsRecFields fs@(_ : _) (Just (L _ dotdoti))))+ | dotdoti == length fs -> do+ -- Abc { a = locA, .. }+ let t = lrdrNameToText lname+ fds <- fs `forM` \(L _ (HsRecField (L _ fieldOcc) fPat pun)) -> do+ let FieldOcc _ lnameF = fieldOcc+ fExpDoc <- if pun+ then return Nothing+ else Just <$> docSharedWrapper layoutPat fPat+ return (lrdrNameToText lnameF, fExpDoc)+ 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+ -- (nestedpat1, nestedpat2, nestedpat3) -> expr+ -- (#nestedpat1, nestedpat2, nestedpat3#) -> expr+ case boxity of+ Boxed -> wrapPatListy args "()" docParenL docParenR+ Unboxed -> wrapPatListy args "(##)" docParenHashLSep docParenHashRSep+ AsPat _ asName asPat -> do+ -- bind@nestedpat -> expr+ wrapPatPrepend asPat (docLit $ lrdrNameToText asName <> Text.pack "@")+ SigPat _ pat1 (HsPS _ ty1) -> do+ -- i :: Int -> expr+ patDocs <- layoutPat pat1+ tyDoc <- docSharedWrapper layoutType ty1+ case Seq.viewr patDocs of+ Seq.EmptyR -> error "cannot happen ljoiuxoasdcoviuasd"+ xR Seq.:> xN -> do+ xN' <- -- at the moment, we don't support splitting patterns into+ -- multiple lines. but we cannot enforce pasting everything+ -- into one line either, because the type signature will ignore+ -- this if we overflow sufficiently.+ -- In order to prevent syntactically invalid results in such+ -- cases, we need the AddBaseY here.+ -- This can all change when patterns get multiline support.+ docAddBaseY BrIndentRegular $ docSeq+ [ appSep $ return xN+ , appSep $ docLit $ Text.pack "::"+ , docForceSingleline tyDoc+ ]+ return $ xR Seq.|> xN'+ ListPat _ elems ->+ -- [] -> expr1+ -- [nestedpat1, nestedpat2, nestedpat3] -> expr2+ wrapPatListy elems "[]" docBracketL docBracketR+ BangPat _ pat1 -> do+ -- !nestedpat -> expr+ wrapPatPrepend pat1 (docLit $ Text.pack "!")+ LazyPat _ pat1 -> do+ -- ~nestedpat -> expr+ wrapPatPrepend pat1 (docLit $ Text.pack "~")+ NPat _ llit@(L _ ol) mNegative _ -> do+ -- -13 -> expr+ litDoc <- docWrapNode llit $ allocateNode $ overLitValBriDoc $ GHC.ol_val ol+ negDoc <- docLit $ Text.pack "-"+ pure $ case mNegative of+ Just{} -> Seq.fromList [negDoc, litDoc]+ Nothing -> Seq.singleton litDoc++ _ -> return <$> briDocByExactInlineOnly "some unknown pattern" lpat++colsWrapPat :: Seq BriDocNumbered -> ToBriDocM BriDocNumbered+colsWrapPat = docCols ColPatterns . fmap return . Foldable.toList++wrapPatPrepend+ :: LPat GhcPs -> ToBriDocM BriDocNumbered -> ToBriDocM (Seq BriDocNumbered)+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++wrapPatListy+ :: [LPat GhcPs]+ -> String+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM BriDocNumbered+ -> ToBriDocM (Seq BriDocNumbered)+wrapPatListy elems both start end = do+ elemDocs <- Seq.fromList elems `forM` (layoutPat >=> colsWrapPat)+ case Seq.viewl elemDocs of+ Seq.EmptyL -> fmap Seq.singleton $ docLit $ Text.pack both+ x1 Seq.:< rest -> do+ sDoc <- start+ eDoc <- end+ rest' <- rest `forM` \bd -> docSeq [docCommaSep, return bd]+ return $ (sDoc Seq.<| x1 Seq.<| rest') Seq.|> eDoc
+ source/library/Brittany/Internal/Layouters/Stmt.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal.Layouters.Stmt where++import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import GHC (GenLocated(L))+import GHC.Hs+import Brittany.Internal.Config.Types+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Layouters.Decl+import {-# SOURCE #-} Brittany.Internal.Layouters.Expr+import Brittany.Internal.Layouters.Pattern+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types++++layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))+layoutStmt lstmt@(L _ stmt) = do+ indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack+ indentAmount :: Int <-+ mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ docWrapNode lstmt $ case stmt of+ LastStmt _ body Nothing _ -> do+ layoutExpr body+ BindStmt _ lPat expr -> do+ patDoc <- fmap return $ colsWrapPat =<< layoutPat lPat+ expDoc <- docSharedWrapper layoutExpr expr+ docAlt+ [ docCols+ ColBindStmt+ [ appSep patDoc+ , docSeq+ [ appSep $ docLit $ Text.pack "<-"+ , docAddBaseY BrIndentRegular $ docForceParSpacing expDoc+ ]+ ]+ , docCols+ ColBindStmt+ [ appSep patDoc+ , docAddBaseY BrIndentRegular+ $ docPar (docLit $ Text.pack "<-") (expDoc)+ ]+ ]+ LetStmt _ binds -> do+ let isFree = indentPolicy == IndentPolicyFree+ let indentFourPlus = indentAmount >= 4+ layoutLocalBinds binds >>= \case+ Nothing -> docLit $ Text.pack "let"+ -- i just tested the above, and it is indeed allowed. heh.+ Just [] -> docLit $ Text.pack "let" -- this probably never happens+ Just [bindDoc] -> docAlt+ [ -- let bind = expr+ docCols+ ColDoLet+ [ appSep $ docLit $ Text.pack "let"+ , let+ f = case indentPolicy of+ IndentPolicyFree -> docSetBaseAndIndent+ IndentPolicyLeft -> docForceSingleline+ IndentPolicyMultiple+ | indentFourPlus -> docSetBaseAndIndent+ | otherwise -> docForceSingleline+ in f $ return bindDoc+ ]+ , -- let+ -- bind = expr+ docAddBaseY BrIndentRegular $ docPar+ (docLit $ Text.pack "let")+ (docSetBaseAndIndent $ return bindDoc)+ ]+ Just bindDocs -> runFilteredAlternative $ do+ -- let aaa = expra+ -- bbb = exprb+ -- ccc = exprc+ addAlternativeCond (isFree || indentFourPlus) $ docSeq+ [ appSep $ docLit $ Text.pack "let"+ , let+ f = if indentFourPlus+ then docEnsureIndent BrIndentRegular+ else docSetBaseAndIndent+ in f $ docLines $ return <$> bindDocs+ ]+ -- let+ -- aaa = expra+ -- bbb = exprb+ -- ccc = exprc+ addAlternativeCond (not indentFourPlus)+ $ docAddBaseY BrIndentRegular+ $ docPar+ (docLit $ Text.pack "let")+ (docSetBaseAndIndent $ docLines $ return <$> bindDocs)+ RecStmt _ stmts _ _ _ _ _ -> runFilteredAlternative $ do+ -- rec stmt1+ -- stmt2+ -- stmt3+ addAlternativeCond (indentPolicy == IndentPolicyFree) $ docSeq+ [ docLit (Text.pack "rec")+ , docSeparator+ , docSetBaseAndIndent $ docLines $ layoutStmt <$> stmts+ ]+ -- rec+ -- stmt1+ -- stmt2+ -- stmt3+ addAlternative $ docAddBaseY BrIndentRegular $ docPar+ (docLit (Text.pack "rec"))+ (docLines $ layoutStmt <$> stmts)+ BodyStmt _ expr _ _ -> do+ expDoc <- docSharedWrapper layoutExpr expr+ docAddBaseY BrIndentRegular $ expDoc+ _ -> briDocByExactInlineOnly "some unknown statement" lstmt
+ source/library/Brittany/Internal/Layouters/Stmt.hs-boot view
@@ -0,0 +1,10 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Stmt where++import GHC.Hs+import Brittany.Internal.Types++++layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))
+ source/library/Brittany/Internal/Layouters/Type.hs view
@@ -0,0 +1,635 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Layouters.Type where++import qualified Data.Text as Text+import GHC (AnnKeywordId(..), GenLocated(L))+import GHC.Hs+import qualified GHC.OldList as List+import GHC.Types.Basic+import GHC.Utils.Outputable (ftext, showSDocUnsafe)+import Brittany.Internal.LayouterBasics+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils+ (FirstLastView(..), splitFirstLast)++++layoutType :: ToBriDoc HsType+layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of+ -- _ | traceShow (ExactPrint.Types.mkAnnKey ltype) False -> error "impossible"+ HsTyVar _ promoted name -> do+ t <- lrdrNameToTextAnnTypeEqualityIsSpecial name+ case promoted of+ IsPromoted -> docSeq [docSeparator, docTick, docWrapNode name $ docLit t]+ NotPromoted -> docWrapNode name $ docLit t+ HsForAllTy _ hsf (L _ (HsQualTy _ (L _ cntxts) typ2)) -> do+ let bndrs = getBinders hsf+ typeDoc <- docSharedWrapper layoutType typ2+ tyVarDocs <- layoutTyVarBndrs bndrs+ cntxtDocs <- cntxts `forM` docSharedWrapper layoutType+ let+ maybeForceML = case typ2 of+ (L _ HsFunTy{}) -> docForceMultiline+ _ -> id+ let+ tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs+ forallDoc = docAlt+ [ let open = docLit $ Text.pack "forall"+ in docSeq ([open] ++ tyVarDocLineList)+ , docPar+ (docLit (Text.pack "forall"))+ (docLines $ tyVarDocs <&> \case+ (tname, Nothing) -> docEnsureIndent BrIndentRegular $ docLit tname+ (tname, Just doc) -> docEnsureIndent BrIndentRegular $ docLines+ [ docCols ColTyOpPrefix [docParenLSep, docLit tname]+ , docCols ColTyOpPrefix [docLit $ Text.pack ":: ", doc]+ , docLit $ Text.pack ")"+ ]+ )+ ]+ contextDoc = case cntxtDocs of+ [] -> docLit $ Text.pack "()"+ [x] -> x+ _ -> docAlt+ [ let+ open = docLit $ Text.pack "("+ close = docLit $ Text.pack ")"+ list =+ List.intersperse docCommaSep $ docForceSingleline <$> cntxtDocs+ in docSeq ([open] ++ list ++ [close])+ , let+ open = docCols+ ColTyOpPrefix+ [docParenLSep, docAddBaseY (BrIndentSpecial 2) $ head cntxtDocs]+ close = docLit $ Text.pack ")"+ list = List.tail cntxtDocs <&> \cntxtDoc -> docCols+ ColTyOpPrefix+ [docCommaSep, docAddBaseY (BrIndentSpecial 2) cntxtDoc]+ in docPar open $ docLines $ list ++ [close]+ ]+ docAlt+ -- :: forall a b c . (Foo a b c) => a b -> c+ [ docSeq+ [ if null bndrs+ then docEmpty+ else+ let+ open = docLit $ Text.pack "forall"+ close = docLit $ Text.pack " . "+ in docSeq ([open, docSeparator] ++ tyVarDocLineList ++ [close])+ , docForceSingleline contextDoc+ , docLit $ Text.pack " => "+ , docForceSingleline typeDoc+ ]+ -- :: forall a b c+ -- . (Foo a b c)+ -- => a b+ -- -> c+ , docPar+ forallDoc+ (docLines+ [ docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docLit $ Text.pack " . "+ , docAddBaseY (BrIndentSpecial 3) $ contextDoc+ ]+ , docCols+ ColTyOpPrefix+ [ docLit $ Text.pack "=> "+ , docAddBaseY (BrIndentSpecial 3) $ maybeForceML $ typeDoc+ ]+ ]+ )+ ]+ HsForAllTy _ hsf typ2 -> do+ let bndrs = getBinders hsf+ typeDoc <- layoutType typ2+ tyVarDocs <- layoutTyVarBndrs bndrs+ let+ maybeForceML = case typ2 of+ (L _ HsFunTy{}) -> docForceMultiline+ _ -> id+ let tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs+ docAlt+ -- forall x . x+ [ docSeq+ [ if null bndrs+ then docEmpty+ else+ let+ open = docLit $ Text.pack "forall"+ close = docLit $ Text.pack " . "+ in docSeq ([open] ++ tyVarDocLineList ++ [close])+ , docForceSingleline $ return $ typeDoc+ ]+ -- :: forall x+ -- . x+ , docPar+ (docSeq $ docLit (Text.pack "forall") : tyVarDocLineList)+ (docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docLit $ Text.pack " . "+ , maybeForceML $ return typeDoc+ ]+ )+ -- :: forall+ -- (x :: *)+ -- . x+ , docPar+ (docLit (Text.pack "forall"))+ (docLines+ $ (tyVarDocs <&> \case+ (tname, Nothing) ->+ docEnsureIndent BrIndentRegular $ docLit tname+ (tname, Just doc) -> docEnsureIndent BrIndentRegular $ docLines+ [ docCols ColTyOpPrefix [docParenLSep, docLit tname]+ , docCols ColTyOpPrefix [docLit $ Text.pack ":: ", doc]+ , docLit $ Text.pack ")"+ ]+ )+ ++ [ docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docLit $ Text.pack " . "+ , maybeForceML $ return typeDoc+ ]+ ]+ )+ ]+ HsQualTy _ lcntxts@(L _ cntxts) typ1 -> do+ typeDoc <- docSharedWrapper layoutType typ1+ cntxtDocs <- cntxts `forM` docSharedWrapper layoutType+ let+ contextDoc = docWrapNode lcntxts $ case cntxtDocs of+ [] -> docLit $ Text.pack "()"+ [x] -> x+ _ -> docAlt+ [ let+ open = docLit $ Text.pack "("+ close = docLit $ Text.pack ")"+ list =+ List.intersperse docCommaSep $ docForceSingleline <$> cntxtDocs+ in docSeq ([open] ++ list ++ [close])+ , let+ open = docCols+ ColTyOpPrefix+ [docParenLSep, docAddBaseY (BrIndentSpecial 2) $ head cntxtDocs]+ close = docLit $ Text.pack ")"+ list = List.tail cntxtDocs <&> \cntxtDoc -> docCols+ ColTyOpPrefix+ [docCommaSep, docAddBaseY (BrIndentSpecial 2) $ cntxtDoc]+ in docPar open $ docLines $ list ++ [close]+ ]+ let+ maybeForceML = case typ1 of+ (L _ HsFunTy{}) -> docForceMultiline+ _ -> id+ docAlt+ -- (Foo a b c) => a b -> c+ [ docSeq+ [ docForceSingleline contextDoc+ , docLit $ Text.pack " => "+ , docForceSingleline typeDoc+ ]+ -- (Foo a b c)+ -- => a b+ -- -> c+ , docPar+ (docForceSingleline contextDoc)+ (docCols+ ColTyOpPrefix+ [ docLit $ Text.pack "=> "+ , docAddBaseY (BrIndentSpecial 3) $ maybeForceML typeDoc+ ]+ )+ ]+ HsFunTy _ _ typ1 typ2 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ typeDoc2 <- docSharedWrapper layoutType typ2+ let+ maybeForceML = case typ2 of+ (L _ HsFunTy{}) -> docForceMultiline+ _ -> id+ hasComments <- hasAnyCommentsBelow ltype+ docAlt+ $ [ docSeq+ [ appSep $ docForceSingleline typeDoc1+ , appSep $ docLit $ Text.pack "->"+ , docForceSingleline typeDoc2+ ]+ | not hasComments+ ]+ ++ [ docPar+ (docNodeAnnKW ltype Nothing typeDoc1)+ (docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ appSep $ docLit $ Text.pack "->"+ , docAddBaseY (BrIndentSpecial 3) $ maybeForceML typeDoc2+ ]+ )+ ]+ HsParTy _ typ1 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ docAlt+ [ docSeq+ [ docWrapNodeRest ltype $ docLit $ Text.pack "("+ , docForceSingleline typeDoc1+ , docLit $ Text.pack ")"+ ]+ , docPar+ (docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docParenLSep+ , docAddBaseY (BrIndentSpecial 2) $ typeDoc1+ ]+ )+ (docLit $ Text.pack ")")+ ]+ HsAppTy _ typ1@(L _ HsAppTy{}) typ2 -> do+ let+ gather+ :: [LHsType GhcPs] -> LHsType GhcPs -> (LHsType GhcPs, [LHsType GhcPs])+ gather list = \case+ L _ (HsAppTy _ ty1 ty2) -> gather (ty2 : list) ty1+ final -> (final, list)+ let (typHead, typRest) = gather [typ2] typ1+ docHead <- docSharedWrapper layoutType typHead+ docRest <- docSharedWrapper layoutType `mapM` typRest+ docAlt+ [ docSeq+ $ docForceSingleline docHead+ : (docRest >>= \d -> [docSeparator, docForceSingleline d])+ , docPar docHead (docLines $ docEnsureIndent BrIndentRegular <$> docRest)+ ]+ HsAppTy _ typ1 typ2 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ typeDoc2 <- docSharedWrapper layoutType typ2+ docAlt+ [ docSeq+ [docForceSingleline typeDoc1, docSeparator, docForceSingleline typeDoc2]+ , docPar typeDoc1 (docEnsureIndent BrIndentRegular typeDoc2)+ ]+ HsListTy _ typ1 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ docAlt+ [ docSeq+ [ docWrapNodeRest ltype $ docLit $ Text.pack "["+ , docForceSingleline typeDoc1+ , docLit $ Text.pack "]"+ ]+ , docPar+ (docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docLit $ Text.pack "[ "+ , docAddBaseY (BrIndentSpecial 2) $ typeDoc1+ ]+ )+ (docLit $ Text.pack "]")+ ]+ HsTupleTy _ tupleSort typs -> case tupleSort of+ HsUnboxedTuple -> unboxed+ HsBoxedTuple -> simple+ HsConstraintTuple -> simple+ HsBoxedOrConstraintTuple -> simple+ where+ unboxed = if null typs+ then error "brittany internal error: unboxed unit"+ else unboxedL+ simple = if null typs then unitL else simpleL+ unitL = docLit $ Text.pack "()"+ simpleL = do+ docs <- docSharedWrapper layoutType `mapM` typs+ let+ end = docLit $ Text.pack ")"+ lines =+ List.tail docs+ <&> \d -> docAddBaseY (BrIndentSpecial 2)+ $ docCols ColTyOpPrefix [docCommaSep, d]+ commaDocs = List.intersperse docCommaSep (docForceSingleline <$> docs)+ docAlt+ [ docSeq+ $ [docLit $ Text.pack "("]+ ++ docWrapNodeRest ltype commaDocs+ ++ [end]+ , let line1 = docCols ColTyOpPrefix [docParenLSep, head docs]+ in+ docPar+ (docAddBaseY (BrIndentSpecial 2) $ line1)+ (docLines $ docWrapNodeRest ltype lines ++ [end])+ ]+ unboxedL = do+ docs <- docSharedWrapper layoutType `mapM` typs+ let+ start = docParenHashLSep+ end = docParenHashRSep+ docAlt+ [ docSeq+ $ [start]+ ++ docWrapNodeRest ltype (List.intersperse docCommaSep docs)+ ++ [end]+ , let+ line1 = docCols ColTyOpPrefix [start, head docs]+ lines =+ List.tail docs+ <&> \d -> docAddBaseY (BrIndentSpecial 2)+ $ docCols ColTyOpPrefix [docCommaSep, d]+ in docPar+ (docAddBaseY (BrIndentSpecial 2) line1)+ (docLines $ lines ++ [end])+ ]+ HsOpTy{} -> -- TODO+ briDocByExactInlineOnly "HsOpTy{}" ltype+ -- HsOpTy typ1 opName typ2 -> do+ -- -- TODO: these need some proper fixing. precedences don't add up.+ -- -- maybe the parser just returns some trivial right recursion+ -- -- parse result for any type level operators.+ -- -- need to check how things are handled on the expression level.+ -- let opStr = lrdrNameToText opName+ -- let opLen = Text.length opStr+ -- layouter1@(Layouter desc1 _ _) <- layoutType typ1+ -- layouter2@(Layouter desc2 _ _) <- layoutType typ2+ -- let line = do -- Maybe+ -- l1 <- _ldesc_line desc1+ -- l2 <- _ldesc_line desc2+ -- let len1 = _lColumns_min l1+ -- let len2 = _lColumns_min l2+ -- let len = 2 + opLen + len1 + len2+ -- return $ LayoutColumns+ -- { _lColumns_key = ColumnKeyUnique+ -- , _lColumns_lengths = [len]+ -- , _lColumns_min = len+ -- }+ -- let block = do -- Maybe+ -- rol1 <- descToBlockStart desc1+ -- (min2, max2) <- descToMinMax (1+opLen) desc2+ -- let (minR, maxR) = case descToBlockMinMax desc1 of+ -- Nothing -> (min2, max2)+ -- Just (min1, max1) -> (max min1 min2, max max1 max2)+ -- return $ BlockDesc+ -- { _bdesc_blockStart = rol1+ -- , _bdesc_min = minR+ -- , _bdesc_max = maxR+ -- , _bdesc_opIndentFloatUp = Just (1+opLen)+ -- }+ -- return $ Layouter+ -- { _layouter_desc = LayoutDesc+ -- { _ldesc_line = line+ -- , _ldesc_block = block+ -- }+ -- , _layouter_func = \params -> do+ -- remaining <- getCurRemaining+ -- let allowSameLine = _params_sepLines params /= SepLineTypeOp+ -- case line of+ -- Just (LayoutColumns _ _ m) | m <= remaining && allowSameLine -> do+ -- applyLayouterRestore layouter1 defaultParams+ -- layoutWriteAppend $ Text.pack " " <> opStr <> Text.pack " "+ -- applyLayouterRestore layouter2 defaultParams+ -- _ -> do+ -- let upIndent = maybe (1+opLen) (max (1+opLen)) $ _params_opIndent params+ -- let downIndent = maybe upIndent (max upIndent) $ _bdesc_opIndentFloatUp =<< _ldesc_block desc2+ -- layoutWithAddIndentN downIndent $ applyLayouterRestore layouter1 defaultParams+ -- layoutWriteNewline+ -- layoutWriteAppend $ opStr <> Text.pack " "+ -- layoutWriteEnsureBlockPlusN downIndent+ -- applyLayouterRestore layouter2 defaultParams+ -- { _params_sepLines = SepLineTypeOp+ -- , _params_opIndent = Just downIndent+ -- }+ -- , _layouter_ast = ltype+ -- }+ HsIParamTy _ (L _ (HsIPName ipName)) typ1 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ docAlt+ [ docSeq+ [ docWrapNodeRest ltype $ docLit $ Text.pack+ ("?" ++ showSDocUnsafe (ftext ipName) ++ "::")+ , docForceSingleline typeDoc1+ ]+ , docPar+ (docLit $ Text.pack ("?" ++ showSDocUnsafe (ftext ipName)))+ (docCols+ ColTyOpPrefix+ [ docWrapNodeRest ltype $ docLit $ Text.pack ":: "+ , docAddBaseY (BrIndentSpecial 2) typeDoc1+ ]+ )+ ]+ -- TODO: test KindSig+ HsKindSig _ typ1 kind1 -> do+ typeDoc1 <- docSharedWrapper layoutType typ1+ kindDoc1 <- docSharedWrapper layoutType kind1+ hasParens <- hasAnnKeyword ltype AnnOpenP+ docAlt+ [ 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+ ]+ )+ ]+ HsBangTy{} -> -- TODO+ briDocByExactInlineOnly "HsBangTy{}" ltype+ -- HsBangTy bang typ1 -> do+ -- let bangStr = case bang of+ -- HsSrcBang _ unpackness strictness ->+ -- (++)+ -- (case unpackness of+ -- SrcUnpack -> "{-# UNPACK -#} "+ -- SrcNoUnpack -> "{-# NOUNPACK -#} "+ -- NoSrcUnpack -> ""+ -- )+ -- (case strictness of+ -- SrcLazy -> "~"+ -- SrcStrict -> "!"+ -- NoSrcStrict -> ""+ -- )+ -- let bangLen = length bangStr+ -- layouter@(Layouter desc _ _) <- layoutType typ1+ -- let line = do -- Maybe+ -- l <- _ldesc_line desc+ -- let len = bangLen + _lColumns_min l+ -- return $ LayoutColumns+ -- { _lColumns_key = ColumnKeyUnique+ -- , _lColumns_lengths = [len]+ -- , _lColumns_min = len+ -- }+ -- let block = do -- Maybe+ -- rol <- descToBlockStart desc+ -- (minR,maxR) <- descToBlockMinMax desc+ -- return $ BlockDesc+ -- { _bdesc_blockStart = rol+ -- , _bdesc_min = minR+ -- , _bdesc_max = maxR+ -- , _bdesc_opIndentFloatUp = Nothing+ -- }+ -- return $ Layouter+ -- { _layouter_desc = LayoutDesc+ -- { _ldesc_line = line+ -- , _ldesc_block = block+ -- }+ -- , _layouter_func = \_params -> do+ -- remaining <- getCurRemaining+ -- case line of+ -- Just (LayoutColumns _ _ m) | m <= remaining -> do+ -- layoutWriteAppend $ Text.pack $ bangStr+ -- applyLayouterRestore layouter defaultParams+ -- _ -> do+ -- layoutWriteAppend $ Text.pack $ bangStr+ -- layoutWritePostCommentsRestore ltype+ -- applyLayouterRestore layouter defaultParams+ -- , _layouter_ast = ltype+ -- }+ HsSpliceTy{} -> -- TODO+ briDocByExactInlineOnly "HsSpliceTy{}" ltype+ HsDocTy{} -> -- TODO+ briDocByExactInlineOnly "HsDocTy{}" ltype+ HsRecTy{} -> -- TODO+ briDocByExactInlineOnly "HsRecTy{}" ltype+ HsExplicitListTy _ _ typs -> do+ typDocs <- docSharedWrapper layoutType `mapM` typs+ hasComments <- hasAnyCommentsBelow ltype+ let specialCommaSep = appSep $ docLit $ Text.pack " ,"+ docAlt+ [ docSeq+ $ [docLit $ Text.pack "'["]+ ++ List.intersperse specialCommaSep (docForceSingleline <$> typDocs)+ ++ [docLit $ Text.pack "]"]+ , case splitFirstLast typDocs of+ FirstLastEmpty -> docSeq+ [ docLit $ Text.pack "'["+ , docNodeAnnKW ltype (Just AnnOpenS) $ docLit $ Text.pack "]"+ ]+ FirstLastSingleton e -> docAlt+ [ docSeq+ [ docLit $ Text.pack "'["+ , docNodeAnnKW ltype (Just AnnOpenS) $ docForceSingleline e+ , docLit $ Text.pack "]"+ ]+ , docSetBaseY $ docLines+ [ docSeq+ [ docLit $ Text.pack "'["+ , docSeparator+ , docSetBaseY $ docNodeAnnKW ltype (Just AnnOpenS) e+ ]+ , docLit $ Text.pack " ]"+ ]+ ]+ FirstLast e1 ems eN -> runFilteredAlternative $ do+ addAlternativeCond (not hasComments)+ $ docSeq+ $ [docLit $ Text.pack "'["]+ ++ List.intersperse+ specialCommaSep+ (docForceSingleline+ <$> (e1 : ems ++ [docNodeAnnKW ltype (Just AnnOpenS) eN])+ )+ ++ [docLit $ Text.pack " ]"]+ addAlternative+ $ let+ start = docCols ColList [appSep $ docLit $ Text.pack "'[", e1]+ linesM = ems <&> \d -> docCols ColList [specialCommaSep, d]+ lineN = docCols+ ColList+ [specialCommaSep, docNodeAnnKW ltype (Just AnnOpenS) eN]+ end = docLit $ Text.pack " ]"+ in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN] ++ [end]+ ]+ HsExplicitTupleTy{} -> -- TODO+ briDocByExactInlineOnly "HsExplicitTupleTy{}" ltype+ HsTyLit _ lit -> case lit of+ HsNumTy (SourceText srctext) _ -> docLit $ Text.pack srctext+ HsNumTy NoSourceText _ ->+ error "overLitValBriDoc: literal with no SourceText"+ HsStrTy (SourceText srctext) _ -> docLit $ Text.pack srctext+ HsStrTy NoSourceText _ ->+ error "overLitValBriDoc: literal with no SourceText"+ HsWildCardTy _ -> docLit $ Text.pack "_"+ HsSumTy{} -> -- TODO+ briDocByExactInlineOnly "HsSumTy{}" ltype+ HsStarTy _ isUnicode -> do+ if isUnicode+ then docLit $ Text.pack "\x2605" -- Unicode star+ else docLit $ Text.pack "*"+ XHsType{} -> error "brittany internal error: XHsType"+ HsAppKindTy _ ty kind -> do+ t <- docSharedWrapper layoutType ty+ k <- docSharedWrapper layoutType kind+ docAlt+ [ docSeq+ [ docForceSingleline t+ , docSeparator+ , docLit $ Text.pack "@"+ , docForceSingleline k+ ]+ , docPar t (docSeq [docLit $ Text.pack "@", k])+ ]++layoutTyVarBndrs+ :: [LHsTyVarBndr () GhcPs]+ -> ToBriDocM [(Text, Maybe (ToBriDocM BriDocNumbered))]+layoutTyVarBndrs = mapM $ \case+ (L _ (UserTyVar _ _ name)) -> return $ (lrdrNameToText name, Nothing)+ (L _ (KindedTyVar _ _ lrdrName kind)) -> do+ d <- docSharedWrapper layoutType kind+ return $ (lrdrNameToText lrdrName, Just $ d)++-- there is no specific reason this returns a list instead of a single+-- BriDoc node.+processTyVarBndrsSingleline+ :: [(Text, Maybe (ToBriDocM BriDocNumbered))] -> [ToBriDocM BriDocNumbered]+processTyVarBndrsSingleline bndrDocs = bndrDocs >>= \case+ (tname, Nothing) -> [docSeparator, docLit tname]+ (tname, Just doc) ->+ [ docSeparator+ , docLit $ Text.pack "(" <> tname <> Text.pack " :: "+ , docForceSingleline $ doc+ , docLit $ Text.pack ")"+ ]++getBinders :: HsForAllTelescope pass -> [LHsTyVarBndr () pass]+getBinders x = case x of+ HsForAllVis _ b -> b+ HsForAllInvis _ b -> fmap withoutSpecificity b+ XHsForAllTelescope _ -> []++withoutSpecificity :: LHsTyVarBndr flag pass -> LHsTyVarBndr () pass+withoutSpecificity = fmap $ \case+ UserTyVar a _ c -> UserTyVar a () c+ KindedTyVar a _ c d -> KindedTyVar a () c d+ XTyVarBndr a -> XTyVarBndr a
+ source/library/Brittany/Internal/Obfuscation.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Obfuscation where++import Data.Char+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified GHC.OldList as List+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import System.Random++++obfuscate :: Text -> IO Text+obfuscate input = do+ let predi x = isAlphaNum x || x `elem` "_'"+ let groups = List.groupBy (\a b -> predi a && predi b) (Text.unpack input)+ let idents = Set.toList $ Set.fromList $ filter (all predi) groups+ let+ exceptionFilter x | x `elem` keywords = False+ exceptionFilter x | x `elem` extraKWs = False+ exceptionFilter x = not $ null $ drop 1 x+ let filtered = filter exceptionFilter idents+ mappings <- fmap Map.fromList $ filtered `forM` \x -> do+ r <- createAlias x+ pure (x, r)+ let groups' = groups <&> \w -> fromMaybe w (Map.lookup w mappings)+ pure $ Text.concat $ fmap Text.pack groups'++keywords :: [String]+keywords =+ [ "case"+ , "class"+ , "data"+ , "default"+ , "deriving"+ , "do"+ , "mdo"+ , "else"+ , "forall"+ , "if"+ , "import"+ , "in"+ , "infix"+ , "infixl"+ , "infixr"+ , "instance"+ , "let"+ , "module"+ , "newtype"+ , "of"+ , "qualified"+ , "then"+ , "type"+ , "where"+ , "_"+ , "foreign"+ , "ccall"+ , "as"+ , "safe"+ , "unsafe"+ , "hiding"+ , "proc"+ , "rec"+ , "family"+ ]++extraKWs :: [String]+extraKWs = ["return", "pure", "Int", "True", "False", "otherwise"]++createAlias :: String -> IO String+createAlias xs = go NoHint xs+ where+ go _hint "" = pure ""+ go hint (c : cr) = do+ c' <- case hint of+ VocalHint | isUpper c -> randomFrom $ "AAAEEEOOOIIIUUU" ++ ['A' .. 'Z']+ _ | isUpper c -> randomFrom ['A' .. 'Z']+ VocalHint | isLower c -> randomFrom $ "aaaeeeoooiiiuuu" ++ ['a' .. 'z']+ _ | isLower c -> randomFrom ['a' .. 'z']+ _ -> pure c+ cr' <- go (if c' `elem` "aeuioAEUIO" then NoVocalHint else VocalHint) cr+ pure (c' : cr')++data Hint = NoHint | VocalHint | NoVocalHint++_randomRange :: Random a => a -> a -> IO a+_randomRange lo hi = do+ gen <- getStdGen+ let (x, gen') = randomR (lo, hi) gen+ setStdGen gen'+ pure x++randomFrom :: [a] -> IO a+randomFrom l = do+ let hi = length l - 1+ gen <- getStdGen+ let (x, gen') = randomR (0, hi) gen+ setStdGen gen'+ pure $ l List.!! x
+ source/library/Brittany/Internal/ParseModule.hs view
@@ -0,0 +1,316 @@+{-# OPTIONS_GHC -Wno-implicit-prelude #-}++module Brittany.Internal.ParseModule where++import qualified Control.Monad as Monad+import qualified Control.Monad.IO.Class as IO+import qualified Control.Monad.Trans.Except as Except+import qualified GHC+import qualified GHC.ByteOrder+import qualified GHC.Data.Bag+import qualified GHC.Data.StringBuffer+import qualified GHC.Driver.Session+import qualified GHC.Parser.Header+import qualified GHC.Platform+import qualified GHC.Settings+import qualified GHC.Types.SrcLoc+import qualified GHC.Utils.Error+import qualified GHC.Utils.Fingerprint+import qualified Language.Haskell.GHC.ExactPrint.Parsers as ExactPrint+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint++-- | Parses a Haskell module. Although this nominally requires IO, it is+-- morally pure. It should have no observable effects.+parseModule+ :: IO.MonadIO io+ => [String]+ -> FilePath+ -> (GHC.Driver.Session.DynFlags -> io (Either String a))+ -> String+ -> io (Either String (ExactPrint.Anns, GHC.ParsedSource, a))+parseModule arguments1 filePath checkDynFlags string = Except.runExceptT $ do+ let+ dynFlags1 = GHC.Driver.Session.gopt_set+ -- It feels like this should be either @Sf_Ignore@ or @Sf_None@, but both+ -- of those modes have trouble parsing safe imports (@import safe ...@).+ -- Neither passing in @"-XUnsafe"@ as a command line argument nor having+ -- @{-# LANGUAGE Unsafe #-}@ in the source file seem to help.+ initialDynFlags+ { GHC.Driver.Session.safeHaskell = GHC.Driver.Session.Sf_Unsafe+ }+ GHC.Driver.Session.Opt_KeepRawTokenStream+ (dynFlags2, leftovers1, _) <-+ GHC.Driver.Session.parseDynamicFlagsCmdLine dynFlags1+ $ fmap GHC.Types.SrcLoc.noLoc arguments1+ handleLeftovers leftovers1+ let+ stringBuffer = GHC.Data.StringBuffer.stringToStringBuffer string+ arguments2 = GHC.Parser.Header.getOptions dynFlags2 stringBuffer filePath+ (dynFlags3, leftovers2, _) <- GHC.Driver.Session.parseDynamicFilePragma+ dynFlags2+ arguments2+ handleLeftovers leftovers2+ dynFlagsResult <- Except.ExceptT $ checkDynFlags dynFlags3+ let+ parseResult =+ ExactPrint.parseModuleFromStringInternal dynFlags3 filePath string+ case parseResult of+ Left errorMessages -> handleErrorMessages errorMessages+ Right (anns, parsedSource) -> pure (anns, parsedSource, dynFlagsResult)++handleLeftovers+ :: Monad m => [GHC.Types.SrcLoc.Located String] -> Except.ExceptT String m ()+handleLeftovers leftovers =+ Monad.unless (null leftovers) . Except.throwE $ "leftovers: " <> show+ (fmap GHC.Types.SrcLoc.unLoc leftovers)++handleErrorMessages+ :: Monad m => GHC.Utils.Error.ErrorMessages -> Except.ExceptT String m a+handleErrorMessages =+ Except.throwE . mappend "errorMessages: " . show . GHC.Data.Bag.bagToList++initialDynFlags :: GHC.Driver.Session.DynFlags+initialDynFlags = GHC.Driver.Session.defaultDynFlags initialSettings initialLlvmConfig++initialSettings :: GHC.Driver.Session.Settings+initialSettings = GHC.Driver.Session.Settings+ { GHC.Driver.Session.sGhcNameVersion = initialGhcNameVersion+ , GHC.Driver.Session.sFileSettings = initialFileSettings+ , GHC.Driver.Session.sTargetPlatform = initialTargetPlatform+ , GHC.Driver.Session.sToolSettings = initialToolSettings+ , GHC.Driver.Session.sPlatformMisc = initialPlatformMisc+ , GHC.Driver.Session.sPlatformConstants = initialPlatformConstants+ , GHC.Driver.Session.sRawSettings = []+ }++initialFileSettings :: GHC.Driver.Session.FileSettings+initialFileSettings = GHC.Driver.Session.FileSettings+ { GHC.Driver.Session.fileSettings_ghciUsagePath = ""+ , GHC.Driver.Session.fileSettings_ghcUsagePath = ""+ , GHC.Driver.Session.fileSettings_globalPackageDatabase = ""+ , GHC.Driver.Session.fileSettings_tmpDir = ""+ , GHC.Driver.Session.fileSettings_toolDir = Nothing+ , GHC.Driver.Session.fileSettings_topDir = ""+ }++initialGhcNameVersion :: GHC.Driver.Session.GhcNameVersion+initialGhcNameVersion = GHC.Driver.Session.GhcNameVersion+ { GHC.Driver.Session.ghcNameVersion_programName = ""+ , GHC.Driver.Session.ghcNameVersion_projectVersion = ""+ }++initialPlatformMisc :: GHC.Driver.Session.PlatformMisc+initialPlatformMisc = GHC.Driver.Session.PlatformMisc+ { GHC.Driver.Session.platformMisc_ghcDebugged = False+ , GHC.Driver.Session.platformMisc_ghcRTSWays = ""+ , GHC.Driver.Session.platformMisc_ghcRtsWithLibdw = False+ , GHC.Driver.Session.platformMisc_ghcThreaded = False+ , GHC.Driver.Session.platformMisc_ghcWithInterpreter = False+ , GHC.Driver.Session.platformMisc_ghcWithSMP = False+ , GHC.Driver.Session.platformMisc_libFFI = False+ , GHC.Driver.Session.platformMisc_llvmTarget = ""+ , GHC.Driver.Session.platformMisc_targetPlatformString = ""+ }++initialLlvmConfig :: GHC.Driver.Session.LlvmConfig+initialLlvmConfig = GHC.Driver.Session.LlvmConfig+ { GHC.Driver.Session.llvmPasses = []+ , GHC.Driver.Session.llvmTargets = []+ }++initialPlatformConstants :: GHC.Settings.PlatformConstants+initialPlatformConstants = GHC.Settings.PlatformConstants+ { GHC.Settings.pc_AP_STACK_SPLIM = 0+ , GHC.Settings.pc_BITMAP_BITS_SHIFT = 0+ , GHC.Settings.pc_BLOCK_SIZE = 0+ , GHC.Settings.pc_BLOCKS_PER_MBLOCK = 0+ , GHC.Settings.pc_CINT_SIZE = 0+ , GHC.Settings.pc_CLONG_LONG_SIZE = 0+ , GHC.Settings.pc_CLONG_SIZE = 0+ , GHC.Settings.pc_CONTROL_GROUP_CONST_291 = 0+ , GHC.Settings.pc_DYNAMIC_BY_DEFAULT = False+ , GHC.Settings.pc_ILDV_CREATE_MASK = 0+ , GHC.Settings.pc_ILDV_STATE_CREATE = 0+ , GHC.Settings.pc_ILDV_STATE_USE = 0+ , GHC.Settings.pc_LDV_SHIFT = 0+ , GHC.Settings.pc_MAX_CHARLIKE = 0+ , GHC.Settings.pc_MAX_Double_REG = 0+ , GHC.Settings.pc_MAX_Float_REG = 0+ , GHC.Settings.pc_MAX_INTLIKE = 0+ , GHC.Settings.pc_MAX_Long_REG = 0+ , GHC.Settings.pc_MAX_Real_Double_REG = 0+ , GHC.Settings.pc_MAX_Real_Float_REG = 0+ , GHC.Settings.pc_MAX_Real_Long_REG = 0+ , GHC.Settings.pc_MAX_Real_Vanilla_REG = 0+ , GHC.Settings.pc_MAX_Real_XMM_REG = 0+ , GHC.Settings.pc_MAX_SPEC_AP_SIZE = 0+ , GHC.Settings.pc_MAX_SPEC_SELECTEE_SIZE = 0+ , GHC.Settings.pc_MAX_Vanilla_REG = 0+ , GHC.Settings.pc_MAX_XMM_REG = 0+ , GHC.Settings.pc_MIN_CHARLIKE = 0+ , GHC.Settings.pc_MIN_INTLIKE = 0+ , GHC.Settings.pc_MIN_PAYLOAD_SIZE = 0+ , GHC.Settings.pc_MUT_ARR_PTRS_CARD_BITS = 0+ , GHC.Settings.pc_OFFSET_bdescr_blocks = 0+ , GHC.Settings.pc_OFFSET_bdescr_flags = 0+ , GHC.Settings.pc_OFFSET_bdescr_free = 0+ , GHC.Settings.pc_OFFSET_bdescr_start = 0+ , GHC.Settings.pc_OFFSET_Capability_r = 0+ , GHC.Settings.pc_OFFSET_CostCentreStack_mem_alloc = 0+ , GHC.Settings.pc_OFFSET_CostCentreStack_scc_count = 0+ , GHC.Settings.pc_OFFSET_StgArrBytes_bytes = 0+ , GHC.Settings.pc_OFFSET_stgEagerBlackholeInfo = 0+ , GHC.Settings.pc_OFFSET_StgEntCounter_allocd = 0+ , GHC.Settings.pc_OFFSET_StgEntCounter_allocs = 0+ , GHC.Settings.pc_OFFSET_StgEntCounter_entry_count = 0+ , GHC.Settings.pc_OFFSET_StgEntCounter_link = 0+ , GHC.Settings.pc_OFFSET_StgEntCounter_registeredp = 0+ , GHC.Settings.pc_OFFSET_StgFunInfoExtraFwd_arity = 0+ , GHC.Settings.pc_OFFSET_StgFunInfoExtraRev_arity = 0+ , GHC.Settings.pc_OFFSET_stgGCEnter1 = 0+ , GHC.Settings.pc_OFFSET_stgGCFun = 0+ , GHC.Settings.pc_OFFSET_StgHeader_ccs = 0+ , GHC.Settings.pc_OFFSET_StgHeader_ldvw = 0+ , GHC.Settings.pc_OFFSET_StgMutArrPtrs_ptrs = 0+ , GHC.Settings.pc_OFFSET_StgMutArrPtrs_size = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rCCCS = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rCurrentNursery = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rCurrentTSO = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rD6 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rF6 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rHp = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rHpAlloc = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rHpLim = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rL1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR10 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR6 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR7 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR8 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rR9 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rSp = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rSpLim = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rXMM6 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rYMM6 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM1 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM2 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM3 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM4 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM5 = 0+ , GHC.Settings.pc_OFFSET_StgRegTable_rZMM6 = 0+ , GHC.Settings.pc_OFFSET_StgSmallMutArrPtrs_ptrs = 0+ , GHC.Settings.pc_OFFSET_StgStack_sp = 0+ , GHC.Settings.pc_OFFSET_StgStack_stack = 0+ , GHC.Settings.pc_OFFSET_StgTSO_alloc_limit = 0+ , GHC.Settings.pc_OFFSET_StgTSO_cccs = 0+ , GHC.Settings.pc_OFFSET_StgTSO_stackobj = 0+ , GHC.Settings.pc_OFFSET_StgUpdateFrame_updatee = 0+ , GHC.Settings.pc_PROF_HDR_SIZE = 0+ , GHC.Settings.pc_REP_CostCentreStack_mem_alloc = 0+ , GHC.Settings.pc_REP_CostCentreStack_scc_count = 0+ , GHC.Settings.pc_REP_StgEntCounter_allocd = 0+ , GHC.Settings.pc_REP_StgEntCounter_allocs = 0+ , GHC.Settings.pc_REP_StgFunInfoExtraFwd_arity = 0+ , GHC.Settings.pc_REP_StgFunInfoExtraRev_arity = 0+ , GHC.Settings.pc_RESERVED_C_STACK_BYTES = 0+ , GHC.Settings.pc_RESERVED_STACK_WORDS = 0+ , GHC.Settings.pc_SIZEOF_CostCentreStack = 0+ , GHC.Settings.pc_SIZEOF_StgArrBytes_NoHdr = 0+ , GHC.Settings.pc_SIZEOF_StgFunInfoExtraRev = 0+ , GHC.Settings.pc_SIZEOF_StgMutArrPtrs_NoHdr = 0+ , GHC.Settings.pc_SIZEOF_StgSmallMutArrPtrs_NoHdr = 0+ , GHC.Settings.pc_SIZEOF_StgSMPThunkHeader = 0+ , GHC.Settings.pc_SIZEOF_StgUpdateFrame_NoHdr = 0+ , GHC.Settings.pc_STD_HDR_SIZE = 0+ , GHC.Settings.pc_TAG_BITS = 0+ , GHC.Settings.pc_TICKY_BIN_COUNT = 0+ , GHC.Settings.pc_WORD_SIZE = 0+ }++initialPlatformMini :: GHC.Settings.PlatformMini+initialPlatformMini = GHC.Settings.PlatformMini+ { GHC.Settings.platformMini_arch = GHC.Platform.ArchX86_64+ , GHC.Settings.platformMini_os = GHC.Platform.OSLinux+ }++initialTargetPlatform :: GHC.Settings.Platform+initialTargetPlatform = GHC.Settings.Platform+ { GHC.Settings.platformByteOrder = GHC.ByteOrder.LittleEndian+ , GHC.Settings.platformHasGnuNonexecStack = False+ , GHC.Settings.platformHasIdentDirective = False+ , GHC.Settings.platformHasSubsectionsViaSymbols = False+ , GHC.Settings.platformIsCrossCompiling = False+ , GHC.Settings.platformLeadingUnderscore = False+ , GHC.Settings.platformMini = initialPlatformMini+ , GHC.Settings.platformTablesNextToCode = False+ , GHC.Settings.platformUnregisterised = False+ , GHC.Settings.platformWordSize = GHC.Platform.PW8+ }++initialToolSettings :: GHC.Settings.ToolSettings+initialToolSettings = GHC.Settings.ToolSettings+ { GHC.Settings.toolSettings_ccSupportsNoPie = False+ , GHC.Settings.toolSettings_extraGccViaCFlags = []+ , GHC.Settings.toolSettings_ldIsGnuLd = False+ , GHC.Settings.toolSettings_ldSupportsBuildId = False+ , GHC.Settings.toolSettings_ldSupportsCompactUnwind = False+ , GHC.Settings.toolSettings_ldSupportsFilelist = False+ , GHC.Settings.toolSettings_opt_a = []+ , GHC.Settings.toolSettings_opt_c = []+ , GHC.Settings.toolSettings_opt_cxx = []+ , GHC.Settings.toolSettings_opt_F = []+ , GHC.Settings.toolSettings_opt_i = []+ , GHC.Settings.toolSettings_opt_l = []+ , GHC.Settings.toolSettings_opt_L = []+ , GHC.Settings.toolSettings_opt_lc = []+ , GHC.Settings.toolSettings_opt_lcc = []+ , GHC.Settings.toolSettings_opt_lm = []+ , GHC.Settings.toolSettings_opt_lo = []+ , GHC.Settings.toolSettings_opt_P = []+ , GHC.Settings.toolSettings_opt_P_fingerprint =+ GHC.Utils.Fingerprint.fingerprint0+ , GHC.Settings.toolSettings_opt_windres = []+ , GHC.Settings.toolSettings_pgm_a = ("", [])+ , GHC.Settings.toolSettings_pgm_ar = ""+ , GHC.Settings.toolSettings_pgm_c = ""+ , GHC.Settings.toolSettings_pgm_dll = ("", [])+ , GHC.Settings.toolSettings_pgm_F = ""+ , GHC.Settings.toolSettings_pgm_i = ""+ , GHC.Settings.toolSettings_pgm_install_name_tool = ""+ , GHC.Settings.toolSettings_pgm_l = ("", [])+ , GHC.Settings.toolSettings_pgm_L = ""+ , GHC.Settings.toolSettings_pgm_lc = ("", [])+ , GHC.Settings.toolSettings_pgm_lcc = ("", [])+ , GHC.Settings.toolSettings_pgm_libtool = ""+ , GHC.Settings.toolSettings_pgm_lm = ("", [])+ , GHC.Settings.toolSettings_pgm_lo = ("", [])+ , GHC.Settings.toolSettings_pgm_otool = ""+ , GHC.Settings.toolSettings_pgm_P = ("", [])+ , GHC.Settings.toolSettings_pgm_ranlib = ""+ , GHC.Settings.toolSettings_pgm_T = ""+ , GHC.Settings.toolSettings_pgm_windres = ""+ }
+ source/library/Brittany/Internal/Prelude.hs view
@@ -0,0 +1,194 @@+module Brittany.Internal.Prelude+ ( module E+ ) where++import Control.Applicative as E (Alternative(..), Applicative(..))+import Control.Arrow as E ((&&&), (***), (<<<), (>>>), first, second)+import Control.Concurrent as E (forkIO, forkOS, threadDelay)+import Control.Concurrent.Chan as E (Chan)+import Control.Concurrent.MVar as E+ (MVar, newEmptyMVar, newMVar, putMVar, readMVar, swapMVar, takeMVar)+import Control.Exception as E (assert, bracket, evaluate)+import Control.Monad as E+ ( (<$!>)+ , (<=<)+ , (=<<)+ , (>=>)+ , Functor(..)+ , Monad(..)+ , MonadPlus(..)+ , filterM+ , forM+ , forM_+ , forever+ , guard+ , join+ , liftM+ , liftM2+ , liftM3+ , liftM4+ , liftM5+ , mapM+ , mapM_+ , replicateM+ , replicateM_+ , sequence+ , sequence_+ , unless+ , void+ , when+ )+import Control.Monad.Extra as E+ (allM, andM, anyM, ifM, notM, orM, unlessM, whenM)+import Control.Monad.IO.Class as E (MonadIO(..))+import Control.Monad.ST as E (ST)+import Control.Monad.Trans.Class as E (lift)+import Control.Monad.Trans.Maybe as E (MaybeT(..))+import Control.Monad.Trans.MultiRWS as E+ (MonadMultiReader(..), MonadMultiState(..), MonadMultiWriter(..), mGet)+import Data.Bifunctor as E (bimap)+import Data.Bool as E (Bool(..))+import Data.Char as E (Char, chr, ord)+import Data.Data as E (toConstr)+import Data.Either as E (Either(..), either)+import Data.Foldable as E (asum, fold, foldl', foldr')+import Data.Function as E ((&), fix)+import Data.Functor as E (($>))+import Data.Functor.Identity as E (Identity(..))+import Data.IORef as E (IORef)+import Data.Int as E (Int)+import Data.List as E+ ( all+ , break+ , drop+ , dropWhile+ , elem+ , filter+ , find+ , intercalate+ , intersperse+ , isPrefixOf+ , isSuffixOf+ , iterate+ , length+ , mapAccumL+ , mapAccumR+ , maximum+ , minimum+ , notElem+ , nub+ , null+ , partition+ , repeat+ , replicate+ , sortBy+ , sum+ , take+ , takeWhile+ , transpose+ , uncons+ , unzip+ , zip+ , zip3+ , zipWith+ )+import Data.List.Extra as E (nubOrd, stripSuffix)+import Data.List.NonEmpty as E (NonEmpty(..), nonEmpty)+import Data.Map as E (Map)+import Data.Maybe as E+ (Maybe(..), catMaybes, fromMaybe, listToMaybe, maybe, maybeToList)+import Data.Monoid as E+ ( All(..)+ , Alt(..)+ , Any(..)+ , Endo(..)+ , Monoid(..)+ , Product(..)+ , Sum(..)+ , mconcat+ )+import Data.Ord as E (Down(..), Ordering(..), comparing)+import Data.Proxy as E (Proxy(..))+import Data.Ratio as E ((%), Ratio, Rational, denominator, numerator)+import Data.Semigroup as E ((<>), Semigroup(..))+import Data.Sequence as E (Seq)+import Data.Set as E (Set)+import Data.String as E (String)+import Data.Text as E (Text)+import Data.Tree as E (Tree(..))+import Data.Tuple as E (swap)+import Data.Typeable as E (Typeable)+import Data.Version as E (showVersion)+import Data.Void as E (Void)+import Data.Word as E (Word, Word32)+import Debug.Trace as E+ ( trace+ , traceIO+ , traceId+ , traceM+ , traceShow+ , traceShowId+ , traceShowM+ , traceStack+ )+import Foreign.ForeignPtr as E (ForeignPtr)+import Foreign.Storable as E (Storable)+import GHC.Exts as E (Constraint)+import GHC.Hs.Extension as E (GhcPs)+import GHC.Types.Name.Reader as E (RdrName)+import Prelude as E+ ( ($)+ , ($!)+ , (&&)+ , (++)+ , (.)+ , (<$>)+ , Bounded(..)+ , Double+ , Enum(..)+ , Eq(..)+ , Float+ , Floating(..)+ , Foldable+ , Fractional(..)+ , Integer+ , Integral(..)+ , Num(..)+ , Ord(..)+ , RealFloat(..)+ , RealFrac(..)+ , Show(..)+ , Traversable+ , (^)+ , and+ , any+ , const+ , curry+ , error+ , flip+ , foldl+ , foldr+ , foldr1+ , fromIntegral+ , fst+ , head+ , id+ , map+ , not+ , or+ , otherwise+ , print+ , putStr+ , putStrLn+ , realToFrac+ , reverse+ , seq+ , snd+ , subtract+ , traverse+ , uncurry+ , undefined+ , (||)+ )+import System.IO as E (IO, hFlush, stdout)+import Text.Read as E (readMaybe)
+ source/library/Brittany/Internal/PreludeUtils.hs view
@@ -0,0 +1,66 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Brittany.Internal.PreludeUtils where++import Control.Applicative+import Control.DeepSeq (NFData, force)+import Control.Exception.Base (evaluate)+import Control.Monad+import qualified Data.Strict.Maybe as Strict+import Debug.Trace+import Prelude+import System.IO++++instance Applicative Strict.Maybe where+ pure = Strict.Just+ Strict.Just f <*> Strict.Just x = Strict.Just (f x)+ _ <*> _ = Strict.Nothing++instance Monad Strict.Maybe where+ Strict.Nothing >>= _ = Strict.Nothing+ Strict.Just x >>= f = f x++instance Alternative Strict.Maybe where+ empty = Strict.Nothing+ x <|> Strict.Nothing = x+ _ <|> x = x++traceFunctionWith+ :: String -> (a -> String) -> (b -> String) -> (a -> b) -> (a -> b)+traceFunctionWith name s1 s2 f x = trace traceStr y+ where+ y = f x+ traceStr = name ++ "\nBEFORE:\n" ++ s1 x ++ "\nAFTER:\n" ++ s2 y++(<&!>) :: Monad m => m a -> (a -> b) -> m b+(<&!>) = flip (<$!>)++putStrErrLn :: String -> IO ()+putStrErrLn s = hPutStrLn stderr s++putStrErr :: String -> IO ()+putStrErr s = hPutStr stderr s++printErr :: Show a => a -> IO ()+printErr = putStrErrLn . show++errorIf :: Bool -> a -> a+errorIf False = id+errorIf True = error "errorIf"++errorIfNote :: Maybe String -> a -> a+errorIfNote Nothing = id+errorIfNote (Just x) = error x++(<&>) :: Functor f => f a -> (a -> b) -> f b+(<&>) = flip fmap+infixl 4 <&>++(.>) :: (a -> b) -> (b -> c) -> (a -> c)+f .> g = g . f+infixl 9 .>++evaluateDeep :: NFData a => a -> IO a+evaluateDeep = evaluate . force
+ source/library/Brittany/Internal/Transformations/Alt.hs view
@@ -0,0 +1,961 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++module Brittany.Internal.Transformations.Alt where++import qualified Control.Monad.Memo as Memo+import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS+import Data.HList.ContainsType+import qualified Data.List.Extra+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import qualified GHC.OldList as List+import Brittany.Internal.Config.Types+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils++++data AltCurPos = AltCurPos+ { _acp_line :: Int -- chars in the current line+ , _acp_indent :: Int -- current indentation level+ , _acp_indentPrep :: Int -- indentChange affecting the next Par+ , _acp_forceMLFlag :: AltLineModeState+ }+ deriving Show++data AltLineModeState+ = AltLineModeStateNone+ | AltLineModeStateForceML Bool -- true ~ decays on next wrap+ | AltLineModeStateForceSL+ | AltLineModeStateContradiction+ -- i.e. ForceX False -> ForceX True -> None+ deriving (Show)++altLineModeRefresh :: AltLineModeState -> AltLineModeState+altLineModeRefresh AltLineModeStateNone = AltLineModeStateNone+altLineModeRefresh AltLineModeStateForceML{} = AltLineModeStateForceML False+altLineModeRefresh AltLineModeStateForceSL = AltLineModeStateForceSL+altLineModeRefresh AltLineModeStateContradiction =+ AltLineModeStateContradiction++altLineModeDecay :: AltLineModeState -> AltLineModeState+altLineModeDecay AltLineModeStateNone = AltLineModeStateNone+altLineModeDecay (AltLineModeStateForceML False) = AltLineModeStateForceML True+altLineModeDecay (AltLineModeStateForceML True) = AltLineModeStateNone+altLineModeDecay AltLineModeStateForceSL = AltLineModeStateForceSL+altLineModeDecay AltLineModeStateContradiction = AltLineModeStateContradiction++mergeLineMode :: AltCurPos -> AltLineModeState -> AltCurPos+mergeLineMode acp s = case (_acp_forceMLFlag acp, s) of+ (AltLineModeStateContradiction, _) -> acp+ (AltLineModeStateNone, x) -> acp { _acp_forceMLFlag = x }+ (AltLineModeStateForceSL, AltLineModeStateForceSL) -> acp+ (AltLineModeStateForceML{}, AltLineModeStateForceML{}) ->+ acp { _acp_forceMLFlag = s }+ _ -> acp { _acp_forceMLFlag = AltLineModeStateContradiction }+++-- removes any BDAlt's from the BriDoc+transformAlts+ :: forall r w s+ . ( Data.HList.ContainsType.ContainsType Config r+ , Data.HList.ContainsType.ContainsType (Seq String) w+ )+ => BriDocNumbered+ -> MultiRWSS.MultiRWS r w s BriDoc+transformAlts =+ MultiRWSS.withMultiStateA (AltCurPos 0 0 0 AltLineModeStateNone)+ . Memo.startEvalMemoT+ . fmap unwrapBriDocNumbered+ . rec+ where+ -- this function is exponential by nature and cannot be improved in any+ -- way i can think of, and i've tried. (stupid StableNames.)+ -- transWrap :: BriDoc -> BriDocNumbered+ -- transWrap brDc = flip StateS.evalState (1::Int)+ -- $ Memo.startEvalMemoT+ -- $ go brDc+ -- where+ -- incGet = StateS.get >>= \i -> StateS.put (i+1) $> i+ -- go :: BriDoc -> Memo.MemoT BriDoc BriDocNumbered (StateS.State Int) BriDocNumbered+ -- go = Memo.memo $ \bdX -> do+ -- i <- lift $ incGet+ -- fmap (\bd' -> (i,bd')) $ case bdX of+ -- BDEmpty -> return $ BDFEmpty+ -- BDLit t -> return $ BDFLit t+ -- BDSeq list -> BDFSeq <$> go `mapM` list+ -- BDCols sig list -> BDFCols sig <$> go `mapM` list+ -- BDSeparator -> return $ BDFSeparator+ -- BDAddBaseY ind bd -> BDFAddBaseY ind <$> go bd+ -- BDSetBaseY bd -> BDFSetBaseY <$> go bd+ -- BDSetIndentLevel bd -> BDFSetIndentLevel <$> go bd+ -- BDPar ind line indented -> [ BDFPar ind line' indented'+ -- | line' <- go line+ -- , indented' <- go indented+ -- ]+ -- BDAlt alts -> BDFAlt <$> go `mapM` alts -- not that this will happen+ -- BDForceMultiline bd -> BDFForceMultiline <$> go bd+ -- BDForceSingleline bd -> BDFForceSingleline <$> go bd+ -- BDForwardLineMode bd -> BDFForwardLineMode <$> go bd+ -- BDExternal k ks c t -> return $ BDFExternal k ks c t+ -- BDAnnotationPrior annKey bd -> BDFAnnotationPrior annKey <$> go bd+ -- BDAnnotationPost annKey bd -> BDFAnnotationRest annKey <$> go bd+ -- BDLines lines -> BDFLines <$> go `mapM` lines+ -- BDEnsureIndent ind bd -> BDFEnsureIndent ind <$> go bd+ -- BDProhibitMTEL bd -> BDFProhibitMTEL <$> go bd++++ rec+ :: BriDocNumbered+ -> Memo.MemoT+ Int+ [VerticalSpacing]+ (MultiRWSS.MultiRWS r w (AltCurPos ': s))+ BriDocNumbered+ rec bdX@(brDcId, brDc) = do+ let reWrap = (,) brDcId+ -- debugAcp :: AltCurPos <- mGet+ case brDc of+ -- BDWrapAnnKey annKey bd -> do+ -- acp <- mGet+ -- mSet $ acp { _acp_forceMLFlag = altLineModeDecay $ _acp_forceMLFlag acp }+ -- BDWrapAnnKey annKey <$> rec bd+ BDFEmpty{} -> processSpacingSimple bdX $> bdX+ BDFLit{} -> processSpacingSimple bdX $> bdX+ BDFSeq list -> reWrap . BDFSeq <$> list `forM` rec+ BDFCols sig list -> reWrap . BDFCols sig <$> list `forM` rec+ BDFSeparator -> processSpacingSimple bdX $> bdX+ BDFAddBaseY indent bd -> do+ acp <- mGet+ indAdd <- fixIndentationForMultiple acp indent+ mSet $ acp { _acp_indentPrep = max (_acp_indentPrep acp) indAdd }+ r <- rec bd+ acp' <- mGet+ mSet $ acp' { _acp_indent = _acp_indent acp }+ return $ case indent of+ BrIndentNone -> r+ BrIndentRegular -> reWrap $ BDFAddBaseY (BrIndentSpecial indAdd) r+ BrIndentSpecial i -> reWrap $ BDFAddBaseY (BrIndentSpecial i) r+ BDFBaseYPushCur bd -> do+ acp <- mGet+ mSet $ acp { _acp_indent = _acp_line acp }+ r <- rec bd+ return $ reWrap $ BDFBaseYPushCur r+ BDFBaseYPop bd -> do+ acp <- mGet+ r <- rec bd+ acp' <- mGet+ mSet $ acp' { _acp_indent = _acp_indentPrep acp }+ return $ reWrap $ BDFBaseYPop r+ BDFIndentLevelPushCur bd -> do+ reWrap . BDFIndentLevelPushCur <$> rec bd+ BDFIndentLevelPop bd -> do+ reWrap . BDFIndentLevelPop <$> rec bd+ BDFPar indent sameLine indented -> do+ indAmount <-+ mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ let+ indAdd = case indent of+ BrIndentNone -> 0+ BrIndentRegular -> indAmount+ BrIndentSpecial i -> i+ acp <- mGet+ let ind = _acp_indent acp + _acp_indentPrep acp + indAdd+ mSet $ acp { _acp_indent = ind, _acp_indentPrep = 0 }+ sameLine' <- rec sameLine+ mModify $ \acp' -> acp' { _acp_line = ind, _acp_indent = ind }+ indented' <- rec indented+ return $ reWrap $ BDFPar indent sameLine' indented'+ BDFAlt [] -> error "empty BDAlt" -- returning BDEmpty instead is a+ -- possibility, but i will prefer a+ -- fail-early approach; BDEmpty does not+ -- make sense semantically for Alt[].+ BDFAlt alts -> do+ altChooser <- mAsk <&> _conf_layout .> _lconfig_altChooser .> confUnpack+ case altChooser of+ AltChooserSimpleQuick -> do+ rec $ head alts+ AltChooserShallowBest -> do+ spacings <- alts `forM` getSpacing+ acp <- mGet+ let+ lineCheck LineModeInvalid = False+ lineCheck (LineModeValid (VerticalSpacing _ p _)) =+ case _acp_forceMLFlag acp of+ AltLineModeStateNone -> True+ AltLineModeStateForceSL{} -> p == VerticalSpacingParNone+ AltLineModeStateForceML{} -> p /= VerticalSpacingParNone+ AltLineModeStateContradiction -> False+ -- TODO: use COMPLETE pragma instead?+ lineCheck _ = error "ghc exhaustive check is insufficient"+ lconf <- _conf_layout <$> mAsk+ let+ options = -- trace ("considering options:" ++ show (length alts, acp)) $+ (zip spacings alts+ <&> \(vs, bd) -> -- trace ("spacing=" ++ show vs ++ ",hasSpace=" ++ show (hasSpace lconf acp vs) ++ ",lineCheck=" ++ show (lineCheck vs))+ (hasSpace1 lconf acp vs && lineCheck vs, bd)+ )+ rec+ $ fromMaybe (-- trace ("choosing last") $+ List.last alts)+ $ Data.List.Extra.firstJust+ (\(_i :: Int, (b, x)) ->+ [ -- traceShow ("choosing option " ++ show i) $+ x+ | b+ ]+ )+ $ zip [1 ..] options+ AltChooserBoundedSearch limit -> do+ spacings <- alts `forM` getSpacings limit+ acp <- mGet+ let+ lineCheck (VerticalSpacing _ p _) = case _acp_forceMLFlag acp of+ AltLineModeStateNone -> True+ AltLineModeStateForceSL{} -> p == VerticalSpacingParNone+ AltLineModeStateForceML{} -> p /= VerticalSpacingParNone+ AltLineModeStateContradiction -> False+ lconf <- _conf_layout <$> mAsk+ let+ options = -- trace ("considering options:" ++ show (length alts, acp)) $+ (zip spacings alts+ <&> \(vs, bd) -> -- trace ("spacing=" ++ show vs ++ ",hasSpace=" ++ show (hasSpace lconf acp vs) ++ ",lineCheck=" ++ show (lineCheck vs))+ (any (hasSpace2 lconf acp) vs && any lineCheck vs, bd)+ )+ let+ checkedOptions :: [Maybe (Int, BriDocNumbered)] =+ zip [1 ..] options <&> (\(i, (b, x)) -> [ (i, x) | b ])+ rec+ $ fromMaybe (-- trace ("choosing last") $+ List.last alts)+ $ Data.List.Extra.firstJust (fmap snd) checkedOptions+ BDFForceMultiline bd -> do+ acp <- mGet+ x <- do+ mSet $ mergeLineMode acp (AltLineModeStateForceML False)+ rec bd+ acp' <- mGet+ mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }+ return $ x+ BDFForceSingleline bd -> do+ acp <- mGet+ x <- do+ mSet $ mergeLineMode acp AltLineModeStateForceSL+ rec bd+ acp' <- mGet+ mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }+ return $ x+ BDFForwardLineMode bd -> do+ acp <- mGet+ x <- do+ mSet $ acp+ { _acp_forceMLFlag = altLineModeRefresh $ _acp_forceMLFlag acp+ }+ rec bd+ acp' <- mGet+ mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }+ return $ x+ BDFExternal{} -> processSpacingSimple bdX $> bdX+ BDFPlain{} -> processSpacingSimple bdX $> bdX+ BDFAnnotationPrior annKey bd -> do+ acp <- mGet+ mSet+ $ acp { _acp_forceMLFlag = altLineModeDecay $ _acp_forceMLFlag acp }+ bd' <- rec bd+ return $ reWrap $ BDFAnnotationPrior annKey bd'+ BDFAnnotationRest annKey bd ->+ reWrap . BDFAnnotationRest annKey <$> rec bd+ BDFAnnotationKW annKey kw bd ->+ reWrap . BDFAnnotationKW annKey kw <$> rec bd+ BDFMoveToKWDP annKey kw b bd ->+ reWrap . BDFMoveToKWDP annKey kw b <$> rec bd+ BDFLines [] -> return $ reWrap BDFEmpty -- evil transformation. or harmless.+ BDFLines (l : lr) -> do+ ind <- _acp_indent <$> mGet+ l' <- rec l+ lr' <- lr `forM` \x -> do+ mModify $ \acp -> acp { _acp_line = ind, _acp_indent = ind }+ rec x+ return $ reWrap $ BDFLines (l' : lr')+ BDFEnsureIndent indent bd -> do+ acp <- mGet+ indAdd <- fixIndentationForMultiple acp indent+ mSet $ acp+ { _acp_indentPrep = 0+ -- TODO: i am not sure this is valid, in general.+ , _acp_indent = _acp_indent acp + indAdd+ , _acp_line = max (_acp_line acp) (_acp_indent acp + indAdd)+ -- we cannot use just _acp_line acp + indAdd because of the case+ -- where there are multiple BDFEnsureIndents in the same line.+ -- Then, the actual indentation is relative to the current+ -- indentation, not the current cursor position.+ }+ r <- rec bd+ acp' <- mGet+ mSet $ acp' { _acp_indent = _acp_indent acp }+ return $ case indent of+ BrIndentNone -> r+ BrIndentRegular ->+ reWrap $ BDFEnsureIndent (BrIndentSpecial indAdd) r+ BrIndentSpecial i -> reWrap $ BDFEnsureIndent (BrIndentSpecial i) r+ BDFNonBottomSpacing _ bd -> rec bd+ BDFSetParSpacing bd -> rec bd+ BDFForceParSpacing bd -> rec bd+ BDFDebug s bd -> do+ acp :: AltCurPos <- mGet+ tellDebugMess+ $ "transformAlts: BDFDEBUG "+ ++ s+ ++ " (node-id="+ ++ show brDcId+ ++ "): acp="+ ++ show acp+ reWrap . BDFDebug s <$> rec bd+ processSpacingSimple+ :: ( MonadMultiReader Config m+ , MonadMultiState AltCurPos m+ , MonadMultiWriter (Seq String) m+ )+ => BriDocNumbered+ -> m ()+ processSpacingSimple bd = getSpacing bd >>= \case+ LineModeInvalid -> error "processSpacingSimple inv"+ LineModeValid (VerticalSpacing i VerticalSpacingParNone _) -> do+ acp <- mGet+ mSet $ acp { _acp_line = _acp_line acp + i }+ LineModeValid VerticalSpacing{} -> error "processSpacingSimple par"+ _ -> error "ghc exhaustive check is insufficient"+ hasSpace1+ :: LayoutConfig -> AltCurPos -> LineModeValidity VerticalSpacing -> Bool+ hasSpace1 _ _ LineModeInvalid = False+ hasSpace1 lconf acp (LineModeValid vs) = hasSpace2 lconf acp vs+ hasSpace1 _ _ _ = error "ghc exhaustive check is insufficient"+ hasSpace2 :: LayoutConfig -> AltCurPos -> VerticalSpacing -> Bool+ hasSpace2 lconf (AltCurPos line _indent _ _) (VerticalSpacing sameLine VerticalSpacingParNone _)+ = line + sameLine <= confUnpack (_lconfig_cols lconf)+ hasSpace2 lconf (AltCurPos line indent indentPrep _) (VerticalSpacing sameLine (VerticalSpacingParSome par) _)+ = line+ + sameLine+ <= confUnpack (_lconfig_cols lconf)+ && indent+ + indentPrep+ + par+ <= confUnpack (_lconfig_cols lconf)+ hasSpace2 lconf (AltCurPos line _indent _ _) (VerticalSpacing sameLine VerticalSpacingParAlways{} _)+ = line + sameLine <= confUnpack (_lconfig_cols lconf)++getSpacing+ :: forall m+ . (MonadMultiReader Config m, MonadMultiWriter (Seq String) m)+ => BriDocNumbered+ -> m (LineModeValidity VerticalSpacing)+getSpacing !bridoc = rec bridoc+ where+ rec :: BriDocNumbered -> m (LineModeValidity VerticalSpacing)+ rec (brDcId, brDc) = do+ config <- mAsk+ let colMax = config & _conf_layout & _lconfig_cols & confUnpack+ result <- case brDc of+ -- BDWrapAnnKey _annKey bd -> rec bd+ BDFEmpty ->+ return $ LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False+ BDFLit t -> return $ LineModeValid $ VerticalSpacing+ (Text.length t)+ VerticalSpacingParNone+ False+ BDFSeq list -> sumVs <$> rec `mapM` list+ BDFCols _sig list -> sumVs <$> rec `mapM` list+ BDFSeparator ->+ return $ LineModeValid $ VerticalSpacing 1 VerticalSpacingParNone False+ BDFAddBaseY indent bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs+ { _vs_paragraph = case _vs_paragraph vs of+ VerticalSpacingParNone -> VerticalSpacingParNone+ VerticalSpacingParAlways i ->+ VerticalSpacingParAlways $ case indent of+ BrIndentNone -> i+ BrIndentRegular ->+ i+ + (confUnpack+ $ _lconfig_indentAmount+ $ _conf_layout+ $ config+ )+ BrIndentSpecial j -> i + j+ VerticalSpacingParSome i -> VerticalSpacingParSome $ case indent of+ BrIndentNone -> i+ BrIndentRegular ->+ i + (confUnpack $ _lconfig_indentAmount $ _conf_layout $ config)+ BrIndentSpecial j -> i + j+ }+ BDFBaseYPushCur bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs+ -- We leave par as-is, even though it technically is not+ -- accurate (in general).+ -- the reason is that we really want to _keep_ it Just if it is+ -- just so we properly communicate the is-multiline fact.+ -- An alternative would be setting to (Just 0).+ { _vs_sameLine = max+ (_vs_sameLine vs)+ (case _vs_paragraph vs of+ VerticalSpacingParNone -> 0+ VerticalSpacingParSome i -> i+ VerticalSpacingParAlways i -> min colMax i+ )+ , _vs_paragraph = VerticalSpacingParSome 0+ }+ BDFBaseYPop bd -> rec bd+ BDFIndentLevelPushCur bd -> rec bd+ BDFIndentLevelPop bd -> rec bd+ BDFPar BrIndentNone sameLine indented -> do+ mVs <- rec sameLine+ mIndSp <- rec indented+ return+ $ [ VerticalSpacing lsp pspResult parFlagResult+ | VerticalSpacing lsp mPsp _ <- mVs+ , indSp <- mIndSp+ , lineMax <- getMaxVS $ mIndSp+ , let+ pspResult = case mPsp of+ VerticalSpacingParSome psp ->+ VerticalSpacingParSome $ max psp lineMax+ VerticalSpacingParNone -> VerticalSpacingParSome $ lineMax+ VerticalSpacingParAlways psp ->+ VerticalSpacingParAlways $ max psp lineMax+ , let+ parFlagResult =+ mPsp+ == VerticalSpacingParNone+ && _vs_paragraph indSp+ == VerticalSpacingParNone+ && _vs_parFlag indSp+ ]+ BDFPar{} -> error "BDPar with indent in getSpacing"+ BDFAlt [] -> error "empty BDAlt"+ BDFAlt (alt : _) -> rec alt+ BDFForceMultiline bd -> do+ mVs <- rec bd+ return $ mVs >>= _vs_paragraph .> \case+ VerticalSpacingParNone -> LineModeInvalid+ _ -> mVs+ BDFForceSingleline bd -> do+ mVs <- rec bd+ return $ mVs >>= _vs_paragraph .> \case+ VerticalSpacingParNone -> mVs+ _ -> LineModeInvalid+ BDFForwardLineMode bd -> rec bd+ BDFExternal _ _ _ txt -> return $ LineModeValid $ case Text.lines txt of+ [t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False+ _ -> VerticalSpacing 999 VerticalSpacingParNone False+ BDFPlain txt -> return $ LineModeValid $ case Text.lines txt of+ [t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False+ _ -> VerticalSpacing 999 VerticalSpacingParNone False+ BDFAnnotationPrior _annKey bd -> rec bd+ BDFAnnotationKW _annKey _kw bd -> rec bd+ BDFAnnotationRest _annKey bd -> rec bd+ BDFMoveToKWDP _annKey _kw _b bd -> rec bd+ BDFLines [] ->+ return $ LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False+ BDFLines ls@(_ : _) -> do+ lSps <- rec `mapM` ls+ let (mVs : _) = lSps -- separated into let to avoid MonadFail+ return+ $ [ VerticalSpacing lsp (VerticalSpacingParSome $ lineMax) False+ | VerticalSpacing lsp _ _ <- mVs+ , lineMax <- getMaxVS $ maxVs $ lSps+ ]+ BDFEnsureIndent indent bd -> do+ mVs <- rec bd+ let+ addInd = case indent of+ BrIndentNone -> 0+ BrIndentRegular ->+ confUnpack $ _lconfig_indentAmount $ _conf_layout $ config+ BrIndentSpecial i -> i+ return $ mVs <&> \(VerticalSpacing lsp psp pf) ->+ VerticalSpacing (lsp + addInd) psp pf+ BDFNonBottomSpacing b bd -> do+ mVs <- rec bd+ return $ mVs <|> LineModeValid+ (VerticalSpacing+ 0+ (if b+ then VerticalSpacingParSome 0+ else VerticalSpacingParAlways colMax+ )+ False+ )+ BDFSetParSpacing bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs { _vs_parFlag = True }+ BDFForceParSpacing bd -> do+ mVs <- rec bd+ return+ $ [ vs+ | vs <- mVs+ , _vs_parFlag vs || _vs_paragraph vs == VerticalSpacingParNone+ ]+ BDFDebug s bd -> do+ r <- rec bd+ tellDebugMess+ $ "getSpacing: BDFDebug "+ ++ show s+ ++ " (node-id="+ ++ show brDcId+ ++ "): mVs="+ ++ show r+ return r+ return result+ maxVs+ :: [LineModeValidity VerticalSpacing] -> LineModeValidity VerticalSpacing+ maxVs = foldl'+ (liftM2+ (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) -> VerticalSpacing+ (max x1 y1)+ (case (x2, y2) of+ (x, VerticalSpacingParNone) -> x+ (VerticalSpacingParNone, x) -> x+ (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParSome j, VerticalSpacingParAlways i) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParSome x, VerticalSpacingParSome y) ->+ VerticalSpacingParSome $ max x y+ )+ False+ )+ )+ (LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False)+ sumVs+ :: [LineModeValidity VerticalSpacing] -> LineModeValidity VerticalSpacing+ sumVs sps = foldl' (liftM2 go) initial sps+ where+ go (VerticalSpacing x1 x2 x3) (VerticalSpacing y1 y2 _) = VerticalSpacing+ (x1 + y1)+ (case (x2, y2) of+ (x, VerticalSpacingParNone) -> x+ (VerticalSpacingParNone, x) -> x+ (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParSome x, VerticalSpacingParSome y) ->+ VerticalSpacingParSome $ x + y+ )+ x3+ singleline (LineModeValid x) = _vs_paragraph x == VerticalSpacingParNone+ singleline _ = False+ isPar (LineModeValid x) = _vs_parFlag x+ isPar _ = False+ parFlag = case sps of+ [] -> True+ _ -> all singleline (List.init sps) && isPar (List.last sps)+ initial = LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone parFlag+ getMaxVS :: LineModeValidity VerticalSpacing -> LineModeValidity Int+ getMaxVS = fmap $ \(VerticalSpacing x1 x2 _) -> x1 `max` case x2 of+ VerticalSpacingParSome i -> i+ VerticalSpacingParNone -> 0+ VerticalSpacingParAlways i -> i++data SpecialCompare = Unequal | Smaller | Bigger++getSpacings+ :: forall m+ . (MonadMultiReader Config m, MonadMultiWriter (Seq String) m)+ => Int+ -> BriDocNumbered+ -> Memo.MemoT Int [VerticalSpacing] m [VerticalSpacing]+getSpacings limit bridoc = preFilterLimit <$> rec bridoc+ where+ -- when we do `take K . filter someCondition` on a list of spacings, we+ -- need to first (also) limit the size of the input list, otherwise a+ -- _large_ input with a similarly _large_ prefix not passing our filtering+ -- process could lead to exponential runtime behaviour.+ -- TODO: 3 is arbitrary.+ preFilterLimit :: [VerticalSpacing] -> [VerticalSpacing]+ preFilterLimit = take (3 * limit)+ memoWithKey :: Memo.MonadMemo k v m1 => k -> m1 v -> m1 v+ memoWithKey k v = Memo.memo (const v) k+ rec :: BriDocNumbered -> Memo.MemoT Int [VerticalSpacing] m [VerticalSpacing]+ rec (brDcId, brdc) = memoWithKey brDcId $ do+ config <- mAsk+ let colMax = config & _conf_layout & _lconfig_cols & confUnpack+ let+ hasOkColCount (VerticalSpacing lsp psp _) = lsp <= colMax && case psp of+ VerticalSpacingParNone -> True+ VerticalSpacingParSome i -> i <= colMax+ VerticalSpacingParAlways{} -> True+ let+ specialCompare vs1 vs2 =+ if ((_vs_sameLine vs1 == _vs_sameLine vs2)+ && (_vs_parFlag vs1 == _vs_parFlag vs2)+ )+ then case (_vs_paragraph vs1, _vs_paragraph vs2) of+ (VerticalSpacingParAlways i1, VerticalSpacingParAlways i2) ->+ if i1 < i2 then Smaller else Bigger+ (p1, p2) -> if p1 == p2 then Smaller else Unequal+ else Unequal+ let+ allowHangingQuasiQuotes =+ config & _conf_layout & _lconfig_allowHangingQuasiQuotes & confUnpack+ let -- this is like List.nub, with one difference: if two elements+ -- are unequal only in _vs_paragraph, with both ParAlways, we+ -- treat them like equals and replace the first occurence with the+ -- smallest member of this "equal group".+ specialNub :: [VerticalSpacing] -> [VerticalSpacing]+ specialNub [] = []+ specialNub (x1 : xr) = case go x1 xr of+ (r, xs') -> r : specialNub xs'+ where+ go y1 [] = (y1, [])+ go y1 (y2 : yr) = case specialCompare y1 y2 of+ Unequal -> let (r, yr') = go y1 yr in (r, y2 : yr')+ Smaller -> go y1 yr+ Bigger -> go y2 yr+ let -- the standard function used to enforce a constant upper bound+ -- on the number of elements returned for each node. Should be+ -- applied whenever in a parent the combination of spacings from+ -- its children might cause excess of the upper bound.+ filterAndLimit :: [VerticalSpacing] -> [VerticalSpacing]+ filterAndLimit =+ take limit+ -- prune so we always consider a constant+ -- amount of spacings per node of the BriDoc.+ . specialNub+ -- In the end we want to know if there is at least+ -- one valid spacing for any alternative.+ -- If there are duplicates in the list, then these+ -- will either all be valid (so having more than the+ -- first is pointless) or all invalid (in which+ -- case having any of them is pointless).+ -- Nonetheless I think the order of spacings should+ -- be preserved as it provides a deterministic+ -- choice for which spacings to prune (which is+ -- an argument against simply using a Set).+ -- I have also considered `fmap head . group` which+ -- seems to work similarly well for common cases+ -- and which might behave even better when it comes+ -- to determinism of the algorithm. But determinism+ -- should not be overrated here either - in the end+ -- this is about deterministic behaviour of the+ -- pruning we do that potentially results in+ -- non-optimal layouts, and we'd rather take optimal+ -- layouts when we can than take non-optimal layouts+ -- just to be consistent with other cases where+ -- we'd choose non-optimal layouts.+ . filter hasOkColCount+ -- throw out any spacings (i.e. children) that+ -- already use more columns than available in+ -- total.+ . preFilterLimit+ result <- case brdc of+ -- BDWrapAnnKey _annKey bd -> rec bd+ BDFEmpty -> return $ [VerticalSpacing 0 VerticalSpacingParNone False]+ BDFLit t ->+ return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]+ BDFSeq list -> fmap sumVs . mapM filterAndLimit <$> rec `mapM` list+ BDFCols _sig list -> fmap sumVs . mapM filterAndLimit <$> rec `mapM` list+ BDFSeparator -> return $ [VerticalSpacing 1 VerticalSpacingParNone False]+ BDFAddBaseY indent bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs+ { _vs_paragraph = case _vs_paragraph vs of+ VerticalSpacingParNone -> VerticalSpacingParNone+ VerticalSpacingParAlways i ->+ VerticalSpacingParAlways $ case indent of+ BrIndentNone -> i+ BrIndentRegular ->+ i+ + (confUnpack+ $ _lconfig_indentAmount+ $ _conf_layout+ $ config+ )+ BrIndentSpecial j -> i + j+ VerticalSpacingParSome i -> VerticalSpacingParSome $ case indent of+ BrIndentNone -> i+ BrIndentRegular ->+ i + (confUnpack $ _lconfig_indentAmount $ _conf_layout $ config)+ BrIndentSpecial j -> i + j+ }+ BDFBaseYPushCur bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs+ -- We leave par as-is, even though it technically is not+ -- accurate (in general).+ -- the reason is that we really want to _keep_ it Just if it is+ -- just so we properly communicate the is-multiline fact.+ -- An alternative would be setting to (Just 0).+ { _vs_sameLine = max+ (_vs_sameLine vs)+ (case _vs_paragraph vs of+ VerticalSpacingParNone -> 0+ VerticalSpacingParSome i -> i+ VerticalSpacingParAlways i -> min colMax i+ )+ , _vs_paragraph = case _vs_paragraph vs of+ VerticalSpacingParNone -> VerticalSpacingParNone+ VerticalSpacingParSome i -> VerticalSpacingParSome i+ VerticalSpacingParAlways i -> VerticalSpacingParAlways i+ }+ BDFBaseYPop bd -> rec bd+ BDFIndentLevelPushCur bd -> rec bd+ BDFIndentLevelPop bd -> rec bd+ BDFPar BrIndentNone sameLine indented -> do+ mVss <- filterAndLimit <$> rec sameLine+ indSps <- filterAndLimit <$> rec indented+ let mVsIndSp = take limit $ [ (x, y) | x <- mVss, y <- indSps ]+ return $ mVsIndSp <&> \(VerticalSpacing lsp mPsp _, indSp) ->+ VerticalSpacing+ lsp+ (case mPsp of+ VerticalSpacingParSome psp ->+ VerticalSpacingParSome $ max psp $ getMaxVS indSp -- TODO+ VerticalSpacingParNone -> spMakePar indSp+ VerticalSpacingParAlways psp ->+ VerticalSpacingParAlways $ max psp $ getMaxVS indSp+ )+ (mPsp+ == VerticalSpacingParNone+ && _vs_paragraph indSp+ == VerticalSpacingParNone+ && _vs_parFlag indSp+ )++ BDFPar{} -> error "BDPar with indent in getSpacing"+ BDFAlt [] -> error "empty BDAlt"+ -- BDAlt (alt:_) -> rec alt+ BDFAlt alts -> do+ r <- rec `mapM` alts+ return $ filterAndLimit =<< r+ BDFForceMultiline bd -> do+ mVs <- filterAndLimit <$> rec bd+ return $ filter ((/= VerticalSpacingParNone) . _vs_paragraph) mVs+ BDFForceSingleline bd -> do+ mVs <- filterAndLimit <$> rec bd+ return $ filter ((== VerticalSpacingParNone) . _vs_paragraph) mVs+ BDFForwardLineMode bd -> rec bd+ BDFExternal _ _ _ txt | [t] <- Text.lines txt ->+ return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]+ BDFExternal{} -> return $ [] -- yes, we just assume that we cannot properly layout+ -- this.+ BDFPlain t -> return+ [ case Text.lines t of+ [] -> VerticalSpacing 0 VerticalSpacingParNone False+ [t1] ->+ VerticalSpacing (Text.length t1) VerticalSpacingParNone False+ (t1 : _) ->+ VerticalSpacing (Text.length t1) (VerticalSpacingParAlways 0) True+ | allowHangingQuasiQuotes+ ]+ BDFAnnotationPrior _annKey bd -> rec bd+ BDFAnnotationKW _annKey _kw bd -> rec bd+ BDFAnnotationRest _annKey bd -> rec bd+ BDFMoveToKWDP _annKey _kw _b bd -> rec bd+ BDFLines [] -> return $ [VerticalSpacing 0 VerticalSpacingParNone False]+ BDFLines ls@(_ : _) -> do+ -- we simply assume that lines is only used "properly", i.e. in+ -- such a way that the first line can be treated "as a part of the+ -- paragraph". That most importantly means that Lines should never+ -- be inserted anywhere but at the start of the line. A+ -- counterexample would be anything like Seq[Lit "foo", Lines].+ lSpss <- map filterAndLimit <$> rec `mapM` ls+ let+ worbled = fmap reverse $ sequence $ reverse $ lSpss+ sumF lSps@(lSp1 : _) =+ VerticalSpacing (_vs_sameLine lSp1) (spMakePar $ maxVs lSps) False+ sumF [] =+ error+ $ "should not happen. if my logic does not fail"+ ++ "me, this follows from not (null ls)."+ return $ sumF <$> worbled+ -- lSpss@(mVs:_) <- rec `mapM` ls+ -- return $ case Control.Lens.transposeOf traverse lSpss of -- TODO: we currently only+ -- -- consider the first alternative for the+ -- -- line's spacings.+ -- -- also i am not sure if always including+ -- -- the first line length in the paragraph+ -- -- length gives the desired results.+ -- -- it is the safe path though, for now.+ -- [] -> []+ -- (lSps:_) -> mVs <&> \(VerticalSpacing lsp _) ->+ -- VerticalSpacing lsp $ VerticalSpacingParSome $ getMaxVS $ maxVs lSps+ BDFEnsureIndent indent bd -> do+ mVs <- rec bd+ let+ addInd = case indent of+ BrIndentNone -> 0+ BrIndentRegular ->+ confUnpack $ _lconfig_indentAmount $ _conf_layout $ config+ BrIndentSpecial i -> i+ return $ mVs <&> \(VerticalSpacing lsp psp parFlag) ->+ VerticalSpacing (lsp + addInd) psp parFlag+ BDFNonBottomSpacing b bd -> do+ -- TODO: the `b` flag is an ugly hack, but I was not able to make+ -- all tests work without it. It should be possible to have+ -- `spMakePar` map VSPAlways{} to VSPSome x1, which fixes this+ -- problem but breaks certain other cases.+ mVs <- rec bd+ return $ if null mVs+ then+ [ VerticalSpacing+ 0+ (if b+ then VerticalSpacingParSome 0+ else VerticalSpacingParAlways colMax+ )+ False+ ]+ else mVs <&> \vs -> vs+ { _vs_sameLine = min colMax (_vs_sameLine vs)+ , _vs_paragraph = case _vs_paragraph vs of+ VerticalSpacingParNone -> VerticalSpacingParNone+ VerticalSpacingParAlways i+ | b -> VerticalSpacingParSome 0+ | otherwise -> VerticalSpacingParAlways i+ VerticalSpacingParSome i+ | b -> VerticalSpacingParSome 0+ | otherwise -> VerticalSpacingParAlways i+ }+ -- the version below is an alternative idea: fold the input+ -- spacings into a single spacing. This was hoped to improve in+ -- certain cases where non-bottom alternatives took up "too much+ -- explored search space"; the downside is that it also cuts+ -- the search-space short in other cases where it is not necessary,+ -- leading to unnecessary new-lines. Disabled for now. A better+ -- solution would require conditionally folding the search-space+ -- only in appropriate locations (i.e. a new BriDoc node type+ -- for this purpose, perhaps "BDFNonBottomSpacing1").+ -- else+ -- [ Foldable.foldl1+ -- (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) ->+ -- VerticalSpacing+ -- (min x1 y1)+ -- (case (x2, y2) of+ -- (x, VerticalSpacingParNone) -> x+ -- (VerticalSpacingParNone, x) -> x+ -- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->+ -- VerticalSpacingParAlways $ min i j+ -- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->+ -- VerticalSpacingParAlways $ min i j+ -- (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->+ -- VerticalSpacingParAlways $ min i j+ -- (VerticalSpacingParSome x, VerticalSpacingParSome y) ->+ -- VerticalSpacingParSome $ min x y)+ -- False)+ -- mVs+ -- ]+ BDFSetParSpacing bd -> do+ mVs <- rec bd+ return $ mVs <&> \vs -> vs { _vs_parFlag = True }+ BDFForceParSpacing bd -> do+ mVs <- preFilterLimit <$> rec bd+ return+ $ [ vs+ | vs <- mVs+ , _vs_parFlag vs || _vs_paragraph vs == VerticalSpacingParNone+ ]+ BDFDebug s bd -> do+ r <- rec bd+ tellDebugMess+ $ "getSpacings: BDFDebug "+ ++ show s+ ++ " (node-id="+ ++ show brDcId+ ++ "): vs="+ ++ show (take 9 r)+ return r+ return result+ maxVs :: [VerticalSpacing] -> VerticalSpacing+ maxVs = foldl'+ (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) -> VerticalSpacing+ (max x1 y1)+ (case (x2, y2) of+ (x, VerticalSpacingParNone) -> x+ (VerticalSpacingParNone, x) -> x+ (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ max i j+ (VerticalSpacingParSome x, VerticalSpacingParSome y) ->+ VerticalSpacingParSome $ max x y+ )+ False+ )+ (VerticalSpacing 0 VerticalSpacingParNone False)+ sumVs :: [VerticalSpacing] -> VerticalSpacing+ sumVs sps = foldl' go initial sps+ where+ go (VerticalSpacing x1 x2 x3) (VerticalSpacing y1 y2 _) = VerticalSpacing+ (x1 + y1)+ (case (x2, y2) of+ (x, VerticalSpacingParNone) -> x+ (VerticalSpacingParNone, x) -> x+ (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->+ VerticalSpacingParAlways $ i + j+ (VerticalSpacingParSome x, VerticalSpacingParSome y) ->+ VerticalSpacingParSome $ x + y+ )+ x3+ singleline x = _vs_paragraph x == VerticalSpacingParNone+ isPar x = _vs_parFlag x+ parFlag = case sps of+ [] -> True+ _ -> all singleline (List.init sps) && isPar (List.last sps)+ initial = VerticalSpacing 0 VerticalSpacingParNone parFlag+ getMaxVS :: VerticalSpacing -> Int+ getMaxVS (VerticalSpacing x1 x2 _) = x1 `max` case x2 of+ VerticalSpacingParSome i -> i+ VerticalSpacingParNone -> 0+ VerticalSpacingParAlways i -> i+ spMakePar :: VerticalSpacing -> VerticalSpacingPar+ spMakePar (VerticalSpacing x1 x2 _) = case x2 of+ VerticalSpacingParSome i -> VerticalSpacingParSome $ x1 `max` i+ VerticalSpacingParNone -> VerticalSpacingParSome $ x1+ VerticalSpacingParAlways i -> VerticalSpacingParAlways $ x1 `max` i++fixIndentationForMultiple+ :: (MonadMultiReader (CConfig Identity) m) => AltCurPos -> BrIndent -> m Int+fixIndentationForMultiple acp indent = do+ indAmount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack+ let+ indAddRaw = case indent of+ BrIndentNone -> 0+ BrIndentRegular -> indAmount+ BrIndentSpecial i -> i+ -- for IndentPolicyMultiple, we restrict the amount of added+ -- indentation in such a manner that we end up on a multiple of the+ -- base indentation.+ indPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack+ pure $ if indPolicy == IndentPolicyMultiple+ then+ let+ indAddMultiple1 =+ indAddRaw - ((_acp_indent acp + indAddRaw) `mod` indAmount)+ indAddMultiple2 = if indAddMultiple1 <= 0+ then indAddMultiple1 + indAmount+ else indAddMultiple1+ in indAddMultiple2+ else indAddRaw
+ source/library/Brittany/Internal/Transformations/Columns.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Transformations.Columns where++import qualified Data.Generics.Uniplate.Direct as Uniplate+import qualified GHC.OldList as List+import Brittany.Internal.Prelude+import Brittany.Internal.Types++++transformSimplifyColumns :: BriDoc -> BriDoc+transformSimplifyColumns = Uniplate.rewrite $ \case+ -- BDWrapAnnKey annKey bd ->+ -- BDWrapAnnKey annKey $ transformSimplify bd+ BDEmpty -> Nothing+ BDLit{} -> Nothing+ BDSeq list+ | any+ (\case+ BDSeq{} -> True+ BDEmpty{} -> True+ _ -> False+ )+ list+ -> Just $ BDSeq $ list >>= \case+ BDEmpty -> []+ BDSeq l -> l+ x -> [x]+ BDSeq (BDCols sig1 cols1@(_ : _) : rest)+ | all+ (\case+ BDSeparator -> True+ _ -> False+ )+ rest+ -> Just $ BDCols sig1 (List.init cols1 ++ [BDSeq (List.last cols1 : rest)])+ BDLines lines+ | any+ (\case+ BDLines{} -> True+ BDEmpty{} -> True+ _ -> False+ )+ lines+ -> Just $ BDLines $ filter isNotEmpty $ lines >>= \case+ BDLines l -> l+ x -> [x]+ -- prior floating in+ BDAnnotationPrior annKey1 (BDSeq (l : lr)) ->+ Just $ BDSeq (BDAnnotationPrior annKey1 l : lr)+ BDAnnotationPrior annKey1 (BDLines (l : lr)) ->+ Just $ BDLines (BDAnnotationPrior annKey1 l : lr)+ BDAnnotationPrior annKey1 (BDCols sig (l : lr)) ->+ Just $ BDCols sig (BDAnnotationPrior annKey1 l : lr)+ -- post floating in+ BDAnnotationRest annKey1 (BDSeq list) ->+ Just $ BDSeq $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDLines list) ->+ Just+ $ BDLines+ $ List.init list+ ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDCols sig cols) ->+ Just+ $ BDCols sig+ $ List.init cols+ ++ [BDAnnotationRest annKey1 $ List.last cols]+ BDAnnotationKW annKey1 kw (BDSeq list) ->+ Just+ $ BDSeq+ $ List.init list+ ++ [BDAnnotationKW annKey1 kw $ List.last list]+ BDAnnotationKW annKey1 kw (BDLines list) ->+ Just+ $ BDLines+ $ List.init list+ ++ [BDAnnotationKW annKey1 kw $ List.last list]+ BDAnnotationKW annKey1 kw (BDCols sig cols) ->+ Just+ $ BDCols sig+ $ List.init cols+ ++ [BDAnnotationKW annKey1 kw $ List.last cols]+ -- ensureIndent float-in+ -- not sure if the following rule is necessary; tests currently are+ -- unaffected.+ -- BDEnsureIndent indent (BDLines lines) ->+ -- Just $ BDLines $ BDEnsureIndent indent <$> lines+ -- matching col special transformation+ BDCols sig1 cols1@(_ : _)+ | BDLines lines@(_ : _ : _) <- List.last cols1+ , BDCols sig2 cols2 <- List.last lines+ , sig1 == sig2+ -> Just $ BDLines+ [ BDCols sig1 $ List.init cols1 ++ [BDLines $ List.init lines]+ , BDCols sig2 cols2+ ]+ BDCols sig1 cols1@(_ : _)+ | BDLines lines@(_ : _ : _) <- List.last cols1+ , BDEnsureIndent _ (BDCols sig2 cols2) <- List.last lines+ , sig1 == sig2+ -> Just $ BDLines+ [ BDCols sig1 $ List.init cols1 ++ [BDLines $ List.init lines]+ , BDCols sig2 cols2+ ]+ BDPar ind col1@(BDCols sig1 _) col2@(BDCols sig2 _) | sig1 == sig2 ->+ Just $ BDAddBaseY ind (BDLines [col1, col2])+ BDPar ind col1@(BDCols sig1 _) (BDLines (col2@(BDCols sig2 _) : rest))+ | sig1 == sig2 -> Just $ BDPar ind (BDLines [col1, col2]) (BDLines rest)+ BDPar ind (BDLines lines1) col2@(BDCols sig2 _)+ | BDCols sig1 _ <- List.last lines1, sig1 == sig2 -> Just+ $ BDAddBaseY ind (BDLines $ lines1 ++ [col2])+ BDPar ind (BDLines lines1) (BDLines (col2@(BDCols sig2 _) : rest))+ | BDCols sig1 _ <- List.last lines1, sig1 == sig2 -> Just+ $ BDPar ind (BDLines $ lines1 ++ [col2]) (BDLines rest)+ -- BDPar ind1 (BDCols sig1 cols1) (BDPar ind2 line (BDCols sig2 cols2))+ -- | sig1==sig2 ->+ -- Just $ BDPar+ -- ind1+ -- (BDLines [BDCols sig1 cols1, BDCols sig])+ BDCols sig1 cols+ | BDPar _ind line (BDCols sig2 cols2) <- List.last cols, sig1 == sig2+ -> Just+ $ BDLines [BDCols sig1 (List.init cols ++ [line]), BDCols sig2 cols2]+ BDCols sig1 cols+ | BDPar ind line (BDLines lines) <- List.last cols+ , BDCols sig2 cols2 <- List.last lines+ , sig1 == sig2+ -> Just $ BDLines+ [ BDCols sig1+ $ List.init cols+ ++ [BDPar ind line (BDLines $ List.init lines)]+ , BDCols sig2 cols2+ ]+ BDLines [x] -> Just $ x+ BDLines [] -> Just $ BDEmpty+ BDSeq{} -> Nothing+ BDCols{} -> Nothing+ BDSeparator -> Nothing+ BDAddBaseY{} -> Nothing+ BDBaseYPushCur{} -> Nothing+ BDBaseYPop{} -> Nothing+ BDIndentLevelPushCur{} -> Nothing+ BDIndentLevelPop{} -> Nothing+ BDPar{} -> Nothing+ BDAlt{} -> Nothing+ BDForceMultiline{} -> Nothing+ BDForceSingleline{} -> Nothing+ BDForwardLineMode{} -> Nothing+ BDExternal{} -> Nothing+ BDPlain{} -> Nothing+ BDLines{} -> Nothing+ BDAnnotationPrior{} -> Nothing+ BDAnnotationKW{} -> Nothing+ BDAnnotationRest{} -> Nothing+ BDMoveToKWDP{} -> Nothing+ BDEnsureIndent{} -> Nothing+ BDSetParSpacing{} -> Nothing+ BDForceParSpacing{} -> Nothing+ BDDebug{} -> Nothing+ BDNonBottomSpacing _ x -> Just x
+ source/library/Brittany/Internal/Transformations/Floating.hs view
@@ -0,0 +1,213 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Transformations.Floating where++import qualified Data.Generics.Uniplate.Direct as Uniplate+import qualified GHC.OldList as List+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils++++-- note that this is not total, and cannot be with that exact signature.+mergeIndents :: BrIndent -> BrIndent -> BrIndent+mergeIndents BrIndentNone x = x+mergeIndents x BrIndentNone = x+mergeIndents (BrIndentSpecial i) (BrIndentSpecial j) =+ BrIndentSpecial (max i j)+mergeIndents _ _ = error "mergeIndents"+++transformSimplifyFloating :: BriDoc -> BriDoc+transformSimplifyFloating = stepBO .> stepFull+ -- note that semantically, stepFull is completely sufficient.+ -- but the bottom-up switch-to-top-down-on-match transformation has much+ -- better complexity.+ -- UPDATE: by now, stepBO does more than stepFull; for semantic equivalence+ -- the push/pop cases would need to be copied over+ where+ descendPrior = transformDownMay $ \case+ -- prior floating in+ BDAnnotationPrior annKey1 (BDPar ind line indented) ->+ Just $ BDPar ind (BDAnnotationPrior annKey1 line) indented+ BDAnnotationPrior annKey1 (BDSeq (l : lr)) ->+ Just $ BDSeq (BDAnnotationPrior annKey1 l : lr)+ BDAnnotationPrior annKey1 (BDLines (l : lr)) ->+ Just $ BDLines (BDAnnotationPrior annKey1 l : lr)+ BDAnnotationPrior annKey1 (BDCols sig (l : lr)) ->+ Just $ BDCols sig (BDAnnotationPrior annKey1 l : lr)+ BDAnnotationPrior annKey1 (BDAddBaseY indent x) ->+ Just $ BDAddBaseY indent $ BDAnnotationPrior annKey1 x+ BDAnnotationPrior annKey1 (BDDebug s x) ->+ Just $ BDDebug s $ BDAnnotationPrior annKey1 x+ _ -> Nothing+ descendRest = transformDownMay $ \case+ -- post floating in+ BDAnnotationRest annKey1 (BDPar ind line indented) ->+ Just $ BDPar ind line $ BDAnnotationRest annKey1 indented+ BDAnnotationRest annKey1 (BDSeq list) ->+ Just+ $ BDSeq+ $ List.init list+ ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDLines list) ->+ Just+ $ BDLines+ $ List.init list+ ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDCols sig cols) ->+ Just+ $ BDCols sig+ $ List.init cols+ ++ [BDAnnotationRest annKey1 $ List.last cols]+ BDAnnotationRest annKey1 (BDAddBaseY indent x) ->+ Just $ BDAddBaseY indent $ BDAnnotationRest annKey1 x+ BDAnnotationRest annKey1 (BDDebug s x) ->+ Just $ BDDebug s $ BDAnnotationRest annKey1 x+ _ -> Nothing+ descendKW = transformDownMay $ \case+ -- post floating in+ BDAnnotationKW annKey1 kw (BDPar ind line indented) ->+ Just $ BDPar ind line $ BDAnnotationKW annKey1 kw indented+ BDAnnotationKW annKey1 kw (BDSeq list) ->+ Just+ $ BDSeq+ $ List.init list+ ++ [BDAnnotationKW annKey1 kw $ List.last list]+ BDAnnotationKW annKey1 kw (BDLines list) ->+ Just+ $ BDLines+ $ List.init list+ ++ [BDAnnotationKW annKey1 kw $ List.last list]+ BDAnnotationKW annKey1 kw (BDCols sig cols) ->+ Just+ $ BDCols sig+ $ List.init cols+ ++ [BDAnnotationKW annKey1 kw $ List.last cols]+ BDAnnotationKW annKey1 kw (BDAddBaseY indent x) ->+ Just $ BDAddBaseY indent $ BDAnnotationKW annKey1 kw x+ BDAnnotationKW annKey1 kw (BDDebug s x) ->+ Just $ BDDebug s $ BDAnnotationKW annKey1 kw x+ _ -> Nothing+ descendBYPush = transformDownMay $ \case+ BDBaseYPushCur (BDCols sig cols@(_ : _)) ->+ Just $ BDCols sig (BDBaseYPushCur (List.head cols) : List.tail cols)+ BDBaseYPushCur (BDDebug s x) -> Just $ BDDebug s (BDBaseYPushCur x)+ _ -> Nothing+ descendBYPop = transformDownMay $ \case+ BDBaseYPop (BDCols sig cols@(_ : _)) ->+ Just $ BDCols sig (List.init cols ++ [BDBaseYPop (List.last cols)])+ BDBaseYPop (BDDebug s x) -> Just $ BDDebug s (BDBaseYPop x)+ _ -> Nothing+ descendILPush = transformDownMay $ \case+ BDIndentLevelPushCur (BDCols sig cols@(_ : _)) ->+ Just $ BDCols sig (BDIndentLevelPushCur (List.head cols) : List.tail cols)+ BDIndentLevelPushCur (BDDebug s x) ->+ Just $ BDDebug s (BDIndentLevelPushCur x)+ _ -> Nothing+ descendILPop = transformDownMay $ \case+ BDIndentLevelPop (BDCols sig cols@(_ : _)) ->+ Just $ BDCols sig (List.init cols ++ [BDIndentLevelPop (List.last cols)])+ BDIndentLevelPop (BDDebug s x) -> Just $ BDDebug s (BDIndentLevelPop x)+ _ -> Nothing+ descendAddB = transformDownMay $ \case+ BDAddBaseY BrIndentNone x -> Just x+ -- AddIndent floats into Lines.+ BDAddBaseY indent (BDLines lines) ->+ Just $ BDLines $ BDAddBaseY indent <$> lines+ -- AddIndent floats into last column+ BDAddBaseY indent (BDCols sig cols) ->+ Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]+ -- merge AddIndent and Par+ BDAddBaseY ind1 (BDPar ind2 line indented) ->+ Just $ BDPar (mergeIndents ind1 ind2) line indented+ BDAddBaseY ind (BDAnnotationPrior annKey1 x) ->+ Just $ BDAnnotationPrior annKey1 (BDAddBaseY ind x)+ BDAddBaseY ind (BDAnnotationRest annKey1 x) ->+ Just $ BDAnnotationRest annKey1 (BDAddBaseY ind x)+ BDAddBaseY ind (BDAnnotationKW annKey1 kw x) ->+ Just $ BDAnnotationKW annKey1 kw (BDAddBaseY ind x)+ BDAddBaseY ind (BDSeq list) ->+ Just $ BDSeq $ List.init list ++ [BDAddBaseY ind (List.last list)]+ BDAddBaseY _ lit@BDLit{} -> Just $ lit+ BDAddBaseY ind (BDBaseYPushCur x) ->+ Just $ BDBaseYPushCur (BDAddBaseY ind x)+ BDAddBaseY ind (BDBaseYPop x) -> Just $ BDBaseYPop (BDAddBaseY ind x)+ BDAddBaseY ind (BDDebug s x) -> Just $ BDDebug s (BDAddBaseY ind x)+ BDAddBaseY ind (BDIndentLevelPop x) ->+ Just $ BDIndentLevelPop (BDAddBaseY ind x)+ BDAddBaseY ind (BDIndentLevelPushCur x) ->+ Just $ BDIndentLevelPushCur (BDAddBaseY ind x)+ BDAddBaseY ind (BDEnsureIndent ind2 x) ->+ Just $ BDEnsureIndent (mergeIndents ind ind2) x+ _ -> Nothing+ stepBO :: BriDoc -> BriDoc+ stepBO = -- traceFunctionWith "stepBO" (show . briDocToDocWithAnns) (show . briDocToDocWithAnns) $+ transformUp f+ where+ f = \case+ x@BDAnnotationPrior{} -> descendPrior x+ x@BDAnnotationKW{} -> descendKW x+ x@BDAnnotationRest{} -> descendRest x+ x@BDAddBaseY{} -> descendAddB x+ x@BDBaseYPushCur{} -> descendBYPush x+ x@BDBaseYPop{} -> descendBYPop x+ x@BDIndentLevelPushCur{} -> descendILPush x+ x@BDIndentLevelPop{} -> descendILPop x+ x -> x+ stepFull = -- traceFunctionWith "stepFull" (show . briDocToDocWithAnns) (show . briDocToDocWithAnns) $+ Uniplate.rewrite $ \case+ BDAddBaseY BrIndentNone x -> Just $ x+ -- AddIndent floats into Lines.+ BDAddBaseY indent (BDLines lines) ->+ Just $ BDLines $ BDAddBaseY indent <$> lines+ -- AddIndent floats into last column+ BDAddBaseY indent (BDCols sig cols) ->+ Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]+ BDAddBaseY ind (BDSeq list) ->+ Just $ BDSeq $ List.init list ++ [BDAddBaseY ind (List.last list)]+ -- merge AddIndent and Par+ BDAddBaseY ind1 (BDPar ind2 line indented) ->+ Just $ BDPar (mergeIndents ind1 ind2) line indented+ BDAddBaseY _ lit@BDLit{} -> Just $ lit+ BDAddBaseY ind (BDBaseYPushCur x) ->+ Just $ BDBaseYPushCur (BDAddBaseY ind x)+ BDAddBaseY ind (BDBaseYPop x) -> Just $ BDBaseYPop (BDAddBaseY ind x)+ -- prior floating in+ BDAnnotationPrior annKey1 (BDPar ind line indented) ->+ Just $ BDPar ind (BDAnnotationPrior annKey1 line) indented+ BDAnnotationPrior annKey1 (BDSeq (l : lr)) ->+ Just $ BDSeq ((BDAnnotationPrior annKey1 l) : lr)+ BDAnnotationPrior annKey1 (BDLines (l : lr)) ->+ Just $ BDLines ((BDAnnotationPrior annKey1 l) : lr)+ BDAnnotationPrior annKey1 (BDCols sig (l : lr)) ->+ Just $ BDCols sig ((BDAnnotationPrior annKey1 l) : lr)+ -- EnsureIndent float-in+ -- BDEnsureIndent indent (BDCols sig (col:colr)) ->+ -- Just $ BDCols sig (BDEnsureIndent indent col : (BDAddBaseY indent <$> colr))+ -- not sure if the following rule is necessary; tests currently are+ -- unaffected.+ -- BDEnsureIndent indent (BDLines lines) ->+ -- Just $ BDLines $ BDEnsureIndent indent <$> lines+ -- post floating in+ BDAnnotationRest annKey1 (BDPar ind line indented) ->+ Just $ BDPar ind line $ BDAnnotationRest annKey1 indented+ BDAnnotationRest annKey1 (BDSeq list) ->+ Just+ $ BDSeq+ $ List.init list+ ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDLines list) ->+ Just+ $ BDLines+ $ List.init list+ ++ [BDAnnotationRest annKey1 $ List.last list]+ BDAnnotationRest annKey1 (BDCols sig cols) ->+ Just+ $ BDCols sig+ $ List.init cols+ ++ [BDAnnotationRest annKey1 $ List.last cols]+ _ -> Nothing
+ source/library/Brittany/Internal/Transformations/Indent.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Transformations.Indent where++import qualified Data.Generics.Uniplate.Direct as Uniplate+import qualified GHC.OldList as List+import Brittany.Internal.Prelude+import Brittany.Internal.Types++++-- prepare layouting by translating BDPar's, replacing them with Indents and+-- floating those in. This gives a more clear picture of what exactly is+-- affected by what amount of indentation.+transformSimplifyIndent :: BriDoc -> BriDoc+transformSimplifyIndent = Uniplate.rewrite $ \case+ BDPar ind (BDLines lines) indented ->+ Just $ BDEnsureIndent ind $ BDLines $ lines ++ [indented]+ BDPar ind (BDCols sig cols) indented ->+ Just $ BDCols sig (List.init cols ++ [BDPar ind (List.last cols) indented])+ BDPar BrIndentNone _ _ -> Nothing+ BDPar ind x indented ->+ Just $ BDPar BrIndentNone (BDAddBaseY ind x) (BDEnsureIndent ind indented)+ -- BDPar ind x indented ->+ -- Just $ BDLines+ -- [ BDAddBaseY ind x+ -- , BDEnsureIndent ind indented+ -- ]+ BDLines lines+ | any+ (\case+ BDLines{} -> True+ BDEmpty{} -> True+ _ -> False+ )+ lines+ -> Just $ BDLines $ filter isNotEmpty $ lines >>= \case+ BDLines l -> l+ x -> [x]+ BDLines [l] -> Just l+ BDAddBaseY i (BDAnnotationPrior k x) ->+ Just $ BDAnnotationPrior k (BDAddBaseY i x)+ BDAddBaseY i (BDAnnotationKW k kw x) ->+ Just $ BDAnnotationKW k kw (BDAddBaseY i x)+ BDAddBaseY i (BDAnnotationRest k x) ->+ Just $ BDAnnotationRest k (BDAddBaseY i x)+ BDAddBaseY i (BDSeq l) ->+ Just $ BDSeq $ List.init l ++ [BDAddBaseY i $ List.last l]+ BDAddBaseY i (BDCols sig l) ->+ Just $ BDCols sig $ List.init l ++ [BDAddBaseY i $ List.last l]+ BDAddBaseY _ lit@BDLit{} -> Just lit++ _ -> Nothing
+ source/library/Brittany/Internal/Transformations/Par.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Internal.Transformations.Par where++import Brittany.Internal.Prelude+import Brittany.Internal.Types+import Brittany.Internal.Utils++++transformSimplifyPar :: BriDoc -> BriDoc+transformSimplifyPar = transformUp $ \case+ -- BDPar BrIndentNone line1 line2 -> Just $ BDLines [line1, line2]+ -- BDPar line indented ->+ -- Just $ BDLines [line, indented]+ -- BDPar ind1 (BDPar ind2 line p1) p2 | ind1==ind2 ->+ -- Just $ BDPar ind1 line (BDLines [p1, p2])+ x@(BDPar _ (BDPar _ BDPar{} _) _) -> x+ BDPar ind1 (BDPar ind2 line p1) (BDLines indenteds) ->+ BDPar ind1 line (BDLines (BDEnsureIndent ind2 p1 : indenteds))+ BDPar ind1 (BDPar ind2 line p1) p2 ->+ BDPar ind1 line (BDLines [BDEnsureIndent ind2 p1, p2])+ BDLines lines+ | any+ (\case+ BDLines{} -> True+ BDEmpty{} -> True+ _ -> False+ )+ lines+ -> case go lines of+ [] -> BDEmpty+ [x] -> x+ xs -> BDLines xs+ where+ go = (=<<) $ \case+ BDLines l -> go l+ BDEmpty -> []+ x -> [x]+ BDLines [] -> BDEmpty+ BDLines [x] -> x+ -- BDCols sig cols | BDPar ind line indented <- List.last cols ->+ -- Just $ BDPar ind (BDCols sig (List.init cols ++ [line])) indented+ -- BDPar BrIndentNone line indented ->+ -- Just $ BDLines [line, indented]+ BDEnsureIndent BrIndentNone x -> x+ x -> x
+ source/library/Brittany/Internal/Types.hs view
@@ -0,0 +1,463 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}++module Brittany.Internal.Types where++import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS+import qualified Data.Data+import Data.Generics.Uniplate.Direct as Uniplate+import qualified Data.Kind as Kind+import qualified Data.Strict.Maybe as Strict+import qualified Data.Text.Lazy.Builder as Text.Builder+import GHC (AnnKeywordId, GenLocated, Located, SrcSpan)+import Brittany.Internal.Config.Types+import Brittany.Internal.Prelude+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import Language.Haskell.GHC.ExactPrint (AnnKey)+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types+import Language.Haskell.GHC.ExactPrint.Types (Anns)+import qualified Safe+++data PerItemConfig = PerItemConfig+ { _icd_perBinding :: Map String (CConfig Maybe)+ , _icd_perKey :: Map ExactPrint.Types.AnnKey (CConfig Maybe)+ }+ deriving Data.Data.Data++type PPM = MultiRWSS.MultiRWS+ '[Map ExactPrint.AnnKey ExactPrint.Anns, PerItemConfig, Config, ExactPrint.Anns]+ '[Text.Builder.Builder, [BrittanyError], Seq String]+ '[]++type PPMLocal = MultiRWSS.MultiRWS+ '[Config, ExactPrint.Anns]+ '[Text.Builder.Builder, [BrittanyError], Seq String]+ '[]++newtype TopLevelDeclNameMap = TopLevelDeclNameMap (Map ExactPrint.AnnKey String)++data LayoutState = LayoutState+ { _lstate_baseYs :: [Int]+ -- ^ stack of number of current indentation columns+ -- (not number of indentations).+ , _lstate_curYOrAddNewline :: Either Int Int+ -- ^ Either:+ -- 1) number of chars in the current line.+ -- 2) number of newlines to be inserted before inserting any+ -- non-space elements.+ , _lstate_indLevels :: [Int]+ -- ^ stack of current indentation levels. set for+ -- any layout-affected elements such as+ -- let/do/case/where elements.+ -- The main purpose of this member is to+ -- properly align comments, as their+ -- annotation positions are relative to the+ -- current layout indentation level.+ , _lstate_indLevelLinger :: Int -- like a "last" of indLevel. Used for+ -- properly treating cases where comments+ -- on the first indented element have an+ -- annotation offset relative to the last+ -- non-indented element, which is confusing.+ , _lstate_comments :: Anns+ , _lstate_commentCol :: Maybe Int -- this communicates two things:+ -- firstly, that cursor is currently+ -- at the end of a comment (so needs+ -- newline before any actual content).+ -- secondly, the column at which+ -- insertion of comments started.+ , _lstate_addSepSpace :: Maybe Int -- number of spaces to insert if anyone+ -- writes (any non-spaces) in the+ -- current line.+ -- , _lstate_isNewline :: NewLineState+ -- -- captures if the layouter currently is in a new line, i.e. if the+ -- -- current line only contains (indentation) spaces.+ -- this is mostly superseeded by curYOrAddNewline, iirc.+ , _lstate_commentNewlines :: Int -- number of newlines inserted due to+ -- move-to-DP at a start of a comment.+ -- Necessary because some keyword DPs+ -- are relative to the last non-comment+ -- entity (for some reason).+ -- This is not very strictly reset to 0,+ -- so we might in some cases get "artifacts"+ -- from previous document elements.+ -- But the worst effect at the moment would+ -- be that we introduce less newlines on+ -- moveToKWDP, which seems harmless enough.+ }++lstate_baseY :: LayoutState -> Int+lstate_baseY = Safe.headNote "lstate_baseY" . _lstate_baseYs++lstate_indLevel :: LayoutState -> Int+lstate_indLevel = Safe.headNote "lstate_baseY" . _lstate_indLevels++-- evil, incomplete Show instance; only for debugging.+instance Show LayoutState where+ show state =+ "LayoutState"+ ++ "{baseYs=" ++ show (_lstate_baseYs state)+ ++ ",curYOrAddNewline=" ++ show (_lstate_curYOrAddNewline state)+ ++ ",indLevels=" ++ show (_lstate_indLevels state)+ ++ ",indLevelLinger=" ++ show (_lstate_indLevelLinger state)+ ++ ",commentCol=" ++ show (_lstate_commentCol state)+ ++ ",addSepSpace=" ++ show (_lstate_addSepSpace state)+ ++ ",commentNewlines=" ++ show (_lstate_commentNewlines state)+ ++ "}"++-- data NewLineState = NewLineStateInit -- initial state. we do not know if in a+-- -- newline, really. by special-casing+-- -- this we can appropriately handle it+-- -- differently at use-site.+-- | NewLineStateYes+-- | NewLineStateNo+-- deriving Eq++-- data LayoutSettings = LayoutSettings+-- { _lsettings_cols :: Int -- the thing that has default 80.+-- , _lsettings_indentPolicy :: IndentPolicy+-- , _lsettings_indentAmount :: Int+-- , _lsettings_indentWhereSpecial :: Bool -- indent where only 1 sometimes (TODO).+-- , _lsettings_indentListSpecial :: Bool -- use some special indentation for ","+-- -- when creating zero-indentation+-- -- multi-line list literals.+-- , _lsettings_importColumn :: Int+-- , _lsettings_initialAnns :: ExactPrint.Anns+-- }++data BrittanyError+ = ErrorInput String+ -- ^ parsing failed+ | ErrorUnusedComment String+ -- ^ internal error: some comment went missing+ | ErrorMacroConfig String String+ -- ^ in-source config string parsing error; first argument is the parser+ -- output and second the corresponding, ill-formed input.+ | LayoutWarning String+ -- ^ some warning+ | forall ast . Data.Data.Data ast => ErrorUnknownNode String (GenLocated SrcSpan ast)+ -- ^ internal error: pretty-printing is not implemented for type of node+ -- in the syntax-tree+ | ErrorOutputCheck+ -- ^ checking the output for syntactic validity failed++data BriSpacing = BriSpacing+ { _bs_spacePastLineIndent :: Int -- space in the current,+ -- potentially somewhat filled+ -- line.+ , _bs_spacePastIndent :: Int -- space required in properly+ -- indented blocks below the+ -- current line.+ }++data ColSig+ = ColTyOpPrefix+ -- any prefixed operator/paren/"::"/..+ -- expected to have exactly two colums.+ -- e.g. ":: foo"+ -- 111222+ -- "-> bar asd asd"+ -- 11122222222222+ | ColPatternsFuncPrefix+ -- pattern-part of the lhs, e.g. "func (foo a b) c _".+ -- Has variable number of columns depending on the number of patterns.+ | ColPatternsFuncInfix+ -- pattern-part of the lhs, e.g. "Foo a <> Foo b".+ -- Has variable number of columns depending on the number of patterns.+ | ColPatterns+ | ColCasePattern+ | ColBindingLine (Maybe Text)+ -- e.g. "func pat pat = expr"+ -- 1111111111111222222+ -- or "pat | stmt -> expr"+ -- 111111111112222222+ -- expected to have exactly two columns.+ | ColGuard+ -- e.g. "func pat pat | cond = ..."+ -- 11111111111112222222+ -- or "pat | cond1, cond2 -> ..."+ -- 1111222222222222222+ -- expected to have exactly two columns+ | ColGuardedBody+ -- e.g. | foofoo = 1+ -- | bar = 2+ -- 111111111222+ -- expected to have exactly two columns+ | ColBindStmt+ | ColDoLet -- the non-indented variant+ | ColRec+ | ColRecUpdate -- used for both RecCon and RecUpd. TODO: refactor to reflect?+ | ColRecDecl+ | ColListComp+ | ColList+ | ColApp Text+ | ColTuple+ | ColTuples+ | ColOpPrefix -- merge with ColList ? other stuff?+ | ColImport++ -- TODO+ deriving (Eq, Ord, Data.Data.Data, Show)++data BrIndent = BrIndentNone+ | BrIndentRegular+ | BrIndentSpecial Int+ deriving (Eq, Ord, Data.Data.Data, Show)++type ToBriDocM = MultiRWSS.MultiRWS+ '[Config, Anns] -- reader+ '[[BrittanyError], Seq String] -- writer+ '[NodeAllocIndex] -- state++type ToBriDoc (sym :: Kind.Type -> Kind.Type) = Located (sym GhcPs) -> ToBriDocM BriDocNumbered+type ToBriDoc' sym = Located sym -> ToBriDocM BriDocNumbered+type ToBriDocC sym c = Located sym -> ToBriDocM c++data DocMultiLine+ = MultiLineNo+ | MultiLinePossible+ deriving (Eq)++-- isomorphic to BriDocF Identity. Provided for ease of use, as we do a lot+-- of transformations on `BriDocF Identity`s and it is really annoying to+-- `Identity`/`runIdentity` everywhere.+data BriDoc+ = -- BDWrapAnnKey AnnKey BriDoc+ BDEmpty+ | BDLit !Text+ | BDSeq [BriDoc] -- elements other than the last should+ -- not contains BDPars.+ | BDCols ColSig [BriDoc] -- elements other than the last+ -- should not contains BDPars+ | BDSeparator -- semantically, space-unless-at-end-of-line.+ | BDAddBaseY BrIndent BriDoc+ | BDBaseYPushCur BriDoc+ | BDBaseYPop BriDoc+ | BDIndentLevelPushCur BriDoc+ | BDIndentLevelPop BriDoc+ | BDPar+ { _bdpar_indent :: BrIndent+ , _bdpar_restOfLine :: BriDoc -- should not contain other BDPars+ , _bdpar_indented :: BriDoc+ }+ -- | BDAddIndent BrIndent (BriDocF f)+ -- | BDNewline+ | BDAlt [BriDoc]+ | BDForwardLineMode BriDoc+ | BDExternal AnnKey+ (Set AnnKey) -- set of annkeys contained within the node+ -- to be printed via exactprint+ Bool -- should print extra comment ?+ Text+ | BDPlain !Text -- used for QuasiQuotes, content can be multi-line+ -- (contrast to BDLit)+ | BDAnnotationPrior AnnKey BriDoc+ | BDAnnotationKW AnnKey (Maybe AnnKeywordId) BriDoc+ | BDAnnotationRest AnnKey BriDoc+ | BDMoveToKWDP AnnKey AnnKeywordId Bool BriDoc -- True if should respect x offset+ | BDLines [BriDoc]+ | BDEnsureIndent BrIndent BriDoc+ -- the following constructors are only relevant for the alt transformation+ -- and are removed afterwards. They should never occur in any BriDoc+ -- after the alt transformation.+ | BDForceMultiline BriDoc+ | BDForceSingleline BriDoc+ | BDNonBottomSpacing Bool BriDoc+ | BDSetParSpacing BriDoc+ | BDForceParSpacing BriDoc+ -- pseudo-deprecated+ | BDDebug String BriDoc+ deriving (Data.Data.Data, Eq, Ord)++data BriDocF f+ = -- BDWrapAnnKey AnnKey BriDoc+ BDFEmpty+ | BDFLit !Text+ | BDFSeq [f (BriDocF f)] -- elements other than the last should+ -- not contains BDPars.+ | BDFCols ColSig [f (BriDocF f)] -- elements other than the last+ -- should not contains BDPars+ | BDFSeparator -- semantically, space-unless-at-end-of-line.+ | BDFAddBaseY BrIndent (f (BriDocF f))+ | BDFBaseYPushCur (f (BriDocF f))+ | BDFBaseYPop (f (BriDocF f))+ | BDFIndentLevelPushCur (f (BriDocF f))+ | BDFIndentLevelPop (f (BriDocF f))+ | BDFPar+ { _bdfpar_indent :: BrIndent+ , _bdfpar_restOfLine :: f (BriDocF f) -- should not contain other BDPars+ , _bdfpar_indented :: f (BriDocF f)+ }+ -- | BDAddIndent BrIndent (BriDocF f)+ -- | BDNewline+ | BDFAlt [f (BriDocF f)]+ | BDFForwardLineMode (f (BriDocF f))+ | BDFExternal AnnKey+ (Set AnnKey) -- set of annkeys contained within the node+ -- to be printed via exactprint+ Bool -- should print extra comment ?+ Text+ | BDFPlain !Text -- used for QuasiQuotes, content can be multi-line+ -- (contrast to BDLit)+ | BDFAnnotationPrior AnnKey (f (BriDocF f))+ | BDFAnnotationKW AnnKey (Maybe AnnKeywordId) (f (BriDocF f))+ | BDFAnnotationRest AnnKey (f (BriDocF f))+ | BDFMoveToKWDP AnnKey AnnKeywordId Bool (f (BriDocF f)) -- True if should respect x offset+ | BDFLines [(f (BriDocF f))]+ | BDFEnsureIndent BrIndent (f (BriDocF f))+ | BDFForceMultiline (f (BriDocF f))+ | BDFForceSingleline (f (BriDocF f))+ | BDFNonBottomSpacing Bool (f (BriDocF f))+ | BDFSetParSpacing (f (BriDocF f))+ | BDFForceParSpacing (f (BriDocF f))+ | BDFDebug String (f (BriDocF f))++-- deriving instance Data.Data.Data (BriDocF Identity)+deriving instance Data.Data.Data (BriDocF ((,) Int))++type BriDocFInt = BriDocF ((,) Int)+type BriDocNumbered = (Int, BriDocFInt)++instance Uniplate.Uniplate BriDoc where+ uniplate x@BDEmpty{} = plate x+ uniplate x@BDLit{} = plate x+ uniplate (BDSeq list ) = plate BDSeq ||* list+ uniplate (BDCols sig list) = plate BDCols |- sig ||* list+ uniplate x@BDSeparator = plate x+ uniplate (BDAddBaseY ind bd ) = plate BDAddBaseY |- ind |* bd+ uniplate (BDBaseYPushCur bd) = plate BDBaseYPushCur |* bd+ uniplate (BDBaseYPop bd) = plate BDBaseYPop |* bd+ uniplate (BDIndentLevelPushCur bd) = plate BDIndentLevelPushCur |* bd+ uniplate (BDIndentLevelPop bd) = plate BDIndentLevelPop |* bd+ uniplate (BDPar ind line indented) = plate BDPar |- ind |* line |* indented+ uniplate (BDAlt alts ) = plate BDAlt ||* alts+ uniplate (BDForwardLineMode bd ) = plate BDForwardLineMode |* bd+ uniplate x@BDExternal{} = plate x+ uniplate x@BDPlain{} = plate x+ uniplate (BDAnnotationPrior annKey bd) =+ plate BDAnnotationPrior |- annKey |* bd+ uniplate (BDAnnotationKW annKey kw bd) =+ plate BDAnnotationKW |- annKey |- kw |* bd+ uniplate (BDAnnotationRest annKey bd) =+ plate BDAnnotationRest |- annKey |* bd+ uniplate (BDMoveToKWDP annKey kw b bd) =+ plate BDMoveToKWDP |- annKey |- kw |- b |* bd+ uniplate (BDLines lines ) = plate BDLines ||* lines+ uniplate (BDEnsureIndent ind bd ) = plate BDEnsureIndent |- ind |* bd+ uniplate (BDForceMultiline bd ) = plate BDForceMultiline |* bd+ uniplate (BDForceSingleline bd ) = plate BDForceSingleline |* bd+ uniplate (BDNonBottomSpacing b bd) = plate BDNonBottomSpacing |- b |* bd+ uniplate (BDSetParSpacing bd ) = plate BDSetParSpacing |* bd+ uniplate (BDForceParSpacing bd ) = plate BDForceParSpacing |* bd+ uniplate (BDDebug s bd ) = plate BDDebug |- s |* bd++newtype NodeAllocIndex = NodeAllocIndex Int++-- TODO: rename to "dropLabels" ?+unwrapBriDocNumbered :: BriDocNumbered -> BriDoc+unwrapBriDocNumbered tpl = case snd tpl of+ BDFEmpty -> BDEmpty+ BDFLit t -> BDLit t+ BDFSeq list -> BDSeq $ rec <$> list+ BDFCols sig list -> BDCols sig $ rec <$> list+ BDFSeparator -> BDSeparator+ BDFAddBaseY ind bd -> BDAddBaseY ind $ rec bd+ BDFBaseYPushCur bd -> BDBaseYPushCur $ rec bd+ BDFBaseYPop bd -> BDBaseYPop $ rec bd+ BDFIndentLevelPushCur bd -> BDIndentLevelPushCur $ rec bd+ BDFIndentLevelPop bd -> BDIndentLevelPop $ rec bd+ BDFPar ind line indented -> BDPar ind (rec line) (rec indented)+ BDFAlt alts -> BDAlt $ rec <$> alts -- not that this will happen+ BDFForwardLineMode bd -> BDForwardLineMode $ rec bd+ BDFExternal k ks c t -> BDExternal k ks c t+ BDFPlain t -> BDPlain t+ BDFAnnotationPrior annKey bd -> BDAnnotationPrior annKey $ rec bd+ BDFAnnotationKW annKey kw bd -> BDAnnotationKW annKey kw $ rec bd+ BDFAnnotationRest annKey bd -> BDAnnotationRest annKey $ rec bd+ BDFMoveToKWDP annKey kw b bd -> BDMoveToKWDP annKey kw b $ rec bd+ BDFLines lines -> BDLines $ rec <$> lines+ BDFEnsureIndent ind bd -> BDEnsureIndent ind $ rec bd+ BDFForceMultiline bd -> BDForceMultiline $ rec bd+ BDFForceSingleline bd -> BDForceSingleline $ rec bd+ BDFNonBottomSpacing b bd -> BDNonBottomSpacing b $ rec bd+ BDFSetParSpacing bd -> BDSetParSpacing $ rec bd+ BDFForceParSpacing bd -> BDForceParSpacing $ rec bd+ BDFDebug s bd -> BDDebug (s ++ "@" ++ show (fst tpl)) $ rec bd+ where rec = unwrapBriDocNumbered++isNotEmpty :: BriDoc -> Bool+isNotEmpty BDEmpty = False+isNotEmpty _ = True++-- this might not work. is not used anywhere either.+briDocSeqSpine :: BriDoc -> ()+briDocSeqSpine = \case+ BDEmpty -> ()+ BDLit _t -> ()+ BDSeq list -> foldl' ((briDocSeqSpine .) . seq) () list+ BDCols _sig list -> foldl' ((briDocSeqSpine .) . seq) () list+ BDSeparator -> ()+ BDAddBaseY _ind bd -> briDocSeqSpine bd+ BDBaseYPushCur bd -> briDocSeqSpine bd+ BDBaseYPop bd -> briDocSeqSpine bd+ BDIndentLevelPushCur bd -> briDocSeqSpine bd+ BDIndentLevelPop bd -> briDocSeqSpine bd+ BDPar _ind line indented -> briDocSeqSpine line `seq` briDocSeqSpine indented+ BDAlt alts -> foldl' (\() -> briDocSeqSpine) () alts+ BDForwardLineMode bd -> briDocSeqSpine bd+ BDExternal{} -> ()+ BDPlain{} -> ()+ BDAnnotationPrior _annKey bd -> briDocSeqSpine bd+ BDAnnotationKW _annKey _kw bd -> briDocSeqSpine bd+ BDAnnotationRest _annKey bd -> briDocSeqSpine bd+ BDMoveToKWDP _annKey _kw _b bd -> briDocSeqSpine bd+ BDLines lines -> foldl' (\() -> briDocSeqSpine) () lines+ BDEnsureIndent _ind bd -> briDocSeqSpine bd+ BDForceMultiline bd -> briDocSeqSpine bd+ BDForceSingleline bd -> briDocSeqSpine bd+ BDNonBottomSpacing _ bd -> briDocSeqSpine bd+ BDSetParSpacing bd -> briDocSeqSpine bd+ BDForceParSpacing bd -> briDocSeqSpine bd+ BDDebug _s bd -> briDocSeqSpine bd++briDocForceSpine :: BriDoc -> BriDoc+briDocForceSpine bd = briDocSeqSpine bd `seq` bd+++data VerticalSpacingPar+ = VerticalSpacingParNone -- no indented lines+ | VerticalSpacingParSome Int -- indented lines, requiring this much+ -- vertical space at most+ | VerticalSpacingParAlways Int -- indented lines, requiring this much+ -- vertical space at most, but should+ -- be considered as having space for+ -- any spacing validity check.+ -- TODO: it might be wrong not to extend "always" to the none case, i.e.+ -- we might get better properties of spacing operators by having a+ -- product like (Normal|Always, None|Some Int).+ deriving (Eq, Show)++data VerticalSpacing+ = VerticalSpacing+ { _vs_sameLine :: !Int+ , _vs_paragraph :: !VerticalSpacingPar+ , _vs_parFlag :: !Bool+ }+ deriving (Eq, Show)++newtype LineModeValidity a = LineModeValidity (Strict.Maybe a)+ deriving (Functor, Applicative, Monad, Show, Alternative)++pattern LineModeValid :: forall t. t -> LineModeValidity t+pattern LineModeValid x = LineModeValidity (Strict.Just x) :: LineModeValidity t+pattern LineModeInvalid :: forall t. LineModeValidity t+pattern LineModeInvalid = LineModeValidity Strict.Nothing :: LineModeValidity t
+ source/library/Brittany/Internal/Utils.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Brittany.Internal.Utils where++import qualified Data.ByteString as B+import qualified Data.Coerce+import Data.Data+import Data.Generics.Aliases+import qualified Data.Generics.Uniplate.Direct as Uniplate+import qualified Data.Semigroup as Semigroup+import qualified Data.Sequence as Seq+import DataTreePrint+import qualified GHC.Data.FastString as GHC+import qualified GHC.Driver.Session as GHC+import qualified GHC.Hs.Extension as HsExtension+import qualified GHC.OldList as List+import GHC.Types.Name.Occurrence as OccName (occNameString)+import qualified GHC.Types.SrcLoc as GHC+import qualified GHC.Utils.Outputable as GHC+import Brittany.Internal.Config.Types+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types+import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils+import qualified Text.PrettyPrint as PP++++parDoc :: String -> PP.Doc+parDoc = PP.fsep . fmap PP.text . List.words++parDocW :: [String] -> PP.Doc+parDocW = PP.fsep . fmap PP.text . List.words . List.unwords+++showSDoc_ :: GHC.SDoc -> String+showSDoc_ = GHC.showSDoc GHC.unsafeGlobalDynFlags++showOutputable :: (GHC.Outputable a) => a -> String+showOutputable = GHC.showPpr GHC.unsafeGlobalDynFlags++fromMaybeIdentity :: Identity a -> Maybe a -> Identity a+fromMaybeIdentity x y = Data.Coerce.coerce $ fromMaybe (Data.Coerce.coerce x) y++fromOptionIdentity :: Identity a -> Maybe a -> Identity a+fromOptionIdentity x y =+ Data.Coerce.coerce $ fromMaybe (Data.Coerce.coerce x) y++-- maximum monoid over N+0+-- or more than N, because Num is allowed.+newtype Max a = Max { getMax :: a }+ deriving (Eq, Ord, Show, Bounded, Num)++instance (Num a, Ord a) => Semigroup (Max a) where+ (<>) = Data.Coerce.coerce (max :: a -> a -> a)++instance (Num a, Ord a) => Monoid (Max a) where+ mempty = Max 0+ mappend = (<>)++newtype ShowIsId = ShowIsId String deriving Data++instance Show ShowIsId where+ show (ShowIsId x) = x++data A x = A ShowIsId x+ deriving Data++customLayouterF :: ExactPrint.Types.Anns -> LayouterF+customLayouterF anns layoutF =+ DataToLayouter+ $ f+ `extQ` showIsId+ `extQ` fastString+ `extQ` bytestring+ `extQ` occName+ `extQ` srcSpan+ `ext2Q` located+ where+ DataToLayouter f = defaultLayouterF layoutF+ simpleLayouter :: String -> NodeLayouter+ simpleLayouter s = NodeLayouter (length s) False (const $ PP.text s)+ showIsId :: ShowIsId -> NodeLayouter+ showIsId (ShowIsId s) = NodeLayouter (length s + 2) True $ \case+ Left True -> PP.parens $ PP.text s+ Left False -> PP.text s+ Right _ -> PP.text s+ fastString =+ simpleLayouter . ("{FastString: " ++) . (++ "}") . show :: GHC.FastString+ -> NodeLayouter+ bytestring = simpleLayouter . show :: B.ByteString -> NodeLayouter+ occName =+ simpleLayouter . ("{OccName: " ++) . (++ "}") . OccName.occNameString+ srcSpan :: GHC.SrcSpan -> NodeLayouter+ srcSpan ss =+ simpleLayouter+ -- - $ "{"++ showSDoc_ (GHC.ppr ss)++"}"+ $ "{"+ ++ showOutputable ss+ ++ "}"+ located :: (Data b, Data loc) => GHC.GenLocated loc b -> NodeLayouter+ located (GHC.L ss a) = runDataToLayouter layoutF $ A annStr a+ where+ annStr = case cast ss of+ Just (s :: GHC.SrcSpan) ->+ ShowIsId $ show (ExactPrint.Utils.getAnnotationEP (GHC.L s a) anns)+ Nothing -> ShowIsId "nnnnnnnn"++customLayouterNoAnnsF :: LayouterF+customLayouterNoAnnsF layoutF =+ DataToLayouter+ $ f+ `extQ` showIsId+ `extQ` fastString+ `extQ` bytestring+ `extQ` occName+ `extQ` srcSpan+ `ext2Q` located+ where+ DataToLayouter f = defaultLayouterF layoutF+ simpleLayouter :: String -> NodeLayouter+ simpleLayouter s = NodeLayouter (length s) False (const $ PP.text s)+ showIsId :: ShowIsId -> NodeLayouter+ showIsId (ShowIsId s) = NodeLayouter (length s + 2) True $ \case+ Left True -> PP.parens $ PP.text s+ Left False -> PP.text s+ Right _ -> PP.text s+ fastString =+ simpleLayouter . ("{FastString: " ++) . (++ "}") . show :: GHC.FastString+ -> NodeLayouter+ bytestring = simpleLayouter . show :: B.ByteString -> NodeLayouter+ occName =+ simpleLayouter . ("{OccName: " ++) . (++ "}") . OccName.occNameString+ srcSpan :: GHC.SrcSpan -> NodeLayouter+ srcSpan ss = simpleLayouter $ "{" ++ showSDoc_ (GHC.ppr ss) ++ "}"+ located :: (Data b) => GHC.GenLocated loc b -> NodeLayouter+ located (GHC.L _ss a) = runDataToLayouter layoutF a++-- displayBriDocTree :: BriDoc -> PP.Doc+-- displayBriDocTree = \case+-- BDWrapAnnKey annKey doc -> def "BDWrapAnnKey"+-- $ PP.text (show annKey)+-- $+$ displayBriDocTree doc+-- BDEmpty -> PP.text "BDEmpty"+-- BDLit t -> def "BDLit" $ PP.text (show t)+-- BDSeq list -> def "BDSeq" $ displayList list+-- BDCols sig list -> def "BDCols" $ PP.text (show sig)+-- $+$ displayList list+-- BDSeparator -> PP.text "BDSeparator"+-- BDPar rol indent lines -> def "BDPar" $ displayBriDocTree rol+-- $+$ PP.text (show indent)+-- $+$ displayList lines+-- BDAlt alts -> def "BDAlt" $ displayList alts+-- BDExternal ast _t -> def "BDExternal" (astToDoc ast)+-- BDSpecialPostCommentLoc _ -> PP.text "BDSpecialPostCommentLoc"+-- where+-- def x r = PP.text x $+$ PP.nest 2 r+-- displayList :: [BriDoc] -> PP.Doc+-- displayList [] = PP.text "[]"+-- displayList (x:xr) = PP.cat $ PP.text "[" <+> displayBriDocTree x+-- : [PP.text "," <+> displayBriDocTree t | t<-xr]+-- ++ [PP.text "]"]++-- displayBriDocSimpleTree :: BriDocSimple -> PP.Doc+-- displayBriDocSimpleTree = \case+-- BDSWrapAnnKey annKey doc -> def "BDSWrapAnnKey"+-- $ PP.text (show annKey)+-- $+$ displayBriDocSimpleTree doc+-- BDSLit t -> def "BDSLit" $ PP.text (show t)+-- BDSSeq list -> def "BDSSeq" $ displayList list+-- BDSCols sig list -> def "BDSCols" $ PP.text (show sig)+-- $+$ displayList list+-- BDSSeparator -> PP.text "BDSSeparator"+-- BDSPar rol indent lines -> def "BDSPar" $ displayBriDocSimpleTree rol+-- $+$ PP.text (show indent)+-- $+$ displayList lines+-- BDSExternal annKey _subKeys _t -> def "BDSExternal" (PP.text $ show annKey)+-- BDSSpecialPostCommentLoc _ -> PP.text "BDSSpecialPostCommentLoc"+-- where+-- def x r = PP.text x $+$ PP.nest 2 r+-- displayList :: [BriDocSimple] -> PP.Doc+-- displayList [] = PP.text "[]"+-- displayList (x:xr) = PP.cat $ PP.text "[" <+> displayBriDocSimpleTree x+-- : [PP.text "," <+> displayBriDocSimpleTree t | t<-xr]+-- ++ [PP.text "]"]++traceIfDumpConf+ :: (MonadMultiReader Config m, Show a)+ => String+ -> (DebugConfig -> Identity (Semigroup.Last Bool))+ -> a+ -> m ()+traceIfDumpConf s accessor val = do+ whenM (mAsk <&> _conf_debug .> accessor .> confUnpack) $ do+ trace ("---- " ++ s ++ " ----\n" ++ show val) $ return ()++tellDebugMess :: MonadMultiWriter (Seq String) m => String -> m ()+tellDebugMess s = mTell $ Seq.singleton s++tellDebugMessShow+ :: forall a m . (MonadMultiWriter (Seq String) m, Show a) => a -> m ()+tellDebugMessShow = tellDebugMess . show++-- i should really put that into multistate..+mModify :: MonadMultiState s m => (s -> s) -> m ()+mModify f = mGet >>= mSet . f++astToDoc :: Data ast => ast -> PP.Doc+astToDoc ast = printTreeWithCustom 160 customLayouterNoAnnsF ast++briDocToDoc :: BriDoc -> PP.Doc+briDocToDoc = astToDoc . removeAnnotations+ where+ removeAnnotations = Uniplate.transform $ \case+ BDAnnotationPrior _ x -> x+ BDAnnotationKW _ _ x -> x+ BDAnnotationRest _ x -> x+ x -> x++briDocToDocWithAnns :: BriDoc -> PP.Doc+briDocToDocWithAnns = astToDoc++annsDoc :: ExactPrint.Types.Anns -> PP.Doc+annsDoc =+ printTreeWithCustom 100 customLayouterNoAnnsF . fmap (ShowIsId . show)++breakEither :: (a -> Either b c) -> [a] -> ([b], [c])+breakEither _ [] = ([], [])+breakEither fn (a1 : aR) = case fn a1 of+ Left b -> (b : bs, cs)+ Right c -> (bs, c : cs)+ where (bs, cs) = breakEither fn aR++spanMaybe :: (a -> Maybe b) -> [a] -> ([b], [a])+spanMaybe f (x1 : xR) | Just y <- f x1 = (y : ys, xs)+ where (ys, xs) = spanMaybe f xR+spanMaybe _ xs = ([], xs)++data FirstLastView a+ = FirstLastEmpty+ | FirstLastSingleton a+ | FirstLast a [a] a++splitFirstLast :: [a] -> FirstLastView a+splitFirstLast [] = FirstLastEmpty+splitFirstLast [x] = FirstLastSingleton x+splitFirstLast (x1 : xr) = FirstLast x1 (List.init xr) (List.last xr)++-- TODO: move to uniplate upstream?+-- aka `transform`+transformUp :: Uniplate.Uniplate on => (on -> on) -> (on -> on)+transformUp f = g where g = f . Uniplate.descend g+_transformDown :: Uniplate.Uniplate on => (on -> on) -> (on -> on)+_transformDown f = g where g = Uniplate.descend g . f+transformDownMay :: Uniplate.Uniplate on => (on -> Maybe on) -> (on -> on)+transformDownMay f = g where g x = maybe x (Uniplate.descend g) $ f x+_transformDownRec :: Uniplate.Uniplate on => (on -> Maybe on) -> (on -> on)+_transformDownRec f = g where g x = maybe (Uniplate.descend g x) g $ f x++-- | similar to List.lines, but treating the case of final newline character+-- in such a manner that this function is the inverse of @intercalate "\n"@.+lines' :: String -> [String]+lines' s = case break (== '\n') s of+ (s1, []) -> [s1]+ (s1, [_]) -> [s1, ""]+ (s1, (_ : r)) -> s1 : lines' r++absurdExt :: HsExtension.NoExtCon -> a+absurdExt = HsExtension.noExtCon
+ source/library/Brittany/Main.hs view
@@ -0,0 +1,500 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Brittany.Main where++import Control.Monad (zipWithM)+import qualified Control.Monad.Trans.Except as ExceptT+import Data.CZipWith+import qualified Data.Either+import qualified Data.List.Extra+import qualified Data.Monoid+import qualified Data.Semigroup as Semigroup+import qualified Data.Text as Text+import qualified Data.Text.IO as Text.IO+import qualified Data.Text.Lazy as TextL+import DataTreePrint+import GHC (GenLocated(L))+import qualified GHC.Driver.Session as GHC+import qualified GHC.LanguageExtensions.Type as GHC+import qualified GHC.OldList as List+import GHC.Utils.Outputable (Outputable(..), showSDocUnsafe)+import Brittany.Internal+import Brittany.Internal.Config+import Brittany.Internal.Config.Types+import Brittany.Internal.Obfuscation+import Brittany.Internal.Prelude+import Brittany.Internal.PreludeUtils+import Brittany.Internal.Types+import Brittany.Internal.Utils+import qualified Language.Haskell.GHC.ExactPrint as ExactPrint+import Paths_brittany+import qualified System.Directory as Directory+import qualified System.Environment as Environment+import qualified System.Exit+import qualified System.FilePath.Posix as FilePath+import qualified System.IO+import qualified Text.ParserCombinators.ReadP as ReadP+import qualified Text.ParserCombinators.ReadPrec as ReadPrec+import qualified Text.PrettyPrint as PP+import Text.Read (Read(..))+import UI.Butcher.Monadic++++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 = do+ progName <- Environment.getProgName+ args <- Environment.getArgs+ mainWith progName args++mainWith :: String -> [String] -> IO ()+mainWith progName args =+ Environment.withProgName progName+ . Environment.withArgs args+ $ mainFromCmdParserWithHelpDesc mainCmdParser++helpDoc :: PP.Doc+helpDoc = PP.vcat $ List.intersperse+ (PP.text "")+ [ parDocW+ [ "Reformats one or more haskell modules."+ , "Currently affects only the module head (imports/exports), type"+ , "signatures and function bindings;"+ , "everything else is left unmodified."+ , "Based on ghc-exactprint, thus (theoretically) supporting all"+ , "that ghc does."+ ]+ , parDoc $ "Example invocations:"+ , PP.hang (PP.text "") 2 $ PP.vcat+ [ PP.text "brittany"+ , PP.nest 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 output is syntactically valid and that no comments are removed."+ , "Nonetheless, this is a young project, and there will always be bugs,"+ , "and ensuring that the transformation never changes semantics of the"+ , "transformed source is currently not possible."+ , "Please do check the output and do not let brittany override your large"+ , "codebase without having backups."+ ]+ , parDoc $ "There is NO WARRANTY, to the extent permitted by law."+ , parDocW+ [ "This program is free software released under the AGPLv3."+ , "For details use the --license flag."+ ]+ , parDoc $ "See https://github.com/lspitzner/brittany"+ , parDoc+ $ "Please report bugs at"+ ++ " https://github.com/lspitzner/brittany/issues"+ ]++licenseDoc :: PP.Doc+licenseDoc = PP.vcat $ List.intersperse+ (PP.text "")+ [ parDoc $ "Copyright (C) 2016-2019 Lennart Spitzner"+ , parDoc $ "Copyright (C) 2019 PRODA LTD"+ , parDocW+ [ "This program is free software: you can redistribute it and/or modify"+ , "it under the terms of the GNU Affero General Public License,"+ , "version 3, as published by the Free Software Foundation."+ ]+ , parDocW+ [ "This program is distributed in the hope that it will be useful,"+ , "but WITHOUT ANY WARRANTY; without even the implied warranty of"+ , "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"+ , "GNU Affero General Public License for more details."+ ]+ , parDocW+ [ "You should have received a copy of the GNU Affero General Public"+ , "License along with this program. If not, see"+ , "<http://www.gnu.org/licenses/>."+ ]+ ]+++mainCmdParser :: CommandDesc () -> CmdParser Identity (IO ()) ()+mainCmdParser helpDesc = do+ addCmdSynopsis "haskell source pretty printer"+ addCmdHelp $ helpDoc+ -- addCmd "debugArgs" $ do+ addHelpCommand helpDesc+ addCmd "license" $ addCmdImpl $ print $ licenseDoc+ -- addButcherDebugCommand+ reorderStart+ printHelp <- addSimpleBoolFlag "h" ["help"] mempty+ printVersion <- addSimpleBoolFlag "" ["version"] mempty+ printLicense <- addSimpleBoolFlag "" ["license"] mempty+ noUserConfig <- addSimpleBoolFlag "" ["no-user-config"] mempty+ configPaths <- addFlagStringParams+ ""+ ["config-file"]+ "PATH"+ (flagHelpStr "path to config file") -- TODO: allow default on addFlagStringParam ?+ cmdlineConfig <- cmdlineConfigParser+ suppressOutput <- addSimpleBoolFlag+ ""+ ["suppress-output"]+ (flagHelp $ parDoc+ "suppress the regular output, i.e. the transformed haskell source"+ )+ _verbosity <- addSimpleCountFlag+ "v"+ ["verbose"]+ (flagHelp $ parDoc "[currently without effect; TODO]")+ checkMode <- addSimpleBoolFlag+ "c"+ ["check-mode"]+ (flagHelp+ (PP.vcat+ [ PP.text "check for changes but do not write them out"+ , PP.text "exits with code 0 if no changes necessary, 1 otherwise"+ , PP.text "and print file path(s) of files that have changes to stdout"+ ]+ )+ )+ writeMode <- addFlagReadParam+ ""+ ["write-mode"]+ "(display|inplace)"+ (flagHelp+ (PP.vcat+ [ PP.text "display: output for any input(s) goes to stdout"+ , PP.text "inplace: override respective input file (without backup!)"+ ]+ )+ Data.Monoid.<> flagDefault Display+ )+ inputParams <- addParamNoFlagStrings+ "PATH"+ (paramHelpStr "paths to input/inout haskell source files")+ reorderStop+ addCmdImpl $ void $ do+ when printLicense $ do+ print licenseDoc+ System.Exit.exitSuccess+ when printVersion $ do+ do+ putStrLn $ "brittany version " ++ showVersion version+ putStrLn $ "Copyright (C) 2016-2019 Lennart Spitzner"+ putStrLn $ "Copyright (C) 2019 PRODA LTD"+ putStrLn $ "There is NO WARRANTY, to the extent permitted by law."+ System.Exit.exitSuccess+ when printHelp $ do+ liftIO+ $ putStrLn+ $ PP.renderStyle PP.style { PP.ribbonsPerLine = 1.0 }+ $ ppHelpShallow helpDesc+ System.Exit.exitSuccess++ let+ inputPaths = if null inputParams then [Nothing] else map Just inputParams+ let+ outputPaths = case writeMode of+ Display -> repeat Nothing+ Inplace -> inputPaths++ configsToLoad <- liftIO $ if null configPaths+ then+ maybeToList <$> (Directory.getCurrentDirectory >>= findLocalConfigPath)+ else pure configPaths++ config <-+ runMaybeT+ (if noUserConfig+ then readConfigs cmdlineConfig configsToLoad+ else readConfigsWithUserConfig cmdlineConfig configsToLoad+ )+ >>= \case+ Nothing -> System.Exit.exitWith (System.Exit.ExitFailure 53)+ Just x -> return x+ when (config & _conf_debug & _dconf_dump_config & confUnpack)+ $ trace (showConfigYaml config)+ $ return ()++ results <- zipWithM+ (coreIO putStrErrLn config suppressOutput checkMode)+ inputPaths+ outputPaths++ if checkMode+ then when (Changes `elem` (Data.Either.rights results))+ $ System.Exit.exitWith (System.Exit.ExitFailure 1)+ else case results of+ xs | all Data.Either.isRight xs -> pure ()+ [Left x] -> System.Exit.exitWith (System.Exit.ExitFailure x)+ _ -> System.Exit.exitWith (System.Exit.ExitFailure 1)+++data ChangeStatus = Changes | NoChanges+ deriving (Eq)++-- | The main IO parts for the default mode of operation, and after commandline+-- and config stuff is processed.+coreIO+ :: (String -> IO ()) -- ^ error output function. In parallel operation, you+ -- may want serialize the different outputs and+ -- consequently not directly print to stderr.+ -> Config -- ^ global program config.+ -> Bool -- ^ whether to supress output (to stdout). Purely IO flag, so+ -- currently not part of program config.+ -> Bool -- ^ whether we are (just) in check mode.+ -> Maybe FilePath.FilePath -- ^ input filepath; stdin if Nothing.+ -> Maybe FilePath.FilePath -- ^ output filepath; stdout if Nothing.+ -> IO (Either Int ChangeStatus) -- ^ Either an errorNo, or the change status.+coreIO putErrorLnIO config suppressOutput checkMode 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+ -- amount of slight differences: This module is a bit more verbose, and+ -- it tries to use the full-blown `parseModule` function which supports+ -- CPP (but requires the input to be a file..).+ let cppMode = config & _conf_preprocessor & _ppconf_CPPMode & confUnpack+ -- the flag will do the following: insert a marker string+ -- ("-- BRITANY_INCLUDE_HACK ") right before any lines starting with+ -- "#include" before processing (parsing) input; and remove that marker+ -- string from the transformation output.+ -- The flag is intentionally misspelled to prevent clashing with+ -- inline-config stuff.+ let+ hackAroundIncludes =+ config & _conf_preprocessor & _ppconf_hackAroundIncludes & confUnpack+ let+ exactprintOnly = viaGlobal || viaDebug+ where+ viaGlobal = config & _conf_roundtrip_exactprint_only & confUnpack+ viaDebug =+ config & _conf_debug & _dconf_roundtrip_exactprint_only & confUnpack++ let+ cppCheckFunc dynFlags = if GHC.xopt GHC.Cpp dynFlags+ then case cppMode of+ CPPModeAbort -> do+ return $ Left "Encountered -XCPP. Aborting."+ CPPModeWarn -> do+ putErrorLnIO+ $ "Warning: Encountered -XCPP."+ ++ " Be warned that -XCPP is not supported and that"+ ++ " brittany cannot check that its output is syntactically"+ ++ " valid in its presence."+ return $ Right True+ CPPModeNowarn -> return $ Right True+ else return $ Right False+ (parseResult, originalContents) <- case inputPathM of+ Nothing -> do+ -- TODO: refactor this hack to not be mixed into parsing logic+ let+ hackF s = if "#include" `isPrefixOf` s+ then "-- BRITANY_INCLUDE_HACK " ++ s+ else s+ let+ hackTransform = if hackAroundIncludes && not exactprintOnly+ then List.intercalate "\n" . fmap hackF . lines'+ else id+ inputString <- liftIO System.IO.getContents+ parseRes <- liftIO $ parseModuleFromString+ ghcOptions+ "stdin"+ cppCheckFunc+ (hackTransform inputString)+ return (parseRes, Text.pack inputString)+ Just p -> liftIO $ do+ parseRes <- parseModule ghcOptions p cppCheckFunc+ inputText <- Text.IO.readFile p+ -- The above means we read the file twice, but the+ -- GHC API does not really expose the source it+ -- read. Should be in cache still anyways.+ --+ -- We do not use TextL.IO.readFile because lazy IO is evil.+ -- (not identical -> read is not finished ->+ -- handle still open -> write below crashes - evil.)+ return (parseRes, inputText)+ case parseResult of+ Left left -> do+ putErrorLn "parse error:"+ putErrorLn left+ ExceptT.throwE 60+ Right (anns, parsedSource, hasCPP) -> do+ (inlineConf, perItemConf) <-+ case+ extractCommentConfigs anns (getTopLevelDeclNameMap parsedSource)+ of+ Left (err, input) -> do+ putErrorLn $ "Error: parse error in inline configuration:"+ putErrorLn err+ putErrorLn $ " in the string \"" ++ input ++ "\"."+ ExceptT.throwE 61+ Right c -> -- trace (showTree c) $+ pure c+ let moduleConf = cZipWith fromOptionIdentity config inlineConf+ when (config & _conf_debug & _dconf_dump_ast_full & confUnpack) $ do+ let val = printTreeWithCustom 100 (customLayouterF anns) parsedSource+ trace ("---- ast ----\n" ++ show val) $ return ()+ let+ disableFormatting =+ moduleConf & _conf_disable_formatting & confUnpack+ (errsWarns, outSText, hasChanges) <- do+ if+ | disableFormatting -> do+ pure ([], originalContents, False)+ | exactprintOnly -> do+ let r = Text.pack $ ExactPrint.exactPrint parsedSource anns+ pure ([], r, r /= originalContents)+ | otherwise -> do+ let+ omitCheck =+ moduleConf+ & _conf_errorHandling+ .> _econf_omit_output_valid_check+ .> confUnpack+ (ews, outRaw) <- if hasCPP || omitCheck+ then return+ $ pPrintModule moduleConf perItemConf anns parsedSource+ else liftIO $ pPrintModuleAndCheck+ moduleConf+ perItemConf+ anns+ parsedSource+ let+ hackF s = fromMaybe s $ TextL.stripPrefix+ (TextL.pack "-- BRITANY_INCLUDE_HACK ")+ s+ let+ out = TextL.toStrict $ if hackAroundIncludes+ then+ TextL.intercalate (TextL.pack "\n")+ $ hackF+ <$> TextL.splitOn (TextL.pack "\n") outRaw+ else outRaw+ out' <- if moduleConf & _conf_obfuscate & confUnpack+ then lift $ obfuscate out+ else pure out+ pure $ (ews, out', out' /= originalContents)+ let+ customErrOrder ErrorInput{} = 4+ customErrOrder LayoutWarning{} = -1 :: Int+ customErrOrder ErrorOutputCheck{} = 1+ customErrOrder ErrorUnusedComment{} = 2+ customErrOrder ErrorUnknownNode{} = -2 :: Int+ customErrOrder ErrorMacroConfig{} = 5+ unless (null errsWarns) $ do+ let+ groupedErrsWarns =+ Data.List.Extra.groupOn customErrOrder+ $ List.sortOn customErrOrder+ $ errsWarns+ groupedErrsWarns `forM_` \case+ (ErrorOutputCheck{} : _) -> do+ putErrorLn+ $ "ERROR: brittany pretty printer"+ ++ " returned syntactically invalid result."+ (ErrorInput str : _) -> do+ putErrorLn $ "ERROR: parse error: " ++ str+ uns@(ErrorUnknownNode{} : _) -> do+ putErrorLn+ $ "WARNING: encountered unknown syntactical constructs:"+ uns `forM_` \case+ ErrorUnknownNode str ast@(L loc _) -> do+ putErrorLn $ " " <> str <> " at " <> showSDocUnsafe (ppr loc)+ when+ (config+ & _conf_debug+ & _dconf_dump_ast_unknown+ & confUnpack+ )+ $ do+ putErrorLn $ " " ++ show (astToDoc ast)+ _ -> error "cannot happen (TM)"+ putErrorLn+ " -> falling back on exactprint for this element of the module"+ warns@(LayoutWarning{} : _) -> do+ putErrorLn $ "WARNINGS:"+ warns `forM_` \case+ LayoutWarning str -> putErrorLn str+ _ -> error "cannot happen (TM)"+ unused@(ErrorUnusedComment{} : _) -> do+ putErrorLn+ $ "Error: detected unprocessed comments."+ ++ " The transformation output will most likely"+ ++ " not contain some of the comments"+ ++ " present in the input haskell source file."+ putErrorLn $ "Affected are the following comments:"+ unused `forM_` \case+ ErrorUnusedComment str -> putErrorLn str+ _ -> error "cannot happen (TM)"+ (ErrorMacroConfig err input : _) -> do+ putErrorLn $ "Error: parse error in inline configuration:"+ putErrorLn err+ putErrorLn $ " in the string \"" ++ input ++ "\"."+ [] -> error "cannot happen"+ -- TODO: don't output anything when there are errors unless user+ -- adds some override?+ let+ hasErrors =+ if config & _conf_errorHandling & _econf_Werror & confUnpack+ then not $ null errsWarns+ else 0 < maximum (-1 : fmap customErrOrder errsWarns)+ outputOnErrs =+ config+ & _conf_errorHandling+ & _econf_produceOutputOnErrors+ & confUnpack+ shouldOutput =+ not suppressOutput+ && not checkMode+ && (not hasErrors || outputOnErrs)++ when shouldOutput+ $ addTraceSep (_conf_debug config)+ $ case outputPathM of+ Nothing -> liftIO $ Text.IO.putStr $ outSText+ Just p -> liftIO $ do+ let+ isIdentical = case inputPathM of+ Nothing -> False+ Just _ -> not hasChanges+ unless isIdentical $ Text.IO.writeFile p $ outSText++ when (checkMode && hasChanges) $ case inputPathM of+ Nothing -> pure ()+ Just p -> liftIO $ putStrLn $ "formatting would modify: " ++ p++ when hasErrors $ ExceptT.throwE 70+ return (if hasChanges then Changes else NoChanges)+ where+ addTraceSep conf =+ if or+ [ confUnpack $ _dconf_dump_annotations conf+ , confUnpack $ _dconf_dump_ast_unknown conf+ , confUnpack $ _dconf_dump_ast_full conf+ , confUnpack $ _dconf_dump_bridoc_raw conf+ , confUnpack $ _dconf_dump_bridoc_simpl_alt conf+ , confUnpack $ _dconf_dump_bridoc_simpl_floating conf+ , confUnpack $ _dconf_dump_bridoc_simpl_columns conf+ , confUnpack $ _dconf_dump_bridoc_simpl_indent conf+ , confUnpack $ _dconf_dump_bridoc_final conf+ ]+ then trace "----"+ else id
− source/library/Language/Haskell/Brittany.hs
@@ -1,24 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany- ( parsePrintModule- , staticDefaultConfig- , forwardOptionsSyntaxExtsEnabled- , userConfigPath- , findLocalConfigPath- , readConfigs- , readConfigsWithUserConfig- , Config- , CConfig(..)- , CDebugConfig(..)- , CLayoutConfig(..)- , CErrorHandlingConfig(..)- , CForwardOptions(..)- , CPreProcessorConfig(..)- , BrittanyError(..)- ) where--import Language.Haskell.Brittany.Internal-import Language.Haskell.Brittany.Internal.Config-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Types
− source/library/Language/Haskell/Brittany/Internal.hs
@@ -1,660 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal- ( parsePrintModule- , parsePrintModuleTests- , pPrintModule- , pPrintModuleAndCheck- -- re-export from utils:- , parseModule- , parseModuleFromString- , extractCommentConfigs- , getTopLevelDeclNameMap- ) where--import Control.Monad.Trans.Except-import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS-import qualified Data.ByteString.Char8-import Data.CZipWith-import Data.Char (isSpace)-import Data.HList.HList-import qualified Data.Map as Map-import qualified Data.Maybe-import qualified Data.Semigroup as Semigroup-import qualified Data.Sequence as Seq-import qualified Data.Text as Text-import qualified Data.Text.Lazy as TextL-import qualified Data.Text.Lazy.Builder as Text.Builder-import qualified Data.Yaml-import qualified GHC hiding (parseModule)-import GHC (GenLocated(L))-import qualified GHC.Driver.Session as GHC-import GHC.Hs-import qualified GHC.LanguageExtensions.Type as GHC-import qualified GHC.OldList as List-import GHC.Parser.Annotation (AnnKeywordId(..))-import GHC.Types.SrcLoc (SrcSpan)-import Language.Haskell.Brittany.Internal.Backend-import Language.Haskell.Brittany.Internal.BackendUtils-import Language.Haskell.Brittany.Internal.Config-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.ExactPrintUtils-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.Decl-import Language.Haskell.Brittany.Internal.Layouters.Module-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Transformations.Alt-import Language.Haskell.Brittany.Internal.Transformations.Columns-import Language.Haskell.Brittany.Internal.Transformations.Floating-import Language.Haskell.Brittany.Internal.Transformations.Indent-import Language.Haskell.Brittany.Internal.Transformations.Par-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint-import qualified UI.Butcher.Monadic as Butcher----data InlineConfigTarget- = InlineConfigTargetModule- | InlineConfigTargetNextDecl -- really only next in module- | InlineConfigTargetNextBinding -- by name- | InlineConfigTargetBinding String--extractCommentConfigs- :: ExactPrint.Anns- -> TopLevelDeclNameMap- -> Either (String, String) (CConfig Maybe, PerItemConfig)-extractCommentConfigs anns (TopLevelDeclNameMap declNameMap) = do- let- commentLiness =- [ ( k- , [ x- | (ExactPrint.Comment x _ _, _) <-- ( ExactPrint.annPriorComments ann- ++ ExactPrint.annFollowingComments ann- )- ]- ++ [ x- | (ExactPrint.AnnComment (ExactPrint.Comment x _ _), _) <-- ExactPrint.annsDP ann- ]- )- | (k, ann) <- Map.toList anns- ]- let configLiness = commentLiness <&> second- (Data.Maybe.mapMaybe $ \line -> do- l1 <-- List.stripPrefix "-- BRITTANY" line- <|> List.stripPrefix "--BRITTANY" line- <|> List.stripPrefix "-- brittany" line- <|> List.stripPrefix "--brittany" line- <|> (List.stripPrefix "{- BRITTANY" line >>= stripSuffix "-}")- let l2 = dropWhile isSpace l1- guard- ( ("@" `isPrefixOf` l2)- || ("-disable" `isPrefixOf` l2)- || ("-next" `isPrefixOf` l2)- || ("{" `isPrefixOf` l2)- || ("--" `isPrefixOf` l2)- )- pure l2- )- let- configParser = Butcher.addAlternatives- [ ( "commandline-config"- , \s -> "-" `isPrefixOf` dropWhile (== ' ') s- , cmdlineConfigParser- )- , ( "yaml-config-document"- , \s -> "{" `isPrefixOf` dropWhile (== ' ') s- , Butcher.addCmdPart (Butcher.varPartDesc "yaml-config-document")- $ fmap (\lconf -> (mempty { _conf_layout = lconf }, ""))- . either (const Nothing) Just- . Data.Yaml.decodeEither'- . Data.ByteString.Char8.pack- -- TODO: use some proper utf8 encoder instead?- )- ]- parser = do -- we will (mis?)use butcher here to parse the inline config- -- line.- let nextDecl = do- conf <- configParser- Butcher.addCmdImpl (InlineConfigTargetNextDecl, conf)- Butcher.addCmd "-next-declaration" nextDecl- Butcher.addCmd "-Next-Declaration" nextDecl- Butcher.addCmd "-NEXT-DECLARATION" nextDecl- let nextBinding = do- conf <- configParser- Butcher.addCmdImpl (InlineConfigTargetNextBinding, conf)- Butcher.addCmd "-next-binding" nextBinding- Butcher.addCmd "-Next-Binding" nextBinding- Butcher.addCmd "-NEXT-BINDING" nextBinding- let disableNextBinding = do- Butcher.addCmdImpl- ( InlineConfigTargetNextBinding- , mempty { _conf_roundtrip_exactprint_only = pure $ pure True }- )- Butcher.addCmd "-disable-next-binding" disableNextBinding- Butcher.addCmd "-Disable-Next-Binding" disableNextBinding- Butcher.addCmd "-DISABLE-NEXT-BINDING" disableNextBinding- let disableNextDecl = do- Butcher.addCmdImpl- ( InlineConfigTargetNextDecl- , mempty { _conf_roundtrip_exactprint_only = pure $ pure True }- )- Butcher.addCmd "-disable-next-declaration" disableNextDecl- Butcher.addCmd "-Disable-Next-Declaration" disableNextDecl- Butcher.addCmd "-DISABLE-NEXT-DECLARATION" disableNextDecl- let disableFormatting = do- Butcher.addCmdImpl- ( InlineConfigTargetModule- , mempty { _conf_disable_formatting = pure $ pure True }- )- Butcher.addCmd "-disable" disableFormatting- Butcher.addCmd "@" $ do- -- Butcher.addCmd "module" $ do- -- conf <- configParser- -- Butcher.addCmdImpl (InlineConfigTargetModule, conf)- Butcher.addNullCmd $ do- bindingName <- Butcher.addParamString "BINDING" mempty- conf <- configParser- Butcher.addCmdImpl (InlineConfigTargetBinding bindingName, conf)- conf <- configParser- Butcher.addCmdImpl (InlineConfigTargetModule, conf)- lineConfigss <- configLiness `forM` \(k, ss) -> do- r <- ss `forM` \s -> case Butcher.runCmdParserSimple s parser of- Left err -> Left $ (err, s)- Right c -> Right $ c- pure (k, r)-- let perModule = foldl'- (<>)- mempty- [ conf- | (_ , lineConfigs) <- lineConfigss- , (InlineConfigTargetModule, conf ) <- lineConfigs- ]- let- perBinding = Map.fromListWith- (<>)- [ (n, conf)- | (k , lineConfigs) <- lineConfigss- , (target, conf ) <- lineConfigs- , n <- case target of- InlineConfigTargetBinding s -> [s]- InlineConfigTargetNextBinding | Just name <- Map.lookup k declNameMap ->- [name]- _ -> []- ]- let- perKey = Map.fromListWith- (<>)- [ (k, conf)- | (k , lineConfigs) <- lineConfigss- , (target, conf ) <- lineConfigs- , case target of- InlineConfigTargetNextDecl -> True- InlineConfigTargetNextBinding | Nothing <- Map.lookup k declNameMap ->- True- _ -> False- ]-- pure- $ ( perModule- , PerItemConfig { _icd_perBinding = perBinding, _icd_perKey = perKey }- )---getTopLevelDeclNameMap :: GHC.ParsedSource -> TopLevelDeclNameMap-getTopLevelDeclNameMap (L _ (HsModule _ _name _exports _ decls _ _)) =- TopLevelDeclNameMap $ Map.fromList- [ (ExactPrint.mkAnnKey decl, name)- | decl <- decls- , (name : _) <- [getDeclBindingNames decl]- ]----- | Exposes the transformation in an pseudo-pure fashion. The signature--- contains `IO` due to the GHC API not exposing a pure parsing function, but--- there should be no observable effects.------ Note that this function ignores/resets all config values regarding--- debugging, i.e. it will never use `trace`/write to stderr.------ Note that the ghc parsing function used internally currently is wrapped in--- `mask_`, so cannot be killed easily. If you don't control the input, you--- may wish to put some proper upper bound on the input's size as a timeout--- won't do.-parsePrintModule :: Config -> Text -> IO (Either [BrittanyError] Text)-parsePrintModule configWithDebugs inputText = runExceptT $ do- let config =- configWithDebugs { _conf_debug = _conf_debug staticDefaultConfig }- let ghcOptions = config & _conf_forward & _options_ghc & runIdentity- let config_pp = config & _conf_preprocessor- let cppMode = config_pp & _ppconf_CPPMode & confUnpack- let hackAroundIncludes = config_pp & _ppconf_hackAroundIncludes & confUnpack- (anns, parsedSource, hasCPP) <- do- let hackF s = if "#include" `isPrefixOf` s- then "-- BRITANY_INCLUDE_HACK " ++ s- else s- let hackTransform = if hackAroundIncludes- then List.intercalate "\n" . fmap hackF . lines'- else id- let cppCheckFunc dynFlags = if GHC.xopt GHC.Cpp dynFlags- then case cppMode of- CPPModeAbort -> return $ Left "Encountered -XCPP. Aborting."- CPPModeWarn -> return $ Right True- CPPModeNowarn -> return $ Right True- else return $ Right False- parseResult <- lift $ parseModuleFromString- ghcOptions- "stdin"- cppCheckFunc- (hackTransform $ Text.unpack inputText)- case parseResult of- Left err -> throwE [ErrorInput err]- Right x -> pure x- (inlineConf, perItemConf) <-- either (throwE . (: []) . uncurry ErrorMacroConfig) pure- $ extractCommentConfigs anns (getTopLevelDeclNameMap parsedSource)- let moduleConfig = cZipWith fromOptionIdentity config inlineConf- let disableFormatting = moduleConfig & _conf_disable_formatting & confUnpack- if disableFormatting- then do- return inputText- else do- (errsWarns, outputTextL) <- do- let omitCheck =- moduleConfig- & _conf_errorHandling- & _econf_omit_output_valid_check- & confUnpack- (ews, outRaw) <- if hasCPP || omitCheck- then return $ pPrintModule moduleConfig perItemConf anns parsedSource- else lift- $ pPrintModuleAndCheck moduleConfig perItemConf anns parsedSource- let hackF s = fromMaybe s- $ TextL.stripPrefix (TextL.pack "-- BRITANY_INCLUDE_HACK ") s- pure $ if hackAroundIncludes- then- ( ews- , TextL.intercalate (TextL.pack "\n") $ hackF <$> TextL.splitOn- (TextL.pack "\n")- outRaw- )- else (ews, outRaw)- let customErrOrder ErrorInput{} = 4- customErrOrder LayoutWarning{} = 0 :: Int- customErrOrder ErrorOutputCheck{} = 1- customErrOrder ErrorUnusedComment{} = 2- customErrOrder ErrorUnknownNode{} = 3- customErrOrder ErrorMacroConfig{} = 5- let hasErrors =- if moduleConfig & _conf_errorHandling & _econf_Werror & confUnpack- then not $ null errsWarns- else 0 < maximum (-1 : fmap customErrOrder errsWarns)- if hasErrors- then throwE $ errsWarns- else pure $ TextL.toStrict outputTextL------ BrittanyErrors can be non-fatal warnings, thus both are returned instead--- of an Either.--- This should be cleaned up once it is clear what kinds of errors really--- can occur.-pPrintModule- :: Config- -> PerItemConfig- -> ExactPrint.Anns- -> GHC.ParsedSource- -> ([BrittanyError], TextL.Text)-pPrintModule conf inlineConf anns parsedModule =- let ((out, errs), debugStrings) =- runIdentity- $ MultiRWSS.runMultiRWSTNil- $ MultiRWSS.withMultiWriterAW- $ MultiRWSS.withMultiWriterAW- $ MultiRWSS.withMultiWriterW- $ MultiRWSS.withMultiReader anns- $ MultiRWSS.withMultiReader conf- $ MultiRWSS.withMultiReader inlineConf- $ MultiRWSS.withMultiReader (extractToplevelAnns parsedModule anns)- $ do- traceIfDumpConf "bridoc annotations raw" _dconf_dump_annotations- $ annsDoc anns- ppModule parsedModule- tracer = if Seq.null debugStrings- then id- else- trace ("---- DEBUGMESSAGES ---- ")- . foldr (seq . join trace) id debugStrings- in tracer $ (errs, Text.Builder.toLazyText out)- -- unless () $ do- --- -- debugStrings `forM_` \s ->- -- trace s $ return ()---- | Additionally checks that the output compiles again, appending an error--- if it does not.-pPrintModuleAndCheck- :: Config- -> PerItemConfig- -> ExactPrint.Anns- -> GHC.ParsedSource- -> IO ([BrittanyError], TextL.Text)-pPrintModuleAndCheck conf inlineConf anns parsedModule = do- let ghcOptions = conf & _conf_forward & _options_ghc & runIdentity- let (errs, output) = pPrintModule conf inlineConf anns parsedModule- parseResult <- parseModuleFromString ghcOptions- "output"- (\_ -> return $ Right ())- (TextL.unpack output)- let errs' = errs ++ case parseResult of- Left{} -> [ErrorOutputCheck]- Right{} -> []- return (errs', output)----- used for testing mostly, currently.--- TODO: use parsePrintModule instead and remove this function.-parsePrintModuleTests :: Config -> String -> Text -> IO (Either String Text)-parsePrintModuleTests conf filename input = do- let inputStr = Text.unpack input- parseResult <- parseModuleFromString- (conf & _conf_forward & _options_ghc & runIdentity)- filename- (const . pure $ Right ())- inputStr- case parseResult of- Left err -> return $ Left err- Right (anns, parsedModule, _) -> runExceptT $ do- (inlineConf, perItemConf) <-- case extractCommentConfigs anns (getTopLevelDeclNameMap parsedModule) of- Left err -> throwE $ "error in inline config: " ++ show err- Right x -> pure x- let moduleConf = cZipWith fromOptionIdentity conf inlineConf- let omitCheck =- conf- & _conf_errorHandling- .> _econf_omit_output_valid_check- .> confUnpack- (errs, ltext) <- if omitCheck- then return $ pPrintModule moduleConf perItemConf anns parsedModule- else lift- $ pPrintModuleAndCheck moduleConf perItemConf anns parsedModule- if null errs- then pure $ TextL.toStrict $ ltext- else- let- errStrs = errs <&> \case- ErrorInput str -> str- ErrorUnusedComment str -> str- LayoutWarning str -> str- ErrorUnknownNode str _ -> str- ErrorMacroConfig str _ -> "when parsing inline config: " ++ str- ErrorOutputCheck -> "Output is not syntactically valid."- in throwE $ "pretty printing error(s):\n" ++ List.unlines errStrs---- this approach would for if there was a pure GHC.parseDynamicFilePragma.--- Unfortunately that does not exist yet, so we cannot provide a nominally--- pure interface.---- parsePrintModuleTests :: Text -> Either String Text--- parsePrintModuleTests input = do--- let dflags = GHC.unsafeGlobalDynFlags--- let fakeFileName = "SomeTestFakeFileName.hs"--- let pragmaInfo = GHC.getOptions--- dflags--- (GHC.stringToStringBuffer $ Text.unpack input)--- fakeFileName--- (dflags1, _, _) <- GHC.parseDynamicFilePragma dflags pragmaInfo--- let parseResult = ExactPrint.Parsers.parseWith--- dflags1--- fakeFileName--- GHC.parseModule--- inputStr--- case parseResult of--- Left (_, s) -> Left $ "parsing error: " ++ s--- Right (anns, parsedModule) -> do--- let (out, errs) = runIdentity--- $ runMultiRWSTNil--- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiWriterAW--- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiWriterW--- $ Control.Monad.Trans.MultiRWS.Lazy.withMultiReader anns--- $ ppModule parsedModule--- if (not $ null errs)--- then do--- let errStrs = errs <&> \case--- ErrorUnusedComment str -> str--- Left $ "pretty printing error(s):\n" ++ List.unlines errStrs--- else return $ TextL.toStrict $ Text.Builder.toLazyText out--toLocal :: Config -> ExactPrint.Anns -> PPMLocal a -> PPM a-toLocal conf anns m = do- (x, write) <-- lift $ MultiRWSS.runMultiRWSTAW (conf :+: anns :+: HNil) HNil $ m- MultiRWSS.mGetRawW >>= \w -> MultiRWSS.mPutRawW (w `mappend` write)- pure x--ppModule :: GenLocated SrcSpan HsModule -> PPM ()-ppModule lmod@(L _loc _m@(HsModule _ _name _exports _ decls _ _)) = do- defaultAnns <- do- anns <- mAsk- let annKey = ExactPrint.mkAnnKey lmod- let annMap = Map.findWithDefault Map.empty annKey anns- let isEof = (== ExactPrint.AnnEofPos)- let overAnnsDP f a = a { ExactPrint.annsDP = f $ ExactPrint.annsDP a }- pure $ fmap (overAnnsDP . filter $ isEof . fst) annMap-- post <- ppPreamble lmod- decls `forM_` \decl -> do- let declAnnKey = ExactPrint.mkAnnKey decl- let declBindingNames = getDeclBindingNames decl- inlineConf <- mAsk- let mDeclConf = Map.lookup declAnnKey $ _icd_perKey inlineConf- let mBindingConfs =- declBindingNames <&> \n -> Map.lookup n $ _icd_perBinding inlineConf- filteredAnns <- mAsk- <&> \annMap ->- Map.union defaultAnns $- Map.findWithDefault Map.empty declAnnKey annMap-- traceIfDumpConf "bridoc annotations filtered/transformed"- _dconf_dump_annotations- $ annsDoc filteredAnns-- config <- mAsk-- let config' = cZipWith fromOptionIdentity config- $ mconcat (catMaybes (mBindingConfs ++ [mDeclConf]))-- let exactprintOnly = config' & _conf_roundtrip_exactprint_only & confUnpack- toLocal config' filteredAnns $ do- bd <- if exactprintOnly- then briDocMToPPM $ briDocByExactNoComment decl- else do- (r, errs, debugs) <- briDocMToPPMInner $ layoutDecl decl- mTell debugs- mTell errs- if null errs- then pure r- else briDocMToPPM $ briDocByExactNoComment decl- layoutBriDoc bd-- let finalComments = filter- (fst .> \case- ExactPrint.AnnComment{} -> True- _ -> False- )- post- post `forM_` \case- (ExactPrint.AnnComment (ExactPrint.Comment cmStr _ _), l) -> do- ppmMoveToExactLoc l- mTell $ Text.Builder.fromString cmStr- (ExactPrint.AnnEofPos, (ExactPrint.DP (eofZ, eofX))) ->- let folder (acc, _) (kw, ExactPrint.DP (y, x)) = case kw of- ExactPrint.AnnComment cm- | span <- ExactPrint.commentIdentifier cm- -> ( acc + y + GHC.srcSpanEndLine span - GHC.srcSpanStartLine span- , x + GHC.srcSpanEndCol span - GHC.srcSpanStartCol span- )- _ -> (acc + y, x)- (cmY, cmX) = foldl' folder (0, 0) finalComments- in ppmMoveToExactLoc $ ExactPrint.DP (eofZ - cmY, eofX - cmX)- _ -> return ()--getDeclBindingNames :: LHsDecl GhcPs -> [String]-getDeclBindingNames (L _ decl) = case decl of- SigD _ (TypeSig _ ns _) -> ns <&> \(L _ n) -> Text.unpack (rdrNameToText n)- ValD _ (FunBind _ (L _ n) _ _) -> [Text.unpack $ rdrNameToText n]- _ -> []----- Prints the information associated with the module annotation--- This includes the imports-ppPreamble- :: GenLocated SrcSpan HsModule- -> PPM [(ExactPrint.KeywordId, ExactPrint.DeltaPos)]-ppPreamble lmod@(L loc m@HsModule{}) = do- filteredAnns <- mAsk <&> \annMap ->- Map.findWithDefault Map.empty (ExactPrint.mkAnnKey lmod) annMap- -- Since ghc-exactprint adds annotations following (implicit)- -- modules to both HsModule and the elements in the module- -- this can cause duplication of comments. So strip- -- attached annotations that come after the module's where- -- from the module node- config <- mAsk- let shouldReformatPreamble =- config & _conf_layout & _lconfig_reformatModulePreamble & confUnpack-- let- (filteredAnns', post) =- case Map.lookup (ExactPrint.mkAnnKey lmod) filteredAnns of- Nothing -> (filteredAnns, [])- Just mAnn ->- let- modAnnsDp = ExactPrint.annsDP mAnn- isWhere (ExactPrint.G AnnWhere) = True- isWhere _ = False- isEof (ExactPrint.AnnEofPos) = True- isEof _ = False- whereInd = List.findIndex (isWhere . fst) modAnnsDp- eofInd = List.findIndex (isEof . fst) modAnnsDp- (pre, post') = case (whereInd, eofInd) of- (Nothing, Nothing) -> ([], modAnnsDp)- (Just i , Nothing) -> List.splitAt (i + 1) modAnnsDp- (Nothing, Just _i) -> ([], modAnnsDp)- (Just i , Just j ) -> List.splitAt (min (i + 1) j) modAnnsDp- mAnn' = mAnn { ExactPrint.annsDP = pre }- filteredAnns'' =- Map.insert (ExactPrint.mkAnnKey lmod) mAnn' filteredAnns- in- (filteredAnns'', post')- traceIfDumpConf "bridoc annotations filtered/transformed"- _dconf_dump_annotations- $ annsDoc filteredAnns'-- if shouldReformatPreamble- then toLocal config filteredAnns' $ withTransformedAnns lmod $ do- briDoc <- briDocMToPPM $ layoutModule lmod- layoutBriDoc briDoc- else- let emptyModule = L loc m { hsmodDecls = [] }- in MultiRWSS.withMultiReader filteredAnns' $ processDefault emptyModule- return post--_sigHead :: Sig GhcPs -> String-_sigHead = \case- TypeSig _ names _ ->- "TypeSig " ++ intercalate "," (Text.unpack . lrdrNameToText <$> names)- _ -> "unknown sig"--_bindHead :: HsBind GhcPs -> String-_bindHead = \case- FunBind _ fId _ [] -> "FunBind " ++ (Text.unpack $ lrdrNameToText $ fId)- PatBind _ _pat _ ([], []) -> "PatBind smth"- _ -> "unknown bind"----layoutBriDoc :: BriDocNumbered -> PPMLocal ()-layoutBriDoc briDoc = do- -- first step: transform the briDoc.- briDoc' <- MultiRWSS.withMultiStateS BDEmpty $ do- -- Note that briDoc is BriDocNumbered, but state type is BriDoc.- -- That's why the alt-transform looks a bit special here.- traceIfDumpConf "bridoc raw" _dconf_dump_bridoc_raw- $ briDocToDoc- $ unwrapBriDocNumbered- $ briDoc- -- bridoc transformation: remove alts- transformAlts briDoc >>= mSet- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc post-alt" _dconf_dump_bridoc_simpl_alt- -- bridoc transformation: float stuff in- mGet >>= transformSimplifyFloating .> mSet- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc post-floating"- _dconf_dump_bridoc_simpl_floating- -- bridoc transformation: par removal- mGet >>= transformSimplifyPar .> mSet- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc post-par" _dconf_dump_bridoc_simpl_par- -- bridoc transformation: float stuff in- mGet >>= transformSimplifyColumns .> mSet- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc post-columns" _dconf_dump_bridoc_simpl_columns- -- bridoc transformation: indent- mGet >>= transformSimplifyIndent .> mSet- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc post-indent" _dconf_dump_bridoc_simpl_indent- mGet- >>= briDocToDoc- .> traceIfDumpConf "bridoc final" _dconf_dump_bridoc_final- -- -- convert to Simple type- -- simpl <- mGet <&> transformToSimple- -- return simpl-- anns :: ExactPrint.Anns <- mAsk-- let state = LayoutState { _lstate_baseYs = [0]- , _lstate_curYOrAddNewline = Right 0 -- important that we dont use left- -- here because moveToAnn stuff- -- of the first node needs to do- -- its thing properly.- , _lstate_indLevels = [0]- , _lstate_indLevelLinger = 0- , _lstate_comments = anns- , _lstate_commentCol = Nothing- , _lstate_addSepSpace = Nothing- , _lstate_commentNewlines = 0- }-- state' <- MultiRWSS.withMultiStateS state $ layoutBriDocM briDoc'-- let remainingComments =- [ c- | (ExactPrint.AnnKey _ con, elemAnns) <- Map.toList- (_lstate_comments state')- -- With the new import layouter, we manually process comments- -- without relying on the backend to consume the comments out of- -- the state/map. So they will end up here, and we need to ignore- -- them.- , ExactPrint.unConName con /= "ImportDecl"- , c <- extractAllComments elemAnns- ]- remainingComments- `forM_` (fst .> show .> ErrorUnusedComment .> (: []) .> mTell)-- return $ ()
− source/library/Language/Haskell/Brittany/Internal/Backend.hs
@@ -1,707 +0,0 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal.Backend where--import qualified Control.Monad.Trans.State.Strict as StateS-import qualified Data.Either as Either-import qualified Data.Foldable as Foldable-import qualified Data.IntMap.Lazy as IntMapL-import qualified Data.IntMap.Strict as IntMapS-import qualified Data.Map as Map-import qualified Data.Maybe as Maybe-import qualified Data.Semigroup as Semigroup-import qualified Data.Sequence as Seq-import qualified Data.Set as Set-import qualified Data.Text as Text-import qualified Data.Text.Lazy.Builder as Text.Builder-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.BackendUtils-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types----type ColIndex = Int--data ColumnSpacing- = ColumnSpacingLeaf Int- | ColumnSpacingRef Int Int--type ColumnBlock a = [a]-type ColumnBlocks a = Seq [a]-type ColMap1 = IntMapL.IntMap {- ColIndex -} (Bool, ColumnBlocks ColumnSpacing)-type ColMap2 = IntMapL.IntMap {- ColIndex -} (Float, ColumnBlock Int, ColumnBlocks Int)- -- (ratio of hasSpace, maximum, raw)--data ColInfo- = ColInfoStart -- start value to begin the mapAccumL.- | ColInfoNo BriDoc- | ColInfo ColIndex ColSig [(Int, ColInfo)]--instance Show ColInfo where- show ColInfoStart = "ColInfoStart"- show (ColInfoNo bd) = "ColInfoNo " ++ show (take 30 (show (briDocToDoc bd)) ++ "..")- show (ColInfo ind sig list) = "ColInfo " ++ show ind ++ " " ++ show sig ++ " " ++ show list--data ColBuildState = ColBuildState- { _cbs_map :: ColMap1- , _cbs_index :: ColIndex- }--type LayoutConstraints m = ( MonadMultiReader Config m- , MonadMultiReader ExactPrint.Types.Anns m- , MonadMultiWriter Text.Builder.Builder m- , MonadMultiWriter (Seq String) m- , MonadMultiState LayoutState m- )--layoutBriDocM :: forall m . LayoutConstraints m => BriDoc -> m ()-layoutBriDocM = \case- BDEmpty -> do- return () -- can it be that simple- BDLit t -> do- layoutIndentRestorePostComment- layoutRemoveIndentLevelLinger- layoutWriteAppend t- BDSeq list -> do- list `forM_` layoutBriDocM- -- in this situation, there is nothing to do about cols.- -- i think this one does not happen anymore with the current simplifications.- -- BDCols cSig list | BDPar sameLine lines <- List.last list ->- -- alignColsPar $ BDCols cSig (List.init list ++ [sameLine]) : lines- BDCols _ list -> do- list `forM_` layoutBriDocM- BDSeparator -> do- layoutAddSepSpace- BDAddBaseY indent bd -> do- let indentF = case indent of- BrIndentNone -> id- BrIndentRegular -> layoutWithAddBaseCol- BrIndentSpecial i -> layoutWithAddBaseColN i- indentF $ layoutBriDocM bd- BDBaseYPushCur bd -> do- layoutBaseYPushCur- layoutBriDocM bd- BDBaseYPop bd -> do- layoutBriDocM bd- layoutBaseYPop- BDIndentLevelPushCur bd -> do- layoutIndentLevelPushCur- layoutBriDocM bd- BDIndentLevelPop bd -> do- layoutBriDocM bd- layoutIndentLevelPop- BDEnsureIndent indent bd -> do- let indentF = case indent of- BrIndentNone -> id- BrIndentRegular -> layoutWithAddBaseCol- BrIndentSpecial i -> layoutWithAddBaseColN i- indentF $ do- layoutWriteEnsureBlock- layoutBriDocM bd- BDPar indent sameLine indented -> do- layoutBriDocM sameLine- let indentF = case indent of- BrIndentNone -> id- BrIndentRegular -> layoutWithAddBaseCol- BrIndentSpecial i -> layoutWithAddBaseColN i- indentF $ do- layoutWriteNewlineBlock- layoutBriDocM indented- BDLines lines -> alignColsLines lines- BDAlt [] -> error "empty BDAlt"- BDAlt (alt:_) -> layoutBriDocM alt- BDForceMultiline bd -> layoutBriDocM bd- BDForceSingleline bd -> layoutBriDocM bd- BDForwardLineMode bd -> layoutBriDocM bd- BDExternal annKey subKeys shouldAddComment t -> do- let tlines = Text.lines $ t <> Text.pack "\n"- tlineCount = length tlines- anns :: ExactPrint.Anns <- mAsk- when shouldAddComment $ do- layoutWriteAppend- $ Text.pack- $ "{-"- ++ show (annKey, Map.lookup annKey anns)- ++ "-}"- zip [1 ..] tlines `forM_` \(i, l) -> do- layoutWriteAppend $ l- unless (i == tlineCount) layoutWriteNewlineBlock- do- state <- mGet- let filterF k _ = not $ k `Set.member` subKeys- mSet $ state- { _lstate_comments = Map.filterWithKey filterF $ _lstate_comments state- }- BDPlain t -> do- layoutWriteAppend t- BDAnnotationPrior annKey bd -> do- state <- mGet- let m = _lstate_comments state- let moveToExactLocationAction = case _lstate_curYOrAddNewline state of- Left{} -> pure ()- Right{} -> moveToExactAnn annKey- mAnn <- do- let mAnn = ExactPrint.annPriorComments <$> Map.lookup annKey m- mSet $ state- { _lstate_comments = Map.adjust- (\ann -> ann { ExactPrint.annPriorComments = [] })- annKey- m- }- return mAnn- case mAnn of- Nothing -> moveToExactLocationAction- Just [] -> moveToExactLocationAction- Just priors -> do- -- layoutResetSepSpace- priors- `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->- when (comment /= "(" && comment /= ")") $ do- let commentLines = Text.lines $ Text.pack $ comment- case comment of- ('#':_) -> layoutMoveToCommentPos y (-999) (length commentLines)- -- ^ evil hack for CPP- _ -> layoutMoveToCommentPos y x (length commentLines)- -- fixedX <- fixMoveToLineByIsNewline x- -- replicateM_ fixedX layoutWriteNewline- -- layoutMoveToIndentCol y- layoutWriteAppendMultiline commentLines- -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }- moveToExactLocationAction- layoutBriDocM bd- BDAnnotationKW annKey keyword bd -> do- layoutBriDocM bd- mComments <- do- state <- mGet- let m = _lstate_comments state- let mAnn = ExactPrint.annsDP <$> Map.lookup annKey m- let mToSpan = case mAnn of- Just anns | Maybe.isNothing keyword -> Just anns- Just ((ExactPrint.Types.G kw1, _):annR) | keyword == Just kw1 -> Just- annR- _ -> Nothing- case mToSpan of- Just anns -> do- let (comments, rest) = flip spanMaybe anns $ \case- (ExactPrint.Types.AnnComment x, dp) -> Just (x, dp)- _ -> Nothing- mSet $ state- { _lstate_comments = Map.adjust- (\ann -> ann { ExactPrint.annsDP = rest })- annKey- m- }- return $ nonEmpty comments- _ -> return Nothing- case mComments of- Nothing -> pure ()- Just comments -> do- comments `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->- when (comment /= "(" && comment /= ")") $ do- let commentLines = Text.lines $ Text.pack $ comment- -- evil hack for CPP:- case comment of- ('#':_) -> layoutMoveToCommentPos y (-999) (length commentLines)- _ -> layoutMoveToCommentPos y x (length commentLines)- -- fixedX <- fixMoveToLineByIsNewline x- -- replicateM_ fixedX layoutWriteNewline- -- layoutMoveToIndentCol y- layoutWriteAppendMultiline commentLines- -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }- BDAnnotationRest annKey bd -> do- layoutBriDocM bd- annMay <- do- state <- mGet- let m = _lstate_comments state- pure $ Map.lookup annKey m- let mComments = nonEmpty . extractAllComments =<< annMay- let semiCount = length [ ()- | Just ann <- [ annMay ]- , (ExactPrint.Types.AnnSemiSep, _) <- ExactPrint.Types.annsDP ann- ]- shouldAddSemicolonNewlines <- mAsk <&>- _conf_layout .> _lconfig_experimentalSemicolonNewlines .> confUnpack- mModify $ \state -> state- { _lstate_comments = Map.adjust- ( \ann -> ann { ExactPrint.annFollowingComments = []- , ExactPrint.annPriorComments = []- , ExactPrint.annsDP =- flip filter (ExactPrint.annsDP ann) $ \case- (ExactPrint.Types.AnnComment{}, _) -> False- _ -> True- }- )- annKey- (_lstate_comments state)- }- case mComments of- Nothing -> do- when shouldAddSemicolonNewlines $ do- [1..semiCount] `forM_` const layoutWriteNewline- Just comments -> do- comments `forM_` \(ExactPrint.Types.Comment comment _ _, ExactPrint.Types.DP (y, x)) ->- when (comment /= "(" && comment /= ")") $ do- let commentLines = Text.lines $ Text.pack comment- case comment of- ('#':_) -> layoutMoveToCommentPos y (-999) 1- -- ^ evil hack for CPP- ")" -> pure ()- -- ^ fixes the formatting of parens- -- on the lhs of type alias defs- _ -> layoutMoveToCommentPos y x (length commentLines)- -- fixedX <- fixMoveToLineByIsNewline x- -- replicateM_ fixedX layoutWriteNewline- -- layoutMoveToIndentCol y- layoutWriteAppendMultiline commentLines- -- mModify $ \s -> s { _lstate_curYOrAddNewline = Right 0 }- BDMoveToKWDP annKey keyword shouldRestoreIndent bd -> do- mDP <- do- state <- mGet- let m = _lstate_comments state- let mAnn = ExactPrint.annsDP <$> Map.lookup annKey m- let relevant = [ dp- | Just ann <- [mAnn]- , (ExactPrint.Types.G kw1, dp) <- ann- , keyword == kw1- ]- -- mTell $ Seq.fromList [show keyword, "KWDP: " ++ show annKey ++ " " ++ show mAnn, show relevant]- case relevant of- [] -> pure Nothing- (ExactPrint.Types.DP (y, x):_) -> do- mSet state { _lstate_commentNewlines = 0 }- pure $ Just (y - _lstate_commentNewlines state, x)- case mDP of- Nothing -> pure ()- Just (y, x) ->- -- we abuse this, as we probably will print the KW next, which is- -- _not_ a comment..- layoutMoveToCommentPos y (if shouldRestoreIndent then x else 0) 1- layoutBriDocM bd- BDNonBottomSpacing _ bd -> layoutBriDocM bd- BDSetParSpacing bd -> layoutBriDocM bd- BDForceParSpacing bd -> layoutBriDocM bd- BDDebug s bd -> do- mTell $ Text.Builder.fromText $ Text.pack $ "{-" ++ s ++ "-}"- layoutBriDocM bd--briDocLineLength :: BriDoc -> Int-briDocLineLength briDoc = flip StateS.evalState False $ rec briDoc- -- the state encodes whether a separator was already- -- appended at the current position.- where- rec = \case- BDEmpty -> return $ 0- BDLit t -> StateS.put False $> Text.length t- BDSeq bds -> sum <$> rec `mapM` bds- BDCols _ bds -> sum <$> rec `mapM` bds- BDSeparator -> StateS.get >>= \b -> StateS.put True $> if b then 0 else 1- BDAddBaseY _ bd -> rec bd- BDBaseYPushCur bd -> rec bd- BDBaseYPop bd -> rec bd- BDIndentLevelPushCur bd -> rec bd- BDIndentLevelPop bd -> rec bd- BDPar _ line _ -> rec line- BDAlt{} -> error "briDocLineLength BDAlt"- BDForceMultiline bd -> rec bd- BDForceSingleline bd -> rec bd- BDForwardLineMode bd -> rec bd- BDExternal _ _ _ t -> return $ Text.length t- BDPlain t -> return $ Text.length t- BDAnnotationPrior _ bd -> rec bd- BDAnnotationKW _ _ bd -> rec bd- BDAnnotationRest _ bd -> rec bd- BDMoveToKWDP _ _ _ bd -> rec bd- BDLines ls@(_ : _) -> do- x <- StateS.get- return $ maximum $ ls <&> \l -> StateS.evalState (rec l) x- BDLines [] -> error "briDocLineLength BDLines []"- BDEnsureIndent _ bd -> rec bd- BDSetParSpacing bd -> rec bd- BDForceParSpacing bd -> rec bd- BDNonBottomSpacing _ bd -> rec bd- BDDebug _ bd -> rec bd--briDocIsMultiLine :: BriDoc -> Bool-briDocIsMultiLine briDoc = rec briDoc- where- rec :: BriDoc -> Bool- rec = \case- BDEmpty -> False- BDLit _ -> False- BDSeq bds -> any rec bds- BDCols _ bds -> any rec bds- BDSeparator -> False- BDAddBaseY _ bd -> rec bd- BDBaseYPushCur bd -> rec bd- BDBaseYPop bd -> rec bd- BDIndentLevelPushCur bd -> rec bd- BDIndentLevelPop bd -> rec bd- BDPar{} -> True- BDAlt{} -> error "briDocIsMultiLine BDAlt"- BDForceMultiline _ -> True- BDForceSingleline bd -> rec bd- BDForwardLineMode bd -> rec bd- BDExternal _ _ _ t | [_] <- Text.lines t -> False- BDExternal{} -> True- BDPlain t | [_] <- Text.lines t -> False- BDPlain _ -> True- BDAnnotationPrior _ bd -> rec bd- BDAnnotationKW _ _ bd -> rec bd- BDAnnotationRest _ bd -> rec bd- BDMoveToKWDP _ _ _ bd -> rec bd- BDLines (_ : _ : _) -> True- BDLines [_ ] -> False- BDLines [] -> error "briDocIsMultiLine BDLines []"- BDEnsureIndent _ bd -> rec bd- BDSetParSpacing bd -> rec bd- BDForceParSpacing bd -> rec bd- BDNonBottomSpacing _ bd -> rec bd- BDDebug _ bd -> rec bd---- In theory--- =========---- .. this algorithm works roughly in these steps:------ 1. For each line, get the (nested) column info, descending as far as--- BDCols nodes go. The column info is a (rose) tree where the leafs--- are arbitrary (non-BDCols) BriDocs.--- 2. Walk through the lines and compare its column info with that of its--- predecessor. If both are non-leafs and the column "signatures" align--- (they don't align e.g. when they are totally different syntactical--- structures or the number of children differs), mark these parts of--- the two tree structures as connected and recurse to its children--- (i.e. again comparing the children in this line with the children in--- the previous line).--- 3. What we now have is one tree per line, and connections between "same"--- nodes between lines. These connection can span multiple lines.--- We next look at spacing information. This is available at the leafs,--- but in this step we aggregate _over connections_. At the top level, this--- gives us one piece of data: How long would each line be, if we fully--- aligned everything (kept all connections "active"). In contrast to--- just taking the sum of all leafs for each tree, this line length includes--- the spaces used for alignment.--- 4. Treat those lines where alignment would result in overflowing of the--- column limit. This "treatment" is currently configurable, and can e.g.--- mean:--- a) we stop alignment alltogether,--- b) we remove alignment just from the overflowing lines,--- c) we reduce the number of spaces inserted in overflowing lines using--- some technique to make them not overflow, but without reducing the--- space insertion to zero,--- d) don't do anything--- 5. Actually print the lines, walking over each tree and inserting spaces--- according to the info and decisions gathered in the previous steps.------ Possible improvements--- =====================------ - If alignment is disabled for specific lines, the aggregated per-connection--- info of those lines is still retained and not recalculated. This can--- result in spaces being inserted to create alignment with a line that--- would overflow and thus gets disabled entirely.--- An better approach would be to repeat step 3 after marking overflowing--- lines as such, and not include the overflowing spacings as references--- for non-overflowing ones. In the simplest case one additional iteration--- would suffice, e.g. 1-2-3-4-3-5, but it would also be possible to refine--- this and first remove alignment in the deepest parts of the tree for--- overflowing lines, repeating and moving upwards until no lines are--- anymore overflowing.--- Further, it may make sense to break up connections when overflowing would--- occur.--- - It may also make sense to not filter all overflowing lines, but remove--- them one-by-one and in each step recalculate the aggregated connection--- spacing info. Because removing one overflowing line from the calculation--- may very well cause another previously overflowing line to not overflow--- any longer.--- There is also a nasty optimization problem hiding in there (find the--- minimal amount of alignment disabling that results in no overflows)--- but that is overkill.------ (with both these improvements there would be quite some repetition between--- steps 3 and 4, but it should be possible to ensure termination. Still,--- performance might become an issue as such an approach is not necessarily--- linear in bridoc size any more.)------ In practice--- ===========------ .. the current implementation is somewhat sloppy. Steps 1 and 2--- are executed in one step, step 3 already applies one strategy that disables--- certain connections (see `_lconfig_alignmentLimit`) and step 4 does some--- of the calculations one might expect to occur in step 3. Steps 4 and 5--- are executed in the same recursion, too.--- Also, _lconfig_alignmentLimit really is itself a hack that hides the issue--- mentioned in the first "possible improvement".-alignColsLines :: LayoutConstraints m => [BriDoc] -> m ()-alignColsLines bridocs = do -- colInfos `forM_` \colInfo -> do- -- tellDebugMess ("alignColsLines: at " ++ take 100 (show $ briDocToDoc $ head bridocs))- curX <- do- state <- mGet- return $ Either.fromLeft 0 (_lstate_curYOrAddNewline state) + fromMaybe- 0- (_lstate_addSepSpace state)- colMax <- mAsk <&> _conf_layout .> _lconfig_cols .> confUnpack- alignMax <- mAsk <&> _conf_layout .> _lconfig_alignmentLimit .> confUnpack- alignBreak <-- mAsk <&> _conf_layout .> _lconfig_alignmentBreakOnMultiline .> confUnpack- case () of- _ -> do- -- tellDebugMess ("processedMap: " ++ show processedMap)- sequence_- $ List.intersperse layoutWriteEnsureNewlineBlock- $ colInfos- <&> processInfo colMax processedMap- where- (colInfos, finalState) =- StateS.runState (mergeBriDocs bridocs) (ColBuildState IntMapS.empty 0)- -- maxZipper :: [Int] -> [Int] -> [Int]- -- maxZipper [] ys = ys- -- maxZipper xs [] = xs- -- maxZipper (x:xr) (y:yr) = max x y : maxZipper xr yr- colAggregation :: [Int] -> Int- colAggregation [] = 0 -- this probably cannot happen the way we call- -- this function, because _cbs_map only ever- -- contains nonempty Seqs.- colAggregation xs = maximum [ x | x <- xs, x <= minimum xs + alignMax' ]- where alignMax' = max 0 alignMax-- processedMap :: ColMap2- processedMap =- fix $ \result -> _cbs_map finalState <&> \(lastFlag, colSpacingss) ->- let- colss = colSpacingss <&> \spss -> case reverse spss of- [] -> []- (xN:xR) ->- reverse $ (if lastFlag then fLast else fInit) xN : fmap fInit xR- where- fLast (ColumnSpacingLeaf len ) = len- fLast (ColumnSpacingRef len _) = len- fInit (ColumnSpacingLeaf len) = len- fInit (ColumnSpacingRef _ i ) = case IntMapL.lookup i result of- Nothing -> 0- Just (_, maxs, _) -> sum maxs- maxCols = {-Foldable.foldl1 maxZipper-}- fmap colAggregation $ transpose $ Foldable.toList colss- (_, posXs) = -- trace ("colss=" ++ show colss ++ ", maxCols=" ++ show maxCols ++ " for " ++ take 100 (show $ briDocToDoc $ head bridocs)) $- mapAccumL (\acc x -> (acc + x, acc)) curX maxCols- counter count l = if List.last posXs + List.last l <= colMax- then count + 1- else count- ratio = fromIntegral (foldl counter (0 :: Int) colss)- / fromIntegral (length colss)- in- (ratio, maxCols, colss)-- mergeBriDocs :: [BriDoc] -> StateS.State ColBuildState [ColInfo]- mergeBriDocs bds = mergeBriDocsW ColInfoStart bds-- mergeBriDocsW- :: ColInfo -> [BriDoc] -> StateS.State ColBuildState [ColInfo]- mergeBriDocsW _ [] = return []- mergeBriDocsW lastInfo (bd:bdr) = do- info <- mergeInfoBriDoc True lastInfo bd- infor <- mergeBriDocsW- -- (if alignBreak && briDocIsMultiLine bd then ColInfoStart else info)- (if shouldBreakAfter bd then ColInfoStart else info)- bdr- return $ info : infor-- -- even with alignBreak config flag, we don't stop aligning for certain- -- ColSigs - the ones with "False" below. The main reason is that- -- there are uses of BDCols where they provide the alignment of several- -- consecutive full larger code segments, for example ColOpPrefix.- -- Motivating example is- -- > foo- -- > $ [ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa- -- > , bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb- -- > ]- -- > ++ [ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ]- -- If we break the alignment here, then all three lines for the first- -- list move left by one, which is horrible. We really don't want to- -- break whole-block alignments.- -- For list, listcomp, tuple and tuples the reasoning is much simpler:- -- alignment should not have much effect anyways, so i simply make the- -- choice here that enabling alignment is the safer route for preventing- -- potential glitches, and it should never have a negative effect.- -- For RecUpdate the argument is much less clear - it is mostly a- -- personal preference to not break alignment for those, even if- -- multiline. Really, this should be configurable.. (TODO)- shouldBreakAfter :: BriDoc -> Bool- shouldBreakAfter bd = alignBreak &&- briDocIsMultiLine bd && case bd of- (BDCols ColTyOpPrefix _) -> False- (BDCols ColPatternsFuncPrefix _) -> True- (BDCols ColPatternsFuncInfix _) -> True- (BDCols ColPatterns _) -> True- (BDCols ColCasePattern _) -> True- (BDCols ColBindingLine{} _) -> True- (BDCols ColGuard _) -> True- (BDCols ColGuardedBody _) -> True- (BDCols ColBindStmt _) -> True- (BDCols ColDoLet _) -> True- (BDCols ColRec _) -> False- (BDCols ColRecUpdate _) -> False- (BDCols ColRecDecl _) -> False- (BDCols ColListComp _) -> False- (BDCols ColList _) -> False- (BDCols ColApp{} _) -> True- (BDCols ColTuple _) -> False- (BDCols ColTuples _) -> False- (BDCols ColOpPrefix _) -> False- _ -> True-- mergeInfoBriDoc- :: Bool- -> ColInfo- -> BriDoc- -> StateS.StateT ColBuildState Identity ColInfo- mergeInfoBriDoc lastFlag ColInfoStart = briDocToColInfo lastFlag- mergeInfoBriDoc lastFlag ColInfoNo{} = briDocToColInfo lastFlag- mergeInfoBriDoc lastFlag (ColInfo infoInd infoSig subLengthsInfos) =- \case- brdc@(BDCols colSig subDocs)- | infoSig == colSig && length subLengthsInfos == length subDocs- -> do- let- isLastList = if lastFlag- then (==length subDocs) <$> [1 ..]- else repeat False- infos <- zip3 isLastList (snd <$> subLengthsInfos) subDocs- `forM` \(lf, info, bd) -> mergeInfoBriDoc lf info bd- let curLengths = briDocLineLength <$> subDocs- let trueSpacings = getTrueSpacings (zip curLengths infos)- do -- update map- s <- StateS.get- let m = _cbs_map s- let (Just (_, spaces)) = IntMapS.lookup infoInd m- StateS.put s- { _cbs_map = IntMapS.insert- infoInd- (lastFlag, spaces Seq.|> trueSpacings)- m- }- return $ ColInfo infoInd colSig (zip curLengths infos)- | otherwise- -> briDocToColInfo lastFlag brdc- brdc -> return $ ColInfoNo brdc--briDocToColInfo :: Bool -> BriDoc -> StateS.State ColBuildState ColInfo-briDocToColInfo lastFlag = \case- BDCols sig list -> withAlloc lastFlag $ \ind -> do- let isLastList =- if lastFlag then (==length list) <$> [1 ..] else repeat False- subInfos <- zip isLastList list `forM` uncurry briDocToColInfo- let lengthInfos = zip (briDocLineLength <$> list) subInfos- let trueSpacings = getTrueSpacings lengthInfos- return $ (Seq.singleton trueSpacings, ColInfo ind sig lengthInfos)- bd -> return $ ColInfoNo bd--getTrueSpacings :: [(Int, ColInfo)] -> [ColumnSpacing]-getTrueSpacings lengthInfos = lengthInfos <&> \case- (len, ColInfo i _ _) -> ColumnSpacingRef len i- (len, _ ) -> ColumnSpacingLeaf len--withAlloc- :: Bool- -> ( ColIndex- -> StateS.State ColBuildState (ColumnBlocks ColumnSpacing, ColInfo)- )- -> StateS.State ColBuildState ColInfo-withAlloc lastFlag f = do- cbs <- StateS.get- let ind = _cbs_index cbs- StateS.put $ cbs { _cbs_index = ind + 1 }- (space, info) <- f ind- StateS.get >>= \c -> StateS.put- $ c { _cbs_map = IntMapS.insert ind (lastFlag, space) $ _cbs_map c }- return info--processInfo :: LayoutConstraints m => Int -> ColMap2 -> ColInfo -> m ()-processInfo maxSpace m = \case- ColInfoStart -> error "should not happen (TM)"- ColInfoNo doc -> layoutBriDocM doc- ColInfo ind _ list -> -- trace ("processInfo ind=" ++ show ind ++ ", list=" ++ show list ++ ", colmap=" ++ show m) $- do- colMaxConf <- mAsk <&> _conf_layout .> _lconfig_cols .> confUnpack- alignMode <- mAsk <&> _conf_layout .> _lconfig_columnAlignMode .> confUnpack- curX <- do- state <- mGet- -- tellDebugMess ("processInfo: " ++ show (_lstate_curYOrAddNewline state) ++ " - " ++ show ((_lstate_addSepSpace state)))- let spaceAdd = fromMaybe 0 $ _lstate_addSepSpace state- return $ case _lstate_curYOrAddNewline state of- Left i -> case _lstate_commentCol state of- Nothing -> spaceAdd + i- Just c -> c- Right{} -> spaceAdd- let colMax = min colMaxConf (curX + maxSpace)- -- tellDebugMess $ show curX- let Just (ratio, maxCols1, _colss) = IntMapS.lookup ind m- let maxCols2 = list <&> \case- (_, ColInfo i _ _) ->- let Just (_, ms, _) = IntMapS.lookup i m in sum ms- (l, _) -> l- let maxCols = zipWith max maxCols1 maxCols2- let (maxX, posXs) = mapAccumL (\acc x -> (acc + x, acc)) curX maxCols- -- handle the cases that the vertical alignment leads to more than max- -- cols:- -- this is not a full fix, and we must correct individually in addition.- -- because: the (at least) line with the largest element in the last- -- column will always still overflow, because we just updated the column- -- sizes in such a way that it works _if_ we have sizes (*factor)- -- in each column. but in that line, in the last column, we will be- -- forced to occupy the full vertical space, not reduced by any factor.- let fixedPosXs = case alignMode of- ColumnAlignModeAnimouslyScale i | maxX > colMax -> fixed <&> (+curX)- where- factor :: Float =- -- 0.0001 as an offering to the floating point gods.- min- 1.0001- (fromIntegral (i + colMax - curX) / fromIntegral (maxX - curX))- offsets = (subtract curX) <$> posXs- fixed = offsets <&> fromIntegral .> (*factor) .> truncate- _ -> posXs- let spacings = zipWith (-)- (List.tail fixedPosXs ++ [min maxX colMax])- fixedPosXs- -- tellDebugMess $ "ind = " ++ show ind- -- tellDebugMess $ "maxCols = " ++ show maxCols- -- tellDebugMess $ "fixedPosXs = " ++ show fixedPosXs- -- tellDebugMess $ "list = " ++ show list- -- tellDebugMess $ "maxSpace = " ++ show maxSpace- let alignAct = zip3 fixedPosXs spacings list `forM_` \(destX, s, x) -> do- layoutWriteEnsureAbsoluteN destX- processInfo s m (snd x)- noAlignAct = list `forM_` (snd .> processInfoIgnore)- animousAct = -- trace ("animousAct fixedPosXs=" ++ show fixedPosXs ++ ", list=" ++ show list ++ ", maxSpace=" ++ show maxSpace ++ ", colMax="++show colMax) $- if List.last fixedPosXs + fst (List.last list) > colMax- -- per-item check if there is overflowing.- then noAlignAct- else alignAct- case alignMode of- ColumnAlignModeDisabled -> noAlignAct- ColumnAlignModeUnanimously | maxX <= colMax -> alignAct- ColumnAlignModeUnanimously -> noAlignAct- ColumnAlignModeMajority limit | ratio >= limit -> animousAct- ColumnAlignModeMajority{} -> noAlignAct- ColumnAlignModeAnimouslyScale{} -> animousAct- ColumnAlignModeAnimously -> animousAct- ColumnAlignModeAlways -> alignAct--processInfoIgnore :: LayoutConstraints m => ColInfo -> m ()-processInfoIgnore = \case- ColInfoStart -> error "should not happen (TM)"- ColInfoNo doc -> layoutBriDocM doc- ColInfo _ _ list -> list `forM_` (snd .> processInfoIgnore)
− source/library/Language/Haskell/Brittany/Internal/BackendUtils.hs
@@ -1,522 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.BackendUtils where--import qualified Data.Data-import qualified Data.Either-import qualified Data.Map as Map-import qualified Data.Maybe-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import qualified Data.Text.Lazy.Builder as Text.Builder-import GHC (Located)-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils-import Language.Haskell.GHC.ExactPrint.Types (AnnKey, Annotation)-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint----traceLocal- :: (MonadMultiState LayoutState m)- => a- -> m ()-traceLocal _ = return ()---layoutWriteAppend- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Text- -> m ()-layoutWriteAppend t = do- traceLocal ("layoutWriteAppend", t)- state <- mGet- case _lstate_curYOrAddNewline state of- Right i -> do- replicateM_ i $ mTell $ Text.Builder.fromString $ "\n"- Left{} -> do- return ()- let spaces = fromMaybe 0 $ _lstate_addSepSpace state- mTell $ Text.Builder.fromText $ Text.pack (replicate spaces ' ')- mTell $ Text.Builder.fromText $ t- mModify $ \s -> s- { _lstate_curYOrAddNewline = Left $ case _lstate_curYOrAddNewline s of- Left c -> c + Text.length t + spaces- Right{} -> Text.length t + spaces- , _lstate_addSepSpace = Nothing- }--layoutWriteAppendSpaces- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Int- -> m ()-layoutWriteAppendSpaces i = do- traceLocal ("layoutWriteAppendSpaces", i)- unless (i == 0) $ do- state <- mGet- mSet $ state- { _lstate_addSepSpace = Just $ maybe i (+i) $ _lstate_addSepSpace state- }--layoutWriteAppendMultiline- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => [Text]- -> m ()-layoutWriteAppendMultiline ts = do- traceLocal ("layoutWriteAppendMultiline", ts)- case ts of- [] -> layoutWriteAppend (Text.pack "") -- need to write empty, too.- (l:lr) -> do- layoutWriteAppend l- lr `forM_` \x -> do- layoutWriteNewline- layoutWriteAppend x---- adds a newline and adds spaces to reach the base column.-layoutWriteNewlineBlock- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => m ()-layoutWriteNewlineBlock = do- traceLocal ("layoutWriteNewlineBlock")- state <- mGet- mSet $ state { _lstate_curYOrAddNewline = Right 1- , _lstate_addSepSpace = Just $ lstate_baseY state- }---- layoutMoveToIndentCol :: ( MonadMultiState LayoutState m--- , MonadMultiWriter (Seq String) m) => Int -> m ()--- layoutMoveToIndentCol i = do--- #if INSERTTRACES--- tellDebugMessShow ("layoutMoveToIndentCol", i)--- #endif--- state <- mGet--- mSet $ state--- { _lstate_addSepSpace = Just--- $ if isJust $ _lstate_addNewline state--- then i--- else _lstate_indLevelLinger state + i - _lstate_curY state--- }--layoutSetCommentCol- :: (MonadMultiState LayoutState m) => m ()-layoutSetCommentCol = do- state <- mGet- let col = case _lstate_curYOrAddNewline state of- Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)- Right{} -> lstate_baseY state- traceLocal ("layoutSetCommentCol", col)- unless (Data.Maybe.isJust $ _lstate_commentCol state)- $ mSet state { _lstate_commentCol = Just col }---- This is also used to move to non-comments in a couple of places. Seems--- to be harmless so far..-layoutMoveToCommentPos- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Int- -> Int- -> Int- -> m ()-layoutMoveToCommentPos y x commentLines = do- traceLocal ("layoutMoveToCommentPos", y, x, commentLines)- state <- mGet- mSet state- { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of- Left i -> if y == 0 then Left i else Right y- Right{} -> Right y- , _lstate_addSepSpace =- Just $ if Data.Maybe.isJust (_lstate_commentCol state)- then case _lstate_curYOrAddNewline state of- Left{} -> if y == 0 then x else _lstate_indLevelLinger state + x- Right{} -> _lstate_indLevelLinger state + x- else if y == 0 then x else _lstate_indLevelLinger state + x- , _lstate_commentCol =- Just $ case _lstate_commentCol state of- Just existing -> existing- Nothing -> case _lstate_curYOrAddNewline state of- Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)- Right{} -> lstate_baseY state- , _lstate_commentNewlines =- _lstate_commentNewlines state + y + commentLines - 1- }---- | does _not_ add spaces to again reach the current base column.-layoutWriteNewline- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => m ()-layoutWriteNewline = do- traceLocal ("layoutWriteNewline")- state <- mGet- mSet $ state- { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of- Left{} -> Right 1- Right i -> Right (i + 1)- , _lstate_addSepSpace = Nothing- }--_layoutResetCommentNewlines :: MonadMultiState LayoutState m => m ()-_layoutResetCommentNewlines = do- mModify $ \state -> state { _lstate_commentNewlines = 0 }--layoutWriteEnsureNewlineBlock- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => m ()-layoutWriteEnsureNewlineBlock = do- traceLocal ("layoutWriteEnsureNewlineBlock")- state <- mGet- mSet $ state- { _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of- Left{} -> Right 1- Right i -> Right $ max 1 i- , _lstate_addSepSpace = Just $ lstate_baseY state- , _lstate_commentCol = Nothing- }--layoutWriteEnsureAbsoluteN- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Int- -> m ()-layoutWriteEnsureAbsoluteN n = do- state <- mGet- let diff = case (_lstate_commentCol state, _lstate_curYOrAddNewline state) of- (Just c , _ ) -> n - c- (Nothing, Left i ) -> n - i- (Nothing, Right{}) -> n- traceLocal ("layoutWriteEnsureAbsoluteN", n, diff)- when (diff > 0) $ do- mSet $ state { _lstate_addSepSpace = Just diff -- this always sets to- -- at least (Just 1), so we won't- -- overwrite any old value in any- -- bad way.- }--layoutBaseYPushInternal- :: (MonadMultiState LayoutState m)- => Int- -> m ()-layoutBaseYPushInternal i = do- traceLocal ("layoutBaseYPushInternal", i)- mModify $ \s -> s { _lstate_baseYs = i : _lstate_baseYs s }--layoutBaseYPopInternal- :: (MonadMultiState LayoutState m) => m ()-layoutBaseYPopInternal = do- traceLocal ("layoutBaseYPopInternal")- mModify $ \s -> s { _lstate_baseYs = List.tail $ _lstate_baseYs s }--layoutIndentLevelPushInternal- :: (MonadMultiState LayoutState m)- => Int- -> m ()-layoutIndentLevelPushInternal i = do- traceLocal ("layoutIndentLevelPushInternal", i)- mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s- , _lstate_indLevels = i : _lstate_indLevels s- }--layoutIndentLevelPopInternal- :: (MonadMultiState LayoutState m) => m ()-layoutIndentLevelPopInternal = do- traceLocal ("layoutIndentLevelPopInternal")- mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s- , _lstate_indLevels = List.tail $ _lstate_indLevels s- }--layoutRemoveIndentLevelLinger :: ( MonadMultiState LayoutState m) => m ()-layoutRemoveIndentLevelLinger = do- mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s- }--layoutWithAddBaseCol- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- , MonadMultiReader Config m- )- => m ()- -> m ()-layoutWithAddBaseCol m = do- amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack- state <- mGet- layoutBaseYPushInternal $ lstate_baseY state + amount- m- layoutBaseYPopInternal--layoutWithAddBaseColBlock- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- , MonadMultiReader Config m- )- => m ()- -> m ()-layoutWithAddBaseColBlock m = do- amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack- state <- mGet- layoutBaseYPushInternal $ lstate_baseY state + amount- layoutWriteEnsureBlock- m- layoutBaseYPopInternal--layoutWithAddBaseColNBlock- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Int- -> m ()- -> m ()-layoutWithAddBaseColNBlock amount m = do- traceLocal ("layoutWithAddBaseColNBlock", amount)- state <- mGet- layoutBaseYPushInternal $ lstate_baseY state + amount- layoutWriteEnsureBlock- m- layoutBaseYPopInternal--layoutWriteEnsureBlock- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => m ()-layoutWriteEnsureBlock = do- traceLocal ("layoutWriteEnsureBlock")- state <- mGet- let- diff = case (_lstate_addSepSpace state, _lstate_curYOrAddNewline state) of- (Nothing, Left i ) -> lstate_baseY state - i- (Nothing, Right{}) -> lstate_baseY state- (Just sp, Left i ) -> max sp (lstate_baseY state - i)- (Just sp, Right{}) -> max sp (lstate_baseY state)- -- when (diff>0) $ layoutWriteNewlineBlock- when (diff > 0) $ do- mSet $ state { _lstate_addSepSpace = Just $ diff }--layoutWithAddBaseColN- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Int- -> m ()- -> m ()-layoutWithAddBaseColN amount m = do- state <- mGet- layoutBaseYPushInternal $ lstate_baseY state + amount- m- layoutBaseYPopInternal--layoutBaseYPushCur- :: (MonadMultiState LayoutState m) => m ()-layoutBaseYPushCur = do- traceLocal ("layoutBaseYPushCur")- state <- mGet- case _lstate_commentCol state of- Nothing ->- case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of- (Left i , Just j ) -> layoutBaseYPushInternal (i + j)- (Left i , Nothing) -> layoutBaseYPushInternal i- (Right{}, _ ) -> layoutBaseYPushInternal $ lstate_baseY state- Just cCol -> layoutBaseYPushInternal cCol--layoutBaseYPop- :: (MonadMultiState LayoutState m) => m ()-layoutBaseYPop = do- traceLocal ("layoutBaseYPop")- layoutBaseYPopInternal--layoutIndentLevelPushCur- :: (MonadMultiState LayoutState m) => m ()-layoutIndentLevelPushCur = do- traceLocal ("layoutIndentLevelPushCur")- state <- mGet- let y = case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of- (Left i , Just j ) -> i + j- (Left i , Nothing) -> i- (Right{}, Just j ) -> j- (Right{}, Nothing) -> 0- layoutIndentLevelPushInternal y--layoutIndentLevelPop- :: (MonadMultiState LayoutState m) => m ()-layoutIndentLevelPop = do- traceLocal ("layoutIndentLevelPop")- layoutIndentLevelPopInternal- -- why are comment indentations relative to the previous indentation on- -- the first node of an additional indentation, and relative to the outer- -- indentation after the last node of some indented stuff? sure does not- -- make sense.- layoutRemoveIndentLevelLinger--layoutAddSepSpace :: (MonadMultiState LayoutState m)- => m ()-layoutAddSepSpace = do- state <- mGet- mSet $ state- { _lstate_addSepSpace = Just $ fromMaybe 1 $ _lstate_addSepSpace state }---- TODO: when refactoring is complete, the other version of this method--- can probably be removed.-moveToExactAnn- :: ( MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- , MonadMultiReader (Map AnnKey Annotation) m- )- => AnnKey- -> m ()-moveToExactAnn annKey = do- traceLocal ("moveToExactAnn", annKey)- anns <- mAsk- case Map.lookup annKey anns of- Nothing -> return ()- Just ann -> do- -- curY <- mGet <&> _lstate_curY- let ExactPrint.DP (y, _x) = ExactPrint.annEntryDelta ann- -- mModify $ \state -> state { _lstate_addNewline = Just x }- moveToY y--moveToY :: MonadMultiState LayoutState m => Int -> m ()-moveToY y = mModify $ \state ->- let upd = case _lstate_curYOrAddNewline state of- Left i -> if y == 0 then Left i else Right y- Right i -> Right $ max y i- in state- { _lstate_curYOrAddNewline = upd- , _lstate_addSepSpace = if Data.Either.isRight upd- then- _lstate_commentCol state- <|> _lstate_addSepSpace state- <|> Just (lstate_baseY state)- else Nothing- , _lstate_commentCol = Nothing- }--- fixMoveToLineByIsNewline :: MonadMultiState--- LayoutState m => Int -> m Int--- fixMoveToLineByIsNewline x = do--- newLineState <- mGet <&> _lstate_isNewline--- return $ if newLineState == NewLineStateYes--- then x-1--- else x--ppmMoveToExactLoc- :: MonadMultiWriter Text.Builder.Builder m- => ExactPrint.DeltaPos- -> m ()-ppmMoveToExactLoc (ExactPrint.DP (x, y)) = do- replicateM_ x $ mTell $ Text.Builder.fromString "\n"- replicateM_ y $ mTell $ Text.Builder.fromString " "---- TODO: update and use, or clean up. Currently dead code.-layoutWritePriorComments- :: ( Data.Data.Data ast- , MonadMultiWriter Text.Builder.Builder m- , MonadMultiState LayoutState m- )- => Located ast- -> m ()-layoutWritePriorComments ast = do- mAnn <- do- state <- mGet- let key = ExactPrint.mkAnnKey ast- let anns = _lstate_comments state- let mAnn = ExactPrint.annPriorComments <$> Map.lookup key anns- mSet $ state- { _lstate_comments =- Map.adjust (\ann -> ann { ExactPrint.annPriorComments = [] }) key anns- }- return mAnn- case mAnn of- Nothing -> return ()- Just priors -> do- unless (null priors) $ layoutSetCommentCol- priors `forM_` \( ExactPrint.Comment comment _ _- , ExactPrint.DP (x, y)- ) -> do- replicateM_ x layoutWriteNewline- layoutWriteAppendSpaces y- layoutWriteAppendMultiline $ Text.lines $ Text.pack comment---- TODO: update and use, or clean up. Currently dead code.--- this currently only extracs from the `annsDP` field of Annotations.--- per documentation, this seems sufficient, as the--- "..`annFollowingComments` are only added by AST transformations ..".-layoutWritePostComments :: (Data.Data.Data ast,- MonadMultiWriter Text.Builder.Builder m,- MonadMultiState LayoutState m)- => Located ast -> m ()-layoutWritePostComments ast = do- mAnn <- do- state <- mGet- let key = ExactPrint.mkAnnKey ast- let anns = _lstate_comments state- let mAnn = ExactPrint.annFollowingComments <$> Map.lookup key anns- mSet $ state- { _lstate_comments =- Map.adjust (\ann -> ann { ExactPrint.annFollowingComments = [] })- key- anns- }- return mAnn- case mAnn of- Nothing -> return ()- Just posts -> do- unless (null posts) $ layoutSetCommentCol- posts `forM_` \( ExactPrint.Comment comment _ _- , ExactPrint.DP (x, y)- ) -> do- replicateM_ x layoutWriteNewline- layoutWriteAppend $ Text.pack $ replicate y ' '- mModify $ \s -> s { _lstate_addSepSpace = Nothing }- layoutWriteAppendMultiline $ Text.lines $ Text.pack $ comment--layoutIndentRestorePostComment- :: ( MonadMultiState LayoutState m- , MonadMultiWriter Text.Builder.Builder m- )- => m ()-layoutIndentRestorePostComment = do- state <- mGet- let mCommentCol = _lstate_commentCol state- let eCurYAddNL = _lstate_curYOrAddNewline state- mModify $ \s -> s { _lstate_commentCol = Nothing- , _lstate_commentNewlines = 0- }- case (mCommentCol, eCurYAddNL) of- (Just commentCol, Left{}) -> do- layoutWriteEnsureNewlineBlock- layoutWriteEnsureAbsoluteN $ commentCol + fromMaybe 0 (_lstate_addSepSpace state)- _ -> return ()---- layoutWritePriorCommentsRestore :: (Data.Data.Data ast,--- MonadMultiWriter Text.Builder.Builder m,--- MonadMultiState LayoutState m--- , MonadMultiWriter (Seq String) m)--- => Located ast -> m ()--- layoutWritePriorCommentsRestore x = do--- layoutWritePriorComments x--- layoutIndentRestorePostComment------ layoutWritePostCommentsRestore :: (Data.Data.Data ast,--- MonadMultiWriter Text.Builder.Builder m,--- MonadMultiState LayoutState m--- , MonadMultiWriter (Seq String) m)--- => Located ast -> m ()--- layoutWritePostCommentsRestore x = do--- layoutWritePostComments x--- layoutIndentRestorePostComment
− source/library/Language/Haskell/Brittany/Internal/Config.hs
@@ -1,286 +0,0 @@-{-# LANGUAGE MonadComprehensions #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Config where--import qualified Data.Bool as Bool-import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Char8-import Data.CZipWith-import Data.Coerce (coerce)-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Semigroup as Semigroup-import qualified Data.Yaml-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Config.Types.Instances ()-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Utils-import qualified System.Console.CmdArgs.Explicit as CmdArgs-import qualified System.Directory-import qualified System.Directory as Directory-import qualified System.FilePath.Posix as FilePath-import qualified System.IO-import UI.Butcher.Monadic---- brittany-next-binding { lconfig_indentPolicy: IndentPolicyLeft }-staticDefaultConfig :: Config-staticDefaultConfig = Config- { _conf_version = coerce (1 :: Int)- , _conf_debug = DebugConfig- { _dconf_dump_config = coerce False- , _dconf_dump_annotations = coerce False- , _dconf_dump_ast_unknown = coerce False- , _dconf_dump_ast_full = coerce False- , _dconf_dump_bridoc_raw = coerce False- , _dconf_dump_bridoc_simpl_alt = coerce False- , _dconf_dump_bridoc_simpl_floating = coerce False- , _dconf_dump_bridoc_simpl_par = coerce False- , _dconf_dump_bridoc_simpl_columns = coerce False- , _dconf_dump_bridoc_simpl_indent = coerce False- , _dconf_dump_bridoc_final = coerce False- , _dconf_roundtrip_exactprint_only = coerce False- }- , _conf_layout = LayoutConfig- { _lconfig_cols = coerce (80 :: Int)- , _lconfig_indentPolicy = coerce IndentPolicyFree- , _lconfig_indentAmount = coerce (2 :: Int)- , _lconfig_indentWhereSpecial = coerce True- , _lconfig_indentListSpecial = coerce True- , _lconfig_importColumn = coerce (50 :: Int)- , _lconfig_importAsColumn = coerce (50 :: Int)- , _lconfig_altChooser = coerce (AltChooserBoundedSearch 3)- , _lconfig_columnAlignMode = coerce (ColumnAlignModeMajority 0.7)- , _lconfig_alignmentLimit = coerce (30 :: Int)- , _lconfig_alignmentBreakOnMultiline = coerce True- , _lconfig_hangingTypeSignature = coerce False- , _lconfig_reformatModulePreamble = coerce True- , _lconfig_allowSingleLineExportList = coerce False- , _lconfig_allowHangingQuasiQuotes = coerce True- , _lconfig_experimentalSemicolonNewlines = coerce False- -- , _lconfig_allowSinglelineRecord = coerce False- }- , _conf_errorHandling = ErrorHandlingConfig- { _econf_produceOutputOnErrors = coerce False- , _econf_Werror = coerce False- , _econf_ExactPrintFallback = coerce ExactPrintFallbackModeInline- , _econf_omit_output_valid_check = coerce False- }- , _conf_preprocessor = PreProcessorConfig- { _ppconf_CPPMode = coerce CPPModeAbort- , _ppconf_hackAroundIncludes = coerce False- }- , _conf_forward = ForwardOptions { _options_ghc = Identity [] }- , _conf_roundtrip_exactprint_only = coerce False- , _conf_disable_formatting = coerce False- , _conf_obfuscate = coerce False- }--forwardOptionsSyntaxExtsEnabled :: ForwardOptions-forwardOptionsSyntaxExtsEnabled = ForwardOptions- { _options_ghc = Identity- [ "-XLambdaCase"- , "-XMultiWayIf"- , "-XGADTs"- , "-XPatternGuards"- , "-XViewPatterns"- , "-XTupleSections"- , "-XExplicitForAll"- , "-XImplicitParams"- , "-XQuasiQuotes"- , "-XTemplateHaskell"- , "-XBangPatterns"- , "-XTypeApplications"- ]- }---- brittany-next-binding { lconfig_indentPolicy: IndentPolicyLeft, lconfig_cols: 200 }-cmdlineConfigParser :: CmdParser Identity out (CConfig Maybe)-cmdlineConfigParser = do- -- TODO: why does the default not trigger; ind never should be []!!- ind <- addFlagReadParams "" ["indent"] "AMOUNT" (flagHelpStr "spaces per indentation level")- cols <- addFlagReadParams "" ["columns"] "AMOUNT" (flagHelpStr "target max columns (80 is an old default for this)")- importCol <- addFlagReadParams "" ["import-col"] "N" (flagHelpStr "column to align import lists at")- importAsCol <- addFlagReadParams "" ["import-as-col"] "N" (flagHelpStr "column to qualified-as module names at")-- dumpConfig <- addSimpleBoolFlag "" ["dump-config"] (flagHelp $ parDoc "dump the programs full config (merged commandline + file + defaults)")- dumpAnnotations <- addSimpleBoolFlag "" ["dump-annotations"] (flagHelp $ parDoc "dump the full annotations returned by ghc-exactprint")- dumpUnknownAST <- addSimpleBoolFlag "" ["dump-ast-unknown"] (flagHelp $ parDoc "dump the ast for any nodes not transformed, but copied as-is by brittany")- dumpCompleteAST <- addSimpleBoolFlag "" ["dump-ast-full"] (flagHelp $ parDoc "dump the full ast")- dumpBriDocRaw <- addSimpleBoolFlag "" ["dump-bridoc-raw"] (flagHelp $ parDoc "dump the pre-transformation bridoc")- dumpBriDocAlt <- addSimpleBoolFlag "" ["dump-bridoc-alt"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: alt")- dumpBriDocPar <- addSimpleBoolFlag "" ["dump-bridoc-par"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: par")- dumpBriDocFloating <- addSimpleBoolFlag "" ["dump-bridoc-floating"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: floating")- dumpBriDocColumns <- addSimpleBoolFlag "" ["dump-bridoc-columns"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: columns")- dumpBriDocIndent <- addSimpleBoolFlag "" ["dump-bridoc-indent"] (flagHelp $ parDoc "dump the partially transformed bridoc: after transformation: indent")- dumpBriDocFinal <- addSimpleBoolFlag "" ["dump-bridoc-final"] (flagHelp $ parDoc "dump the post-transformation bridoc")-- outputOnErrors <- addSimpleBoolFlag "" ["output-on-errors"] (flagHelp $ parDoc "even when there are errors, produce output (or try to to the degree possible)")- wError <- addSimpleBoolFlag "" ["werror"] (flagHelp $ parDoc "treat warnings as errors")- omitValidCheck <- addSimpleBoolFlag "" ["omit-output-check"] (flagHelp $ parDoc "omit checking if the output is syntactically valid (debugging)")-- roundtripOnly <- addSimpleBoolFlag "" ["exactprint-only"] (flagHelp $ parDoc "do not reformat, but exclusively use exactprint to roundtrip (debugging)")-- optionsGhc <- addFlagStringParams "" ["ghc-options"] "STRING" (flagHelp $ parDoc "allows to define default language extensions. The parameter is forwarded to ghc.")- disableFormatting <- addSimpleBoolFlag "" ["disable-formatting"] (flagHelp $ parDoc "parse, but don't transform the input at all. Useful for inline config for specific modules.")- obfuscate <- addSimpleBoolFlag "" ["obfuscate"] (flagHelp $ parDoc "apply obfuscator to the output.")-- return $ Config- { _conf_version = mempty- , _conf_debug = DebugConfig- { _dconf_dump_config = wrapLast $ falseToNothing dumpConfig- , _dconf_dump_annotations = wrapLast $ falseToNothing dumpAnnotations- , _dconf_dump_ast_unknown = wrapLast $ falseToNothing dumpUnknownAST- , _dconf_dump_ast_full = wrapLast $ falseToNothing dumpCompleteAST- , _dconf_dump_bridoc_raw = wrapLast $ falseToNothing dumpBriDocRaw- , _dconf_dump_bridoc_simpl_alt = wrapLast $ falseToNothing dumpBriDocAlt- , _dconf_dump_bridoc_simpl_par = wrapLast $ falseToNothing dumpBriDocPar- , _dconf_dump_bridoc_simpl_floating = wrapLast $ falseToNothing dumpBriDocFloating- , _dconf_dump_bridoc_simpl_columns = wrapLast $ falseToNothing dumpBriDocColumns- , _dconf_dump_bridoc_simpl_indent = wrapLast $ falseToNothing dumpBriDocIndent- , _dconf_dump_bridoc_final = wrapLast $ falseToNothing dumpBriDocFinal- , _dconf_roundtrip_exactprint_only = mempty- }- , _conf_layout = LayoutConfig- { _lconfig_cols = optionConcat cols- , _lconfig_indentPolicy = mempty- , _lconfig_indentAmount = optionConcat ind- , _lconfig_indentWhereSpecial = mempty -- falseToNothing _- , _lconfig_indentListSpecial = mempty -- falseToNothing _- , _lconfig_importColumn = optionConcat importCol- , _lconfig_importAsColumn = optionConcat importAsCol- , _lconfig_altChooser = mempty- , _lconfig_columnAlignMode = mempty- , _lconfig_alignmentLimit = mempty- , _lconfig_alignmentBreakOnMultiline = mempty- , _lconfig_hangingTypeSignature = mempty- , _lconfig_reformatModulePreamble = mempty- , _lconfig_allowSingleLineExportList = mempty- , _lconfig_allowHangingQuasiQuotes = mempty- , _lconfig_experimentalSemicolonNewlines = mempty- -- , _lconfig_allowSinglelineRecord = mempty- }- , _conf_errorHandling = ErrorHandlingConfig- { _econf_produceOutputOnErrors = wrapLast $ falseToNothing outputOnErrors- , _econf_Werror = wrapLast $ falseToNothing wError- , _econf_ExactPrintFallback = mempty- , _econf_omit_output_valid_check = wrapLast $ falseToNothing omitValidCheck- }- , _conf_preprocessor = PreProcessorConfig { _ppconf_CPPMode = mempty, _ppconf_hackAroundIncludes = mempty }- , _conf_forward = ForwardOptions { _options_ghc = [ optionsGhc & List.unwords & CmdArgs.splitArgs | not $ null optionsGhc ] }- , _conf_roundtrip_exactprint_only = wrapLast $ falseToNothing roundtripOnly- , _conf_disable_formatting = wrapLast $ falseToNothing disableFormatting- , _conf_obfuscate = wrapLast $ falseToNothing obfuscate- }- where- falseToNothing = Bool.bool Nothing (Just True)- wrapLast :: Maybe a -> Maybe (Semigroup.Last a)- wrapLast = fmap Semigroup.Last- optionConcat :: (Semigroup.Semigroup (f a), Applicative f) => [a] -> Maybe (f a)- optionConcat = mconcat . fmap (pure . pure)---- configParser :: Parser Config--- configParser = Config--- <$> option (eitherReader $ maybe (Left "required <int>!") Right . readMaybe)--- (long "indent" <> value 2 <> metavar "AMOUNT" <> help "spaces per indentation level")--- <*> (Bar--- <$> switch (long "bara" <> help "bara help")--- <*> switch (long "barb")--- <*> flag 3 5 (long "barc")--- )------ configParserInfo :: ParserInfo Config--- configParserInfo = ParserInfo--- { infoParser = configParser--- , infoFullDesc = True--- , infoProgDesc = return $ PP.text "a haskell code formatting utility based on ghc-exactprint"--- , infoHeader = return $ PP.text "brittany"--- , infoFooter = empty--- , infoFailureCode = (-55)--- , infoIntersperse = True--- }----- | 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 Maybe))-readConfig path = do- -- TODO: probably should catch IOErrors and then omit the existence check.- exists <- liftIO $ System.Directory.doesFileExist path- 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 (Data.Yaml.prettyPrintParseException e)- mzero- Right x -> return x- return $ Just fileConf- else return $ Nothing---- | Looks for a user-global config file and return its path.--- If there is no global config in a system, one will be created.-userConfigPath :: IO System.IO.FilePath-userConfigPath = do- userBritPathSimple <- Directory.getAppUserDataDirectory "brittany"- userBritPathXdg <- Directory.getXdgDirectory Directory.XdgConfig "brittany"- let searchDirs = [userBritPathSimple, userBritPathXdg]- globalConfig <- Directory.findFileWith Directory.doesFileExist- searchDirs- "config.yaml"- maybe (writeUserConfig userBritPathXdg) pure globalConfig- where- writeUserConfig dir = do- let createConfPath = dir FilePath.</> "config.yaml"- liftIO $ Directory.createDirectoryIfMissing True dir- writeDefaultConfig $ createConfPath- pure createConfPath---- | Searches for a local (per-project) brittany config starting from a given directory-findLocalConfigPath :: System.IO.FilePath -> IO (Maybe System.IO.FilePath)-findLocalConfigPath dir = do- let dirParts = FilePath.splitDirectories dir- -- when provided dir is "a/b/c", searchDirs is ["a/b/c", "a/b", "a", "/"]- let searchDirs = FilePath.joinPath <$> reverse (List.inits dirParts)- Directory.findFileWith Directory.doesFileExist searchDirs "brittany.yaml"---- | Reads specified configs.-readConfigs- :: CConfig Maybe -- ^ Explicit options, take highest priority- -> [System.IO.FilePath] -- ^ List of config files to load and merge, highest priority first- -> MaybeT IO Config-readConfigs cmdlineConfig configPaths = do- configs <- readConfig `mapM` configPaths- let merged = Semigroup.sconcat- $ NonEmpty.reverse (cmdlineConfig :| catMaybes configs)- return $ cZipWith fromOptionIdentity staticDefaultConfig merged---- | Reads provided configs--- but also applies the user default configuration (with lowest priority)-readConfigsWithUserConfig- :: CConfig Maybe -- ^ Explicit options, take highest priority- -> [System.IO.FilePath] -- ^ List of config files to load and merge, highest priority first- -> MaybeT IO Config-readConfigsWithUserConfig cmdlineConfig configPaths = do- defaultPath <- liftIO $ userConfigPath- readConfigs cmdlineConfig (configPaths ++ [defaultPath])--writeDefaultConfig :: MonadIO m => System.IO.FilePath -> m ()-writeDefaultConfig path =- liftIO $ ByteString.writeFile path $ Data.Yaml.encode $ cMap- (Just . runIdentity)- staticDefaultConfig--showConfigYaml :: Config -> String-showConfigYaml = Data.ByteString.Char8.unpack . Data.Yaml.encode . cMap- (\(Identity x) -> Just x)
− source/library/Language/Haskell/Brittany/Internal/Config/Types.hs
@@ -1,339 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TemplateHaskell #-}--module Language.Haskell.Brittany.Internal.Config.Types where--import Data.CZipWith-import Data.Coerce (Coercible, coerce)-import Data.Data (Data)-import qualified Data.Semigroup as Semigroup-import Data.Semigroup (Last)-import Data.Semigroup.Generic-import GHC.Generics-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils ()----confUnpack :: Coercible a b => Identity a -> b-confUnpack (Identity x) = coerce x--data CDebugConfig f = DebugConfig- { _dconf_dump_config :: f (Semigroup.Last Bool)- , _dconf_dump_annotations :: f (Semigroup.Last Bool)- , _dconf_dump_ast_unknown :: f (Semigroup.Last Bool)- , _dconf_dump_ast_full :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_raw :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_simpl_alt :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_simpl_floating :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_simpl_par :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_simpl_columns :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_simpl_indent :: f (Semigroup.Last Bool)- , _dconf_dump_bridoc_final :: f (Semigroup.Last Bool)- , _dconf_roundtrip_exactprint_only :: f (Semigroup.Last Bool)- }- deriving (Generic)--data CLayoutConfig f = LayoutConfig- { _lconfig_cols :: f (Last Int) -- the thing that has default 80.- , _lconfig_indentPolicy :: f (Last IndentPolicy)- , _lconfig_indentAmount :: f (Last Int)- , _lconfig_indentWhereSpecial :: f (Last Bool) -- indent where only 1 sometimes (TODO).- , _lconfig_indentListSpecial :: f (Last Bool) -- use some special indentation for ","- -- when creating zero-indentation- -- multi-line list literals.- , _lconfig_importColumn :: f (Last Int)- -- ^ for import statement layouting, column at which to align the- -- elements to be imported from a module.- -- It is expected that importAsColumn >= importCol.- , _lconfig_importAsColumn :: f (Last Int)- -- ^ for import statement layouting, column at which put the module's- -- "as" name (which also affects the positioning of the "as" keyword).- -- It is expected that importAsColumn >= importCol.- , _lconfig_altChooser :: f (Last AltChooser)- , _lconfig_columnAlignMode :: f (Last ColumnAlignMode)- , _lconfig_alignmentLimit :: f (Last Int)- -- roughly speaking, this sets an upper bound to the number of spaces- -- inserted to create horizontal alignment.- -- More specifically, if 'xs' are the widths of the columns in some- -- alignment-block, then the block will be aligned with the width- -- maximum [ x | x <- xs, x < minimum xs + alignmentLimit ].- , _lconfig_alignmentBreakOnMultiline :: f (Last Bool)- -- stops alignment between items that are not layouted as a single line.- -- e.g. for single-line alignment, things remain unchanged:- -- do- -- short <- stuff- -- loooooooong <- stuff- -- but not in cases such as:- -- do- -- short <- some more stuff- -- that requires two lines- -- loooooooong <- stuff- , _lconfig_hangingTypeSignature :: f (Last Bool)- -- Do not put "::" in a new line, and use hanging indentation for the- -- signature, i.e.:- -- func :: SomeLongStuff- -- -> SomeLongStuff- -- instead of the usual- -- func- -- :: SomeLongStuff- -- -> SomeLongStuff- -- As usual for hanging indentation, the result will be- -- context-sensitive (in the function name).- , _lconfig_reformatModulePreamble :: f (Last Bool)- -- whether the module preamble/header (module keyword, name, export list,- -- import statements) are reformatted. If false, only the elements of the- -- module (everything past the "where") are reformatted.- , _lconfig_allowSingleLineExportList :: f (Last Bool)- -- if true, and it fits in a single line, and there are no comments in the- -- export list, the following layout will be used:- -- > module MyModule (abc, def) where- -- > [stuff]- -- otherwise, the multi-line version is used:- -- > module MyModule- -- > ( abc- -- > , def- -- > )- -- > where- , _lconfig_allowHangingQuasiQuotes :: f (Last Bool)- -- if false, the layouter sees any splices as infinitely big and places- -- them accordingly (in newlines, most likely); This also influences- -- parent nodes.- -- if true, the layouter is free to start a quasi-quotation at the end- -- of a line.- --- -- false:- -- > let- -- > body =- -- > [json|- -- > hello- -- > |]- --- -- true:- -- > let body = [json|- -- > hello- -- > |]- , _lconfig_experimentalSemicolonNewlines :: f (Last Bool)- -- enables an experimental feature to turn semicolons in brace notation- -- into newlines when using layout:- --- -- > do { a ;; b }- --- -- turns into- -- > do- -- > a- -- >- -- > b- --- -- The implementation for this is a bit hacky and not tested; it might- -- break output syntax or not work properly for every kind of brace. So- -- far I have considered `do` and `case-of`.- -- , _lconfig_allowSinglelineRecord :: f (Last Bool)- -- -- if true, layouts record data decls as a single line when possible, e.g.- -- -- > MyPoint { x :: Double, y :: Double }- -- -- if false, always use the multi-line layout- -- -- > MyPoint- -- -- > { x :: Double- -- -- > , y :: Double- -- -- > }- }- deriving (Generic)--data CForwardOptions f = ForwardOptions- { _options_ghc :: f [String]- }- deriving (Generic)--data CErrorHandlingConfig f = ErrorHandlingConfig- { _econf_produceOutputOnErrors :: f (Semigroup.Last Bool)- , _econf_Werror :: f (Semigroup.Last Bool)- , _econf_ExactPrintFallback :: f (Semigroup.Last ExactPrintFallbackMode)- -- ^ Determines when to fall back on the exactprint'ed output when- -- syntactical constructs are encountered which are not yet handled by- -- brittany.- -- Note that the "risky" setting is risky because even with the check of- -- the syntactic validity of the brittany output, at least in theory there- -- may be cases where the output is syntactically/semantically valid but- -- has different semantics than the code pre-transformation.- , _econf_omit_output_valid_check :: f (Semigroup.Last Bool)- }- deriving (Generic)--data CPreProcessorConfig f = PreProcessorConfig- { _ppconf_CPPMode :: f (Semigroup.Last CPPMode)- , _ppconf_hackAroundIncludes :: f (Semigroup.Last Bool)- }- deriving (Generic)--data CConfig f = Config- { _conf_version :: f (Semigroup.Last Int)- , _conf_debug :: CDebugConfig f- , _conf_layout :: CLayoutConfig f- , _conf_errorHandling :: CErrorHandlingConfig f- , _conf_forward :: CForwardOptions f- , _conf_preprocessor :: CPreProcessorConfig f- , _conf_roundtrip_exactprint_only :: f (Semigroup.Last Bool)- -- ^ this field is somewhat of a duplicate of the one in DebugConfig.- -- It is used for per-declaration disabling by the inline config- -- implementation. Could have re-used the existing field, but felt risky- -- to use a "debug" labeled field for non-debug functionality.- , _conf_disable_formatting :: f (Semigroup.Last Bool)- -- ^ Used for inline config that disables brittany entirely for this- -- module. Useful for wildcard application- -- (`find -name "*.hs" | xargs brittany --write-mode inplace` or something- -- in that direction).- , _conf_obfuscate :: f (Semigroup.Last Bool)-- }- deriving (Generic)--type DebugConfig = CDebugConfig Identity-type LayoutConfig = CLayoutConfig Identity-type ForwardOptions = CForwardOptions Identity-type ErrorHandlingConfig = CErrorHandlingConfig Identity-type Config = CConfig Identity---- i wonder if any Show1 stuff could be leveraged.-deriving instance Show (CDebugConfig Identity)-deriving instance Show (CLayoutConfig Identity)-deriving instance Show (CErrorHandlingConfig Identity)-deriving instance Show (CForwardOptions Identity)-deriving instance Show (CPreProcessorConfig Identity)-deriving instance Show (CConfig Identity)--deriving instance Show (CDebugConfig Maybe)-deriving instance Show (CLayoutConfig Maybe)-deriving instance Show (CErrorHandlingConfig Maybe)-deriving instance Show (CForwardOptions Maybe)-deriving instance Show (CPreProcessorConfig Maybe)-deriving instance Show (CConfig Maybe)--deriving instance Data (CDebugConfig Identity)-deriving instance Data (CLayoutConfig Identity)-deriving instance Data (CErrorHandlingConfig Identity)-deriving instance Data (CForwardOptions Identity)-deriving instance Data (CPreProcessorConfig Identity)-deriving instance Data (CConfig Identity)--deriving instance Data (CDebugConfig Maybe)-deriving instance Data (CLayoutConfig Maybe)-deriving instance Data (CErrorHandlingConfig Maybe)-deriving instance Data (CForwardOptions Maybe)-deriving instance Data (CPreProcessorConfig Maybe)-deriving instance Data (CConfig Maybe)--instance Semigroup.Semigroup (CDebugConfig Maybe) where- (<>) = gmappend-instance Semigroup.Semigroup (CLayoutConfig Maybe) where- (<>) = gmappend-instance Semigroup.Semigroup (CErrorHandlingConfig Maybe) where- (<>) = gmappend-instance Semigroup.Semigroup (CForwardOptions Maybe) where- (<>) = gmappend-instance Semigroup.Semigroup (CPreProcessorConfig Maybe) where- (<>) = gmappend-instance Semigroup.Semigroup (CConfig Maybe) where- (<>) = gmappend--instance Semigroup.Semigroup (CDebugConfig Identity) where- (<>) = gmappend-instance Semigroup.Semigroup (CLayoutConfig Identity) where- (<>) = gmappend-instance Semigroup.Semigroup (CErrorHandlingConfig Identity) where- (<>) = gmappend-instance Semigroup.Semigroup (CForwardOptions Identity) where- (<>) = gmappend-instance Semigroup.Semigroup (CPreProcessorConfig Identity) where- (<>) = gmappend-instance Semigroup.Semigroup (CConfig Identity) where- (<>) = gmappend--instance Monoid (CDebugConfig Maybe) where- mempty = gmempty-instance Monoid (CLayoutConfig Maybe) where- mempty = gmempty-instance Monoid (CErrorHandlingConfig Maybe) where- mempty = gmempty-instance Monoid (CForwardOptions Maybe) where- mempty = gmempty-instance Monoid (CPreProcessorConfig Maybe) where- mempty = gmempty-instance Monoid (CConfig Maybe) where- mempty = gmempty---data IndentPolicy = IndentPolicyLeft -- never create a new indentation at more- -- than old indentation + amount- | IndentPolicyFree -- can create new indentations whereever- | IndentPolicyMultiple -- can create indentations only- -- at any n * amount.- deriving (Eq, Show, Generic, Data)--data AltChooser = AltChooserSimpleQuick -- always choose last alternative.- -- leads to tons of sparsely filled- -- lines.- | AltChooserShallowBest -- choose the first matching alternative- -- using the simplest spacing- -- information for the children.- | AltChooserBoundedSearch Int- -- choose the first matching alternative- -- using a bounded list of recursive- -- options having sufficient space.- deriving (Show, Generic, Data)--data ColumnAlignMode- = ColumnAlignModeDisabled- -- ^ Make no column alignments whatsoever- | ColumnAlignModeUnanimously- -- ^ Make column alignments only if it does not cause overflow for any of- -- the affected lines.- | ColumnAlignModeMajority Float- -- ^ If at least (ratio::Float) of the aligned elements have sufficient- -- space for the alignment, act like ColumnAlignModeAnimously; otherwise- -- act like ColumnAlignModeDisabled.- | ColumnAlignModeAnimouslyScale Int- -- ^ Scale back columns to some degree if their sum leads to overflow.- -- This is done in a linear fashion.- -- The Int specifies additional columns to be added to column maximum for- -- scaling calculation purposes.- | ColumnAlignModeAnimously- -- ^ Decide on a case-by-case basis if alignment would cause overflow.- -- If it does, cancel all alignments for this (nested) column description.- -- ColumnAlignModeAnimouslySome -- potentially to implement- | ColumnAlignModeAlways- -- ^ Always respect column alignments, even if it makes stuff overflow.- deriving (Show, Generic, Data)--data CPPMode = CPPModeAbort -- abort program on seeing -XCPP- | CPPModeWarn -- warn about CPP and non-roundtripping in its- -- presence.- | CPPModeNowarn -- silently allow CPP, if possible (i.e. input is- -- file.)- deriving (Show, Generic, Data)--data ExactPrintFallbackMode- = ExactPrintFallbackModeNever -- never fall back on exactprinting- | ExactPrintFallbackModeInline -- fall back only if there are no newlines in- -- the exactprint'ed output.- | ExactPrintFallbackModeRisky -- fall back even in the presence of newlines.- -- THIS MAY THEORETICALLY CHANGE SEMANTICS OF- -- A PROGRAM BY TRANSFORMING IT.- deriving (Show, Generic, Data)--deriveCZipWith ''CDebugConfig-deriveCZipWith ''CLayoutConfig-deriveCZipWith ''CErrorHandlingConfig-deriveCZipWith ''CForwardOptions-deriveCZipWith ''CPreProcessorConfig-deriveCZipWith ''CConfig--instance CFunctor CDebugConfig-instance CFunctor CLayoutConfig-instance CFunctor CErrorHandlingConfig-instance CFunctor CForwardOptions-instance CFunctor CPreProcessorConfig-instance CFunctor CConfig
− source/library/Language/Haskell/Brittany/Internal/Config/Types/Instances.hs
@@ -1,121 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}--- These optimizations are disabled to improve compile times (and compilation--- memory usage). When we do not disable them, the CI servers take more than--- 10 minutes to compile this module alone.--- Having looked into aeson and how the instances are written, I still do--- not understand what makes GHC choke so much here. The size of the raw--- expressions below looks fairly negligible, so there must be some expansion--- due to inlining going on. But even disabling INLINE pragmas in aeson did--- not seem to change anything.--- Nonetheless, this solution works and has no downsides because the--- instances defined here are not in any way performance-critical.-{-# OPTIONS_GHC -fno-pre-inlining #-}-{-# OPTIONS_GHC -fno-specialise #-}-{-# OPTIONS_GHC -fignore-interface-pragmas #-}--{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Config.Types.Instances where--import qualified Data.Aeson.Key as Key-import qualified Data.Aeson.Types as Aeson-import Data.Yaml-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Prelude----aesonDecodeOptionsBrittany :: Aeson.Options-aesonDecodeOptionsBrittany = Aeson.defaultOptions- { Aeson.omitNothingFields = True- , Aeson.fieldLabelModifier = dropWhile (=='_')- }--instance FromJSON (CDebugConfig Maybe) where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON (CDebugConfig Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany--instance FromJSON IndentPolicy where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON IndentPolicy where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany- toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany--instance FromJSON AltChooser where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON AltChooser where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany- toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany--instance FromJSON ColumnAlignMode where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON ColumnAlignMode where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany- toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany--instance FromJSON CPPMode where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON CPPMode where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany- toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany--instance FromJSON ExactPrintFallbackMode where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON ExactPrintFallbackMode where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany- toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany--instance FromJSON (CLayoutConfig Maybe) where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON (CLayoutConfig Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany--instance FromJSON (CErrorHandlingConfig Maybe) where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON (CErrorHandlingConfig Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany--instance FromJSON (CForwardOptions Maybe) where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON (CForwardOptions Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany--instance FromJSON (CPreProcessorConfig Maybe) where- parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany--instance ToJSON (CPreProcessorConfig Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany--instance ToJSON (CConfig Maybe) where- toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany---- This custom instance ensures the "omitNothingFields" behaviour not only for--- leafs, but for nodes of the config as well. This way e.g. "{}" is valid--- config file content.-instance FromJSON (CConfig Maybe) where- parseJSON (Object v) = Config- <$> v .:? Key.fromString "conf_version"- <*> v .:?= Key.fromString "conf_debug"- <*> v .:?= Key.fromString "conf_layout"- <*> v .:?= Key.fromString "conf_errorHandling"- <*> v .:?= Key.fromString "conf_forward"- <*> v .:?= Key.fromString "conf_preprocessor"- <*> v .:? Key.fromString "conf_roundtrip_exactprint_only"- <*> v .:? Key.fromString "conf_disable_formatting"- <*> v .:? Key.fromString "conf_obfuscate"- parseJSON invalid = Aeson.typeMismatch "Config" invalid---- Pretends that the value is {} when the key is not present.-(.:?=) :: FromJSON a => Object -> Key.Key -> Parser a-o .:?= k = o .:? k >>= maybe (parseJSON (Aeson.object [])) pure
− source/library/Language/Haskell/Brittany/Internal/ExactPrintUtils.hs
@@ -1,247 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MonadComprehensions #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal.ExactPrintUtils where--import qualified Control.Monad.State.Class as State.Class-import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS-import Data.Data-import qualified Data.Foldable as Foldable-import qualified Data.Generics as SYB-import Data.HList.HList-import qualified Data.Map as Map-import qualified Data.Maybe-import qualified Data.Sequence as Seq-import qualified Data.Set as Set-import GHC (GenLocated(L))-import qualified GHC hiding (parseModule)-import qualified GHC.Driver.CmdLine as GHC-import GHC.Hs-import qualified GHC.Types.SrcLoc as GHC-import GHC.Types.SrcLoc (Located, SrcSpan)-import Language.Haskell.Brittany.Internal.Config.Types-import qualified Language.Haskell.Brittany.Internal.ParseModule as ParseModule-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint-import qualified System.IO----parseModule- :: [String]- -> System.IO.FilePath- -> (GHC.DynFlags -> IO (Either String a))- -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))-parseModule args fp dynCheck = do- str <- System.IO.readFile fp- parseModuleFromString args fp dynCheck str--parseModuleFromString- :: [String]- -> System.IO.FilePath- -> (GHC.DynFlags -> IO (Either String a))- -> String- -> IO (Either String (ExactPrint.Anns, GHC.ParsedSource, a))-parseModuleFromString = ParseModule.parseModule---commentAnnFixTransformGlob :: SYB.Data ast => ast -> ExactPrint.Transform ()-commentAnnFixTransformGlob ast = do- let extract :: forall a . SYB.Data a => a -> Seq (SrcSpan, ExactPrint.AnnKey)- extract = -- traceFunctionWith "extract" (show . SYB.typeOf) show $- const Seq.empty- `SYB.ext1Q`- (\l@(L span _) -> Seq.singleton (span, ExactPrint.mkAnnKey l))- let nodes = SYB.everything (<>) extract ast- let annsMap :: Map GHC.RealSrcLoc ExactPrint.AnnKey- annsMap = Map.fromListWith- (const id)- [ (GHC.realSrcSpanEnd span, annKey)- | (GHC.RealSrcSpan span _, annKey) <- Foldable.toList nodes- ]- nodes `forM_` (snd .> processComs annsMap)- where- processComs annsMap annKey1 = do- mAnn <- State.Class.gets fst <&> Map.lookup annKey1- mAnn `forM_` \ann1 -> do- let priors = ExactPrint.annPriorComments ann1- follows = ExactPrint.annFollowingComments ann1- assocs = ExactPrint.annsDP ann1- let- processCom- :: (ExactPrint.Comment, ExactPrint.DeltaPos)- -> ExactPrint.TransformT Identity Bool- processCom comPair@(com, _) =- case GHC.realSrcSpanStart $ ExactPrint.commentIdentifier com of- comLoc -> case Map.lookupLE comLoc annsMap of- Just (_, annKey2) | loc1 /= loc2 -> case (con1, con2) of- (ExactPrint.CN "RecordCon", ExactPrint.CN "HsRecField") ->- move $> False- (x, y) | x == y -> move $> False- _ -> return True- where- ExactPrint.AnnKey annKeyLoc1 con1 = annKey1- ExactPrint.AnnKey annKeyLoc2 con2 = annKey2- loc1 = GHC.realSrcSpanStart annKeyLoc1- loc2 = GHC.realSrcSpanStart annKeyLoc2- move = ExactPrint.modifyAnnsT $ \anns ->- let- ann2 = Data.Maybe.fromJust $ Map.lookup annKey2 anns- ann2' = ann2- { ExactPrint.annFollowingComments =- ExactPrint.annFollowingComments ann2 ++ [comPair]- }- in- Map.insert annKey2 ann2' anns- _ -> return True -- retain comment at current node.- priors' <- filterM processCom priors- follows' <- filterM processCom follows- assocs' <- flip filterM assocs $ \case- (ExactPrint.AnnComment com, dp) -> processCom (com, dp)- _ -> return True- let ann1' = ann1 { ExactPrint.annPriorComments = priors'- , ExactPrint.annFollowingComments = follows'- , ExactPrint.annsDP = assocs'- }- ExactPrint.modifyAnnsT $ \anns -> Map.insert annKey1 ann1' anns----- TODO: this is unused by now, but it contains one detail that--- commentAnnFixTransformGlob does not include: Moving of comments for--- "RecordUpd"s.--- commentAnnFixTransform :: GHC.ParsedSource -> ExactPrint.Transform ()--- commentAnnFixTransform modul = SYB.everything (>>) genF modul--- where--- genF :: Data.Data.Data a => a -> ExactPrint.Transform ()--- genF = (\_ -> return ()) `SYB.extQ` exprF--- exprF :: Located (HsExpr GhcPs) -> ExactPrint.Transform ()--- exprF lexpr@(L _ expr) = case expr of--- #if MIN_VERSION_ghc(8,6,0) /* ghc-8.6 */--- RecordCon _ _ (HsRecFields fs@(_:_) Nothing) ->--- #else--- RecordCon _ _ _ (HsRecFields fs@(_:_) Nothing) ->--- #endif--- moveTrailingComments lexpr (List.last fs)--- #if MIN_VERSION_ghc(8,6,0) /* ghc-8.6 */--- RecordUpd _ _e fs@(_:_) ->--- #else--- RecordUpd _e fs@(_:_) _cons _ _ _ ->--- #endif--- moveTrailingComments lexpr (List.last fs)--- _ -> return ()---- commentAnnFixTransform :: GHC.ParsedSource -> ExactPrint.Transform ()--- commentAnnFixTransform modul = SYB.everything (>>) genF modul--- where--- genF :: Data.Data.Data a => a -> ExactPrint.Transform ()--- genF = (\_ -> return ()) `SYB.extQ` exprF--- exprF :: Located (HsExpr GhcPs) -> ExactPrint.Transform ()--- exprF lexpr@(L _ expr) = case expr of--- RecordCon _ _ (HsRecFields fs@(_:_) Nothing) ->--- moveTrailingComments lexpr (List.last fs)--- RecordUpd _ _e fs@(_:_) ->--- moveTrailingComments lexpr (List.last fs)--- _ -> return ()---- moveTrailingComments :: (Data.Data.Data a,Data.Data.Data b)--- => GHC.Located a -> GHC.Located b -> ExactPrint.Transform ()--- moveTrailingComments astFrom astTo = do--- let--- k1 = ExactPrint.mkAnnKey astFrom--- k2 = ExactPrint.mkAnnKey astTo--- moveComments ans = ans'--- where--- an1 = Data.Maybe.fromJust $ Map.lookup k1 ans--- an2 = Data.Maybe.fromJust $ Map.lookup k2 ans--- cs1f = ExactPrint.annFollowingComments an1--- cs2f = ExactPrint.annFollowingComments an2--- (comments, nonComments) = flip breakEither (ExactPrint.annsDP an1)--- $ \case--- (ExactPrint.AnnComment com, dp) -> Left (com, dp)--- x -> Right x--- an1' = an1--- { ExactPrint.annsDP = nonComments--- , ExactPrint.annFollowingComments = []--- }--- an2' = an2--- { ExactPrint.annFollowingComments = cs1f ++ cs2f ++ comments--- }--- ans' = Map.insert k1 an1' $ Map.insert k2 an2' ans---- ExactPrint.modifyAnnsT moveComments---- | split a set of annotations in a module into a map from top-level module--- elements to the relevant annotations. Avoids quadratic behaviour a trivial--- implementation would have.-extractToplevelAnns- :: Located HsModule- -> ExactPrint.Anns- -> Map ExactPrint.AnnKey ExactPrint.Anns-extractToplevelAnns lmod anns = output- where- (L _ (HsModule _ _ _ _ ldecls _ _)) = lmod- declMap1 :: Map ExactPrint.AnnKey ExactPrint.AnnKey- declMap1 = Map.unions $ ldecls <&> \ldecl ->- Map.fromSet (const (ExactPrint.mkAnnKey ldecl)) (foldedAnnKeys ldecl)- declMap2 :: Map ExactPrint.AnnKey ExactPrint.AnnKey- declMap2 =- Map.fromList- $ [ (captured, declMap1 Map.! k)- | (k, ExactPrint.Ann _ _ _ _ _ (Just captured)) <- Map.toList anns- ]- declMap = declMap1 `Map.union` declMap2- modKey = ExactPrint.mkAnnKey lmod- output = groupMap (\k _ -> Map.findWithDefault modKey k declMap) anns--groupMap :: (Ord k, Ord l) => (k -> a -> l) -> Map k a -> Map l (Map k a)-groupMap f = Map.foldlWithKey' (\m k a -> Map.alter (insert k a) (f k a) m)- Map.empty- where- insert k a Nothing = Just (Map.singleton k a)- insert k a (Just m) = Just (Map.insert k a m)--foldedAnnKeys :: Data.Data.Data ast => ast -> Set ExactPrint.AnnKey-foldedAnnKeys ast = SYB.everything- Set.union- ( \x -> maybe- Set.empty- Set.singleton- [ SYB.gmapQi 1 (ExactPrint.mkAnnKey . L l) x- | locTyCon == SYB.typeRepTyCon (SYB.typeOf x)- , l :: SrcSpan <- SYB.gmapQi 0 SYB.cast x- -- for some reason, ghc-8.8 has forgotten how to infer the type of l,- -- even though it is passed to mkAnnKey above, which only accepts- -- SrcSpan.- ]- )- ast- where locTyCon = SYB.typeRepTyCon (SYB.typeOf (L () ()))---withTransformedAnns- :: Data ast- => ast- -> MultiRWSS.MultiRWS '[Config, ExactPrint.Anns] w s a- -> MultiRWSS.MultiRWS '[Config, ExactPrint.Anns] w s a-withTransformedAnns ast m = MultiRWSS.mGetRawR >>= \case- readers@(conf :+: anns :+: HNil) -> do- -- TODO: implement `local` for MultiReader/MultiRWS- MultiRWSS.mPutRawR (conf :+: f anns :+: HNil)- x <- m- MultiRWSS.mPutRawR readers- pure x- where- f anns =- let ((), (annsBalanced, _), _) =- ExactPrint.runTransform anns (commentAnnFixTransformGlob ast)- in annsBalanced---warnExtractorCompat :: GHC.Warn -> String-warnExtractorCompat (GHC.Warn _ (L _ s)) = s
− source/library/Language/Haskell/Brittany/Internal/LayouterBasics.hs
@@ -1,781 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.LayouterBasics where--import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS-import qualified Control.Monad.Writer.Strict as Writer-import qualified Data.Char as Char-import Data.Data-import qualified Data.Map as Map-import qualified Data.Semigroup as Semigroup-import qualified Data.Sequence as Seq-import qualified Data.Set as Set-import qualified Data.Text as Text-import qualified Data.Text.Lazy.Builder as Text.Builder-import DataTreePrint-import GHC (GenLocated(L), Located, moduleName, moduleNameString)-import qualified GHC.OldList as List-import GHC.Parser.Annotation (AnnKeywordId(..))-import GHC.Types.Name (getOccString)-import GHC.Types.Name.Occurrence (occNameString)-import GHC.Types.Name.Reader (RdrName(..))-import qualified GHC.Types.SrcLoc as GHC-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.ExactPrintUtils-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import qualified Language.Haskell.GHC.ExactPrint.Annotate as ExactPrint.Annotate-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint-import Language.Haskell.GHC.ExactPrint.Types (AnnKey, Annotation)-import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils----processDefault- :: ( ExactPrint.Annotate.Annotate ast- , MonadMultiWriter Text.Builder.Builder m- , MonadMultiReader ExactPrint.Types.Anns m- )- => Located ast- -> m ()-processDefault x = do- anns <- mAsk- let str = ExactPrint.exactPrint x anns- -- this hack is here so our print-empty-module trick does not add- -- a newline at the start if there actually is no module header / imports- -- / anything.- -- TODO: instead the appropriate annotation could be removed when "cleaning"- -- the module (header). This would remove the need for this hack!- case str of- "\n" -> return ()- _ -> mTell $ Text.Builder.fromString str---- | Use ExactPrint's output for this node; add a newly generated inline comment--- at insertion position (meant to point out to the user that this node is--- not handled by brittany yet). Useful when starting implementing new--- syntactic constructs when children are not handled yet.-briDocByExact- :: (ExactPrint.Annotate.Annotate ast)- => Located ast- -> ToBriDocM BriDocNumbered-briDocByExact ast = do- anns <- mAsk- traceIfDumpConf "ast"- _dconf_dump_ast_unknown- (printTreeWithCustom 100 (customLayouterF anns) ast)- docExt ast anns True---- | Use ExactPrint's output for this node.--- Consider that for multi-line input, the indentation of the code produced--- by ExactPrint might be different, and even incompatible with the indentation--- of its surroundings as layouted by brittany. But there are safe uses of--- this, e.g. for any top-level declarations.-briDocByExactNoComment- :: (ExactPrint.Annotate.Annotate ast)- => Located ast- -> ToBriDocM BriDocNumbered-briDocByExactNoComment ast = do- anns <- mAsk- traceIfDumpConf "ast"- _dconf_dump_ast_unknown- (printTreeWithCustom 100 (customLayouterF anns) ast)- docExt ast anns False---- | Use ExactPrint's output for this node, presuming that this output does--- not contain any newlines. If this property is not met, the semantics--- depend on the @econf_AllowRiskyExactPrintUse@ config flag.-briDocByExactInlineOnly- :: (ExactPrint.Annotate.Annotate ast)- => String- -> Located ast- -> ToBriDocM BriDocNumbered-briDocByExactInlineOnly infoStr ast = do- anns <- mAsk- traceIfDumpConf "ast"- _dconf_dump_ast_unknown- (printTreeWithCustom 100 (customLayouterF anns) ast)- let exactPrinted = Text.pack $ ExactPrint.exactPrint ast anns- fallbackMode <-- mAsk <&> _conf_errorHandling .> _econf_ExactPrintFallback .> confUnpack- let exactPrintNode t = allocateNode $ BDFExternal- (ExactPrint.Types.mkAnnKey ast)- (foldedAnnKeys ast)- False- t- let errorAction = do- mTell [ErrorUnknownNode infoStr ast]- docLit- $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"- case (fallbackMode, Text.lines exactPrinted) of- (ExactPrintFallbackModeNever, _ ) -> 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-rdrNameToText (Unqual occname) = Text.pack $ occNameString occname-rdrNameToText (Qual mname occname) =- Text.pack $ moduleNameString mname ++ "." ++ occNameString occname-rdrNameToText (Orig modul occname) =- Text.pack $ moduleNameString (moduleName modul) ++ occNameString occname-rdrNameToText (Exact name) = Text.pack $ getOccString name--lrdrNameToText :: GenLocated l RdrName -> Text-lrdrNameToText (L _ n) = rdrNameToText n--lrdrNameToTextAnnGen- :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)- => (Text -> Text)- -> Located RdrName- -> m Text-lrdrNameToTextAnnGen f ast@(L _ n) = do- anns <- mAsk- let t = f $ rdrNameToText n- let hasUni x (ExactPrint.Types.G y, _) = x == y- hasUni _ _ = False- -- TODO: in general: we should _always_ process all annotaiton stuff here.- -- whatever we don't probably should have had some effect on the- -- output. in such cases, resorting to byExact is probably the safe- -- choice.- return $ case Map.lookup (ExactPrint.Types.mkAnnKey ast) anns of- Nothing -> t- Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> case n of- Exact{} | t == Text.pack "()" -> t- _ | any (hasUni AnnBackquote) aks -> Text.pack "`" <> t <> Text.pack "`"- _ | any (hasUni AnnCommaTuple) aks -> t- _ | any (hasUni AnnOpenP) aks -> Text.pack "(" <> t <> Text.pack ")"- _ | otherwise -> t--lrdrNameToTextAnn- :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)- => Located RdrName- -> m Text-lrdrNameToTextAnn = lrdrNameToTextAnnGen id--lrdrNameToTextAnnTypeEqualityIsSpecial- :: (MonadMultiReader Config m, MonadMultiReader (Map AnnKey Annotation) m)- => Located RdrName- -> m Text-lrdrNameToTextAnnTypeEqualityIsSpecial ast = do- let f x = if x == Text.pack "Data.Type.Equality~"- then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh- else x- lrdrNameToTextAnnGen f ast---- | Same as lrdrNameToTextAnnTypeEqualityIsSpecial, but also inspects--- the annotations for a (parent) node for a tick to be added to the--- literal.--- Excessively long name to reflect on us having to work around such--- excessively obscure special cases in the exactprint API.-lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick- :: ( Data ast- , MonadMultiReader Config m- , MonadMultiReader (Map AnnKey Annotation) m- )- => Located ast- -> Located RdrName- -> m Text-lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick ast1 ast2 = do- hasQuote <- hasAnnKeyword ast1 AnnSimpleQuote- x <- lrdrNameToTextAnn ast2- let lit = if x == Text.pack "Data.Type.Equality~"- then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh- else x- return $ if hasQuote then Text.cons '\'' lit else lit--askIndent :: (MonadMultiReader Config m) => m Int-askIndent = confUnpack . _lconfig_indentAmount . _conf_layout <$> mAsk---extractAllComments- :: ExactPrint.Annotation -> [(ExactPrint.Comment, ExactPrint.DeltaPos)]-extractAllComments ann =- ExactPrint.annPriorComments ann ++ extractRestComments ann--extractRestComments- :: ExactPrint.Annotation -> [(ExactPrint.Comment, ExactPrint.DeltaPos)]-extractRestComments ann =- ExactPrint.annFollowingComments ann- ++ (ExactPrint.annsDP ann >>= \case- (ExactPrint.AnnComment com, dp) -> [(com, dp)]- _ -> []- )--filterAnns :: Data.Data.Data ast => ast -> ExactPrint.Anns -> ExactPrint.Anns-filterAnns ast =- Map.filterWithKey (\k _ -> k `Set.member` foldedAnnKeys ast)---- | True if there are any comments that are--- a) connected to any node below (in AST sense) the given node AND--- b) after (in source code order) the node.-hasAnyCommentsBelow :: Data ast => GHC.Located ast -> ToBriDocM Bool-hasAnyCommentsBelow ast@(L l _) =- List.any (\(c, _) -> ExactPrint.commentIdentifier c > ExactPrint.Utils.rs l)- <$> astConnectedComments ast--hasCommentsBetween- :: Data ast- => GHC.Located ast- -> AnnKeywordId- -> AnnKeywordId- -> ToBriDocM Bool-hasCommentsBetween ast leftKey rightKey = do- mAnn <- astAnn ast- let go1 [] = False- go1 ((ExactPrint.G kw, _dp) : rest) | kw == leftKey = go2 rest- go1 (_ : rest) = go1 rest- go2 [] = False- go2 ((ExactPrint.AnnComment _, _dp) : _rest) = True- go2 ((ExactPrint.G kw, _dp) : _rest) | kw == rightKey = False- go2 (_ : rest) = go2 rest- case mAnn of- Nothing -> pure False- Just ann -> pure $ go1 $ ExactPrint.annsDP ann---- | True if there are any comments that are connected to any node below (in AST--- sense) the given node-hasAnyCommentsConnected :: Data ast => GHC.Located ast -> ToBriDocM Bool-hasAnyCommentsConnected ast = not . null <$> astConnectedComments ast---- | True if there are any regular comments connected to any node below (in AST--- sense) the given node-hasAnyRegularCommentsConnected :: Data ast => GHC.Located ast -> ToBriDocM Bool-hasAnyRegularCommentsConnected ast =- any isRegularComment <$> astConnectedComments ast---- | Regular comments are comments that are actually "source code comments",--- i.e. things that start with "--" or "{-". In contrast to comment-annotations--- used by ghc-exactprint for capturing symbols (and their exact positioning).------ Only the type instance layouter makes use of this filter currently, but--- it might make sense to apply it more aggressively or make it the default ---- I believe that most of the time we branch on the existence of comments, we--- only care about "regular" comments. We simply did not need the distinction--- because "irregular" comments are not that common outside of type/data decls.-isRegularComment :: (ExactPrint.Comment, ExactPrint.DeltaPos) -> Bool-isRegularComment = (== Nothing) . ExactPrint.Types.commentOrigin . fst--astConnectedComments- :: Data ast- => GHC.Located ast- -> ToBriDocM [(ExactPrint.Types.Comment, ExactPrint.Types.DeltaPos)]-astConnectedComments ast = do- anns <- filterAnns ast <$> mAsk- pure $ extractAllComments =<< Map.elems anns--hasAnyCommentsPrior :: Data ast => GHC.Located ast -> ToBriDocM Bool-hasAnyCommentsPrior ast = astAnn ast <&> \case- Nothing -> False- Just (ExactPrint.Types.Ann _ priors _ _ _ _) -> not $ null priors--hasAnyRegularCommentsRest :: Data ast => GHC.Located ast -> ToBriDocM Bool-hasAnyRegularCommentsRest ast = astAnn ast <&> \case- Nothing -> False- Just ann -> any isRegularComment (extractRestComments ann)--hasAnnKeywordComment- :: Data ast => GHC.Located ast -> AnnKeywordId -> ToBriDocM Bool-hasAnnKeywordComment ast annKeyword = astAnn ast <&> \case- Nothing -> False- Just ann -> any hasK (extractAllComments ann)- where hasK = (== Just annKeyword) . ExactPrint.Types.commentOrigin . fst--hasAnnKeyword- :: (Data a, MonadMultiReader (Map AnnKey Annotation) m)- => Located a- -> AnnKeywordId- -> m Bool-hasAnnKeyword ast annKeyword = astAnn ast <&> \case- Nothing -> False- Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> any hasK aks- where- hasK (ExactPrint.Types.G x, _) = x == annKeyword- hasK _ = False--astAnn- :: (Data ast, MonadMultiReader (Map AnnKey Annotation) m)- => GHC.Located ast- -> m (Maybe Annotation)-astAnn ast = Map.lookup (ExactPrint.Types.mkAnnKey ast) <$> mAsk---- new BriDoc stuff--allocateNode- :: MonadMultiState NodeAllocIndex m => BriDocFInt -> m BriDocNumbered-allocateNode bd = do- i <- allocNodeIndex- return (i, bd)--allocNodeIndex :: MonadMultiState NodeAllocIndex m => m Int-allocNodeIndex = do- NodeAllocIndex i <- mGet- mSet $ NodeAllocIndex (i + 1)- return i---- docEmpty :: MonadMultiState NodeAllocIndex m => m BriDocNumbered--- docEmpty = allocateNode BDFEmpty------ docLit :: MonadMultiState NodeAllocIndex m => Text -> m BriDocNumbered--- docLit t = allocateNode $ BDFLit t------ docExt :: (ExactPrint.Annotate.Annotate ast, MonadMultiState NodeAllocIndex m)--- => Located ast -> ExactPrint.Types.Anns -> Bool -> m BriDocNumbered--- docExt x anns shouldAddComment = allocateNode $ BDFExternal--- (ExactPrint.Types.mkAnnKey x)--- (foldedAnnKeys x)--- shouldAddComment--- (Text.pack $ ExactPrint.exactPrint x anns)------ docAlt :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered--- docAlt l = allocateNode . BDFAlt =<< sequence l--------- docSeq :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered--- docSeq l = allocateNode . BDFSeq =<< sequence l------ docLines :: MonadMultiState NodeAllocIndex m => [m BriDocNumbered] -> m BriDocNumbered--- docLines l = allocateNode . BDFLines =<< sequence l------ docCols :: MonadMultiState NodeAllocIndex m => ColSig -> [m BriDocNumbered] -> m BriDocNumbered--- docCols sig l = allocateNode . BDFCols sig =<< sequence l------ docAddBaseY :: MonadMultiState NodeAllocIndex m => BrIndent -> m BriDocNumbered -> m BriDocNumbered--- docAddBaseY ind bdm = allocateNode . BDFAddBaseY ind =<< bdm------ docSetBaseY :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- docSetBaseY bdm = allocateNode . BDFSetBaseY =<< bdm------ docSetIndentLevel :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- docSetIndentLevel bdm = allocateNode . BDFSetIndentLevel =<< bdm------ docSeparator :: MonadMultiState NodeAllocIndex m => m BriDocNumbered--- docSeparator = allocateNode BDFSeparator------ docAnnotationPrior :: MonadMultiState NodeAllocIndex m => AnnKey -> m BriDocNumbered -> m BriDocNumbered--- docAnnotationPrior annKey bdm = allocateNode . BDFAnnotationPrior annKey =<< bdm------ docAnnotationPost :: MonadMultiState NodeAllocIndex m => AnnKey -> m BriDocNumbered -> m BriDocNumbered--- docAnnotationPost annKey bdm = allocateNode . BDFAnnotationPost annKey =<< bdm------ docNonBottomSpacing :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- docNonBottomSpacing bdm = allocateNode . BDFNonBottomSpacing =<< bdm------ appSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- appSep x = docSeq [x, docSeparator]------ docCommaSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered--- docCommaSep = appSep $ docLit $ Text.pack ","------ docParenLSep :: MonadMultiState NodeAllocIndex m => m BriDocNumbered--- docParenLSep = appSep $ docLit $ Text.pack "("--------- docPostComment :: (Data.Data.Data ast, MonadMultiState NodeAllocIndex m)--- => Located ast--- -> m BriDocNumbered--- -> m BriDocNumbered--- docPostComment ast bdm = do--- bd <- bdm--- allocateNode $ BDFAnnotationPost (ExactPrint.Types.mkAnnKey ast) bd------ docWrapNode :: ( Data.Data.Data ast, MonadMultiState NodeAllocIndex m)--- => Located ast--- -> m BriDocNumbered--- -> m BriDocNumbered--- docWrapNode ast bdm = do--- bd <- bdm--- i1 <- allocNodeIndex--- i2 <- allocNodeIndex--- return--- $ (,) i1--- $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast)--- $ (,) i2--- $ BDFAnnotationPost (ExactPrint.Types.mkAnnKey ast)--- $ bd------ docPar :: MonadMultiState NodeAllocIndex m--- => m BriDocNumbered--- -> m BriDocNumbered--- -> m BriDocNumbered--- docPar lineM indentedM = do--- line <- lineM--- indented <- indentedM--- allocateNode $ BDFPar BrIndentNone line indented------ docForceSingleline :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- docForceSingleline bdm = allocateNode . BDFForceSingleline =<< bdm------ docForceMultiline :: MonadMultiState NodeAllocIndex m => m BriDocNumbered -> m BriDocNumbered--- docForceMultiline bdm = allocateNode . BDFForceMultiline =<< bdm------ docEnsureIndent :: MonadMultiState NodeAllocIndex m => BrIndent -> m BriDocNumbered -> m BriDocNumbered--- docEnsureIndent ind mbd = mbd >>= \bd -> allocateNode $ BDFEnsureIndent ind bd--docEmpty :: ToBriDocM BriDocNumbered-docEmpty = allocateNode BDFEmpty--docLit :: Text -> ToBriDocM BriDocNumbered-docLit t = allocateNode $ BDFLit t--docLitS :: String -> ToBriDocM BriDocNumbered-docLitS s = allocateNode $ BDFLit $ Text.pack s--docExt- :: (ExactPrint.Annotate.Annotate ast)- => Located ast- -> ExactPrint.Types.Anns- -> Bool- -> ToBriDocM BriDocNumbered-docExt x anns shouldAddComment = allocateNode $ BDFExternal- (ExactPrint.Types.mkAnnKey x)- (foldedAnnKeys x)- shouldAddComment- (Text.pack $ ExactPrint.exactPrint x anns)--docAlt :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered-docAlt l = allocateNode . BDFAlt =<< sequence l--newtype CollectAltM a = CollectAltM (Writer.Writer [ToBriDocM BriDocNumbered] a)- deriving (Functor, Applicative, Monad)--addAlternativeCond :: Bool -> ToBriDocM BriDocNumbered -> CollectAltM ()-addAlternativeCond cond doc =- when cond (addAlternative doc)--addAlternative :: ToBriDocM BriDocNumbered -> CollectAltM ()-addAlternative =- CollectAltM . Writer.tell . (: [])--runFilteredAlternative :: CollectAltM () -> ToBriDocM BriDocNumbered-runFilteredAlternative (CollectAltM action) =- docAlt $ Writer.execWriter action---docSeq :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered-docSeq [] = docEmpty-docSeq l = allocateNode . BDFSeq =<< sequence l--docLines :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered-docLines l = allocateNode . BDFLines =<< sequence l--docCols :: ColSig -> [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered-docCols sig l = allocateNode . BDFCols sig =<< sequence l--docAddBaseY :: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docAddBaseY ind bdm = allocateNode . BDFAddBaseY ind =<< bdm--docSetBaseY :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docSetBaseY bdm = do- bd <- bdm- -- the order here is important so that these two nodes can be treated- -- properly over at `transformAlts`.- n1 <- allocateNode $ BDFBaseYPushCur bd- n2 <- allocateNode $ BDFBaseYPop n1- return n2--docSetIndentLevel :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docSetIndentLevel bdm = do- bd <- bdm- n1 <- allocateNode $ BDFIndentLevelPushCur bd- n2 <- allocateNode $ BDFIndentLevelPop n1- return n2--docSetBaseAndIndent :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docSetBaseAndIndent = docSetBaseY . docSetIndentLevel--docSeparator :: ToBriDocM BriDocNumbered-docSeparator = allocateNode BDFSeparator--docAnnotationPrior- :: AnnKey -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docAnnotationPrior annKey bdm =- allocateNode . BDFAnnotationPrior annKey =<< bdm--docAnnotationKW- :: AnnKey- -> Maybe AnnKeywordId- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-docAnnotationKW annKey kw bdm = allocateNode . BDFAnnotationKW annKey kw =<< bdm--docMoveToKWDP- :: AnnKey- -> AnnKeywordId- -> Bool- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-docMoveToKWDP annKey kw shouldRestoreIndent bdm =- allocateNode . BDFMoveToKWDP annKey kw shouldRestoreIndent =<< bdm--docAnnotationRest- :: AnnKey -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docAnnotationRest annKey bdm = allocateNode . BDFAnnotationRest annKey =<< bdm--docNonBottomSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docNonBottomSpacing bdm = allocateNode . BDFNonBottomSpacing False =<< bdm--docNonBottomSpacingS :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docNonBottomSpacingS bdm = allocateNode . BDFNonBottomSpacing True =<< bdm--docSetParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docSetParSpacing bdm = allocateNode . BDFSetParSpacing =<< bdm--docForceParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docForceParSpacing bdm = allocateNode . BDFForceParSpacing =<< bdm--docDebug :: String -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docDebug s bdm = allocateNode . BDFDebug s =<< bdm--appSep :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-appSep x = docSeq [x, docSeparator]--docCommaSep :: ToBriDocM BriDocNumbered-docCommaSep = appSep $ docLit $ Text.pack ","--docParenLSep :: ToBriDocM BriDocNumbered-docParenLSep = appSep docParenL---- TODO: we don't make consistent use of these (yet). However, I think the--- most readable approach overall might be something else: define--- `lit = docLit . Text.pack` and `prepSep = docSeq [docSeparator, x]`.--- I think those two would make the usage most readable.--- lit "(" and appSep (lit "(") are understandable and short without--- introducing a new top-level binding for all types of parentheses.-docParenL :: ToBriDocM BriDocNumbered-docParenL = docLit $ Text.pack "("--docParenR :: ToBriDocM BriDocNumbered-docParenR = docLit $ Text.pack ")"--docParenHashLSep :: ToBriDocM BriDocNumbered-docParenHashLSep = docSeq [docLit $ Text.pack "(#", docSeparator]--docParenHashRSep :: ToBriDocM BriDocNumbered-docParenHashRSep = docSeq [docSeparator, docLit $ Text.pack "#)"]--docBracketL :: ToBriDocM BriDocNumbered-docBracketL = docLit $ Text.pack "["--docBracketR :: ToBriDocM BriDocNumbered-docBracketR = docLit $ Text.pack "]"---docTick :: ToBriDocM BriDocNumbered-docTick = docLit $ Text.pack "'"--docNodeAnnKW- :: Data.Data.Data ast- => Located ast- -> Maybe AnnKeywordId- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-docNodeAnnKW ast kw bdm =- docAnnotationKW (ExactPrint.Types.mkAnnKey ast) kw bdm--docNodeMoveToKWDP- :: Data.Data.Data ast- => Located ast- -> AnnKeywordId- -> Bool- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-docNodeMoveToKWDP ast kw shouldRestoreIndent bdm =- docMoveToKWDP (ExactPrint.Types.mkAnnKey ast) kw shouldRestoreIndent bdm--class DocWrapable a where- docWrapNode :: ( Data.Data.Data ast)- => Located ast- -> a- -> a- docWrapNodePrior :: ( Data.Data.Data ast)- => Located ast- -> a- -> a- docWrapNodeRest :: ( Data.Data.Data ast)- => Located ast- -> a- -> a--instance DocWrapable (ToBriDocM BriDocNumbered) where- docWrapNode ast bdm = do- bd <- bdm- i1 <- allocNodeIndex- i2 <- allocNodeIndex- return- $ (,) i1- $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast)- $ (,) i2- $ BDFAnnotationRest (ExactPrint.Types.mkAnnKey ast)- $ bd- docWrapNodePrior ast bdm = do- bd <- bdm- i1 <- allocNodeIndex- return- $ (,) i1- $ BDFAnnotationPrior (ExactPrint.Types.mkAnnKey ast)- $ bd- docWrapNodeRest ast bdm = do- bd <- bdm- i2 <- allocNodeIndex- return- $ (,) i2- $ BDFAnnotationRest (ExactPrint.Types.mkAnnKey ast)- $ bd--instance DocWrapable (ToBriDocM a) => DocWrapable [ToBriDocM a] where- docWrapNode ast bdms = case bdms of- [] -> []- [bd] -> [docWrapNode ast bd]- (bd1:bdR) | (bdN:bdM) <- reverse bdR ->- [docWrapNodePrior ast bd1] ++ reverse bdM ++ [docWrapNodeRest ast bdN]- _ -> error "cannot happen (TM)"- docWrapNodePrior ast bdms = case bdms of- [] -> []- [bd] -> [docWrapNodePrior ast bd]- (bd1:bdR) -> docWrapNodePrior ast bd1 : bdR- docWrapNodeRest ast bdms = case reverse bdms of- [] -> []- (bdN:bdR) -> reverse $ docWrapNodeRest ast bdN : bdR--instance DocWrapable (ToBriDocM a) => DocWrapable (ToBriDocM [a]) where- docWrapNode ast bdsm = do- bds <- bdsm- case bds of- [] -> return [] -- TODO: this might be bad. maybe. then again, not really. well.- [bd] -> do- bd' <- docWrapNode ast (return bd)- return [bd']- (bd1:bdR) | (bdN:bdM) <- reverse bdR -> do- bd1' <- docWrapNodePrior ast (return bd1)- bdN' <- docWrapNodeRest ast (return bdN)- return $ [bd1'] ++ reverse bdM ++ [bdN']- _ -> error "cannot happen (TM)"- docWrapNodePrior ast bdsm = do- bds <- bdsm- case bds of- [] -> return []- (bd1:bdR) -> do- bd1' <- docWrapNodePrior ast (return bd1)- return (bd1':bdR)- docWrapNodeRest ast bdsm = do- bds <- bdsm- case reverse bds of- [] -> return []- (bdN:bdR) -> do- bdN' <- docWrapNodeRest ast (return bdN)- return $ reverse (bdN':bdR)--instance DocWrapable (ToBriDocM a) => DocWrapable (ToBriDocM (Seq a)) where- docWrapNode ast bdsm = do- bds <- bdsm- case Seq.viewl bds of- Seq.EmptyL -> return Seq.empty -- TODO: this might be bad. maybe. then again, not really. well.- bd1 Seq.:< rest -> case Seq.viewr rest of- Seq.EmptyR -> do- bd1' <- docWrapNode ast (return bd1)- return $ Seq.singleton bd1'- bdM Seq.:> bdN -> do- bd1' <- docWrapNodePrior ast (return bd1)- bdN' <- docWrapNodeRest ast (return bdN)- return $ (bd1' Seq.<| bdM) Seq.|> bdN'- docWrapNodePrior ast bdsm = do- bds <- bdsm- case Seq.viewl bds of- Seq.EmptyL -> return Seq.empty- bd1 Seq.:< bdR -> do- bd1' <- docWrapNodePrior ast (return bd1)- return $ bd1' Seq.<| bdR- docWrapNodeRest ast bdsm = do- bds <- bdsm- case Seq.viewr bds of- Seq.EmptyR -> return Seq.empty- bdR Seq.:> bdN -> do- bdN' <- docWrapNodeRest ast (return bdN)- return $ bdR Seq.|> bdN'--instance DocWrapable (ToBriDocM ([BriDocNumbered], BriDocNumbered, a)) where- docWrapNode ast stuffM = do- (bds, bd, x) <- stuffM- if null bds- then do- bd' <- docWrapNode ast (return bd)- return (bds, bd', x)- else do- bds' <- docWrapNodePrior ast (return bds)- bd' <- docWrapNodeRest ast (return bd)- return (bds', bd', x)- docWrapNodePrior ast stuffM = do- (bds, bd, x) <- stuffM- bds' <- docWrapNodePrior ast (return bds)- return (bds', bd, x)- docWrapNodeRest ast stuffM = do- (bds, bd, x) <- stuffM- bd' <- docWrapNodeRest ast (return bd)- return (bds, bd', x)----docPar- :: ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-docPar lineM indentedM = do- line <- lineM- indented <- indentedM- allocateNode $ BDFPar BrIndentNone line indented--docForceSingleline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docForceSingleline bdm = allocateNode . BDFForceSingleline =<< bdm--docForceMultiline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docForceMultiline bdm = allocateNode . BDFForceMultiline =<< bdm--docEnsureIndent- :: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-docEnsureIndent ind mbd = mbd >>= \bd -> allocateNode $ BDFEnsureIndent ind bd--unknownNodeError- :: Data.Data.Data ast- => String- -> GenLocated GHC.SrcSpan ast- -> ToBriDocM BriDocNumbered-unknownNodeError infoStr ast = do- mTell [ErrorUnknownNode infoStr ast]- docLit $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"--spacifyDocs :: [ToBriDocM BriDocNumbered] -> [ToBriDocM BriDocNumbered]-spacifyDocs [] = []-spacifyDocs ds = fmap appSep (List.init ds) ++ [List.last ds]--briDocMToPPM :: ToBriDocM a -> PPMLocal a-briDocMToPPM m = do- (x, errs, debugs) <- briDocMToPPMInner m- mTell debugs- mTell errs- return x--briDocMToPPMInner :: ToBriDocM a -> PPMLocal (a, [BrittanyError], Seq String)-briDocMToPPMInner m = do- readers <- MultiRWSS.mGetRawR- let ((x, errs), debugs) =- runIdentity- $ MultiRWSS.runMultiRWSTNil- $ MultiRWSS.withMultiStateA (NodeAllocIndex 1)- $ MultiRWSS.withMultiReaders readers- $ MultiRWSS.withMultiWriterAW- $ MultiRWSS.withMultiWriterAW- $ m- pure (x, errs, debugs)--docSharedWrapper :: Monad m => (x -> m y) -> x -> m (m y)-docSharedWrapper f x = return <$> f x
− source/library/Language/Haskell/Brittany/Internal/Layouters/DataDecl.hs
@@ -1,437 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.DataDecl where--import qualified Data.Data-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import GHC (GenLocated(L), Located)-import qualified GHC-import GHC.Hs-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.Type-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types----layoutDataDecl- :: Located (TyClDecl GhcPs)- -> Located RdrName- -> LHsQTyVars GhcPs- -> HsDataDefn GhcPs- -> ToBriDocM BriDocNumbered-layoutDataDecl ltycl name (HsQTvs _ bndrs) defn = case defn of- -- newtype MyType a b = MyType ..- HsDataDefn _ext NewType (L _ []) _ctype Nothing [cons] mDerivs -> case cons of- (L _ (ConDeclH98 _ext consName (L _ False) _qvars (Just (L _ [])) details _conDoc)) ->- docWrapNode ltycl $ do- nameStr <- lrdrNameToTextAnn name- consNameStr <- lrdrNameToTextAnn consName- tyVarLine <- return <$> createBndrDoc bndrs- -- headDoc <- fmap return $ docSeq- -- [ appSep $ docLitS "newtype")- -- , appSep $ docLit nameStr- -- , appSep tyVarLine- -- ]- rhsDoc <- return <$> createDetailsDoc consNameStr details- createDerivingPar mDerivs $ docSeq- [ appSep $ docLitS "newtype"- , appSep $ docLit nameStr- , appSep tyVarLine- , docSeparator- , docLitS "="- , docSeparator- , rhsDoc- ]- _ -> briDocByExactNoComment ltycl--- -- data MyData a b- -- (zero constructors)- HsDataDefn _ext DataType (L _ lhsContext) _ctype Nothing [] mDerivs ->- docWrapNode ltycl $ do- lhsContextDoc <- docSharedWrapper createContextDoc lhsContext- nameStr <- lrdrNameToTextAnn name- tyVarLine <- return <$> createBndrDoc bndrs- createDerivingPar mDerivs $ docSeq- [ appSep $ docLitS "data"- , lhsContextDoc- , appSep $ docLit nameStr- , appSep tyVarLine- ]-- -- data MyData = MyData ..- -- data MyData = MyData { .. }- HsDataDefn _ext DataType (L _ lhsContext) _ctype Nothing [cons] mDerivs ->- case cons of- (L _ (ConDeclH98 _ext consName (L _ _hasExt) qvars mRhsContext details _conDoc)) ->- docWrapNode ltycl $ do- lhsContextDoc <- docSharedWrapper createContextDoc lhsContext- nameStr <- lrdrNameToTextAnn name- consNameStr <- lrdrNameToTextAnn consName- tyVarLine <- return <$> createBndrDoc bndrs- forallDocMay <- case createForallDoc qvars of- Nothing -> pure Nothing- Just x -> Just . pure <$> x- rhsContextDocMay <- case mRhsContext of- Nothing -> pure Nothing- Just (L _ ctxt) -> Just . pure <$> createContextDoc ctxt- rhsDoc <- return <$> createDetailsDoc consNameStr details- consDoc <- fmap pure- $ docNonBottomSpacing- $ case (forallDocMay, rhsContextDocMay) of- (Just forallDoc, Just rhsContextDoc) -> docLines- [ docSeq [docLitS "=", docSeparator, docForceSingleline forallDoc]- , docSeq- [ docLitS "."- , docSeparator- , docSetBaseY $ docLines [rhsContextDoc, docSetBaseY rhsDoc]- ]- ]- (Just forallDoc, Nothing) -> docLines- [ docSeq [docLitS "=", docSeparator, docForceSingleline forallDoc]- , docSeq [docLitS ".", docSeparator, rhsDoc]- ]- (Nothing, Just rhsContextDoc) -> docSeq- [ docLitS "="- , docSeparator- , docSetBaseY $ docLines [rhsContextDoc, docSetBaseY rhsDoc]- ]- (Nothing, Nothing) -> docSeq [docLitS "=", docSeparator, rhsDoc]- createDerivingPar mDerivs $ docAlt- [ -- data D = forall a . Show a => D a- docSeq- [ docNodeAnnKW ltycl (Just GHC.AnnData)- $ docSeq- [ appSep $ docLitS "data"- , docForceSingleline $ lhsContextDoc- , appSep $ docLit nameStr- , appSep tyVarLine- , docSeparator- ]- , docLitS "="- , docSeparator- , docSetIndentLevel $ docSeq- [ case forallDocMay of- Nothing -> docEmpty- Just forallDoc -> docSeq- [ docForceSingleline forallDoc- , docSeparator- , docLitS "."- , docSeparator- ]- , maybe docEmpty docForceSingleline rhsContextDocMay- , rhsDoc- ]- ]- , -- data D- -- = forall a . Show a => D a- docAddBaseY BrIndentRegular $ docPar- ( docNodeAnnKW ltycl (Just GHC.AnnData)- $ docSeq- [ appSep $ docLitS "data"- , docForceSingleline lhsContextDoc- , appSep $ docLit nameStr- , tyVarLine- ]- )- ( docSeq- [ docLitS "="- , docSeparator- , docSetIndentLevel $ docSeq- [ case forallDocMay of- Nothing -> docEmpty- Just forallDoc -> docSeq- [ docForceSingleline forallDoc- , docSeparator- , docLitS "."- , docSeparator- ]- , maybe docEmpty docForceSingleline rhsContextDocMay- , rhsDoc- ]- ]- )- , -- data D- -- = forall a- -- . Show a =>- -- D a- docAddBaseY BrIndentRegular $ docPar- ( docNodeAnnKW ltycl (Just GHC.AnnData)- $ docSeq- [ appSep $ docLitS "data"- , docForceSingleline lhsContextDoc- , appSep $ docLit nameStr- , tyVarLine- ]- )- consDoc- , -- data- -- Show a =>- -- D- -- = forall a- -- . Show a =>- -- D a- -- This alternative is only for -XDatatypeContexts.- -- But I think it is rather unlikely this will trigger without- -- -XDataTypeContexts, especially with the `docNonBottomSpacing`- -- above, so while not strictly necessary, this should not- -- hurt.- docAddBaseY BrIndentRegular $ docPar- (docLitS "data")- ( docLines- [ lhsContextDoc- , docNodeAnnKW ltycl (Just GHC.AnnData)- $ docSeq- [ appSep $ docLit nameStr- , tyVarLine- ]- , consDoc- ]- )- ]- _ -> briDocByExactNoComment ltycl-- _ -> briDocByExactNoComment ltycl--createContextDoc :: HsContext GhcPs -> ToBriDocM BriDocNumbered-createContextDoc [] = docEmpty-createContextDoc [t] =- docSeq [layoutType t, docSeparator, docLitS "=>", docSeparator]-createContextDoc (t1 : tR) = do- t1Doc <- docSharedWrapper layoutType t1- tRDocs <- tR `forM` docSharedWrapper layoutType- docAlt- [ docSeq- [ docLitS "("- , docForceSingleline $ docSeq $ List.intersperse docCommaSep- (t1Doc : tRDocs)- , docLitS ") =>"- , docSeparator- ]- , docLines $ join- [ [docSeq [docLitS "(", docSeparator, t1Doc]]- , tRDocs- <&> \tRDoc -> docSeq [docLitS ",", docSeparator, tRDoc]- , [docLitS ") =>", docSeparator]- ]- ]--createBndrDoc :: [LHsTyVarBndr flag GhcPs] -> ToBriDocM BriDocNumbered-createBndrDoc bs = do- tyVarDocs <- bs `forM` \case- (L _ (UserTyVar _ _ext vname)) -> return $ (lrdrNameToText vname, Nothing)- (L _ (KindedTyVar _ _ext lrdrName kind)) -> do- d <- docSharedWrapper layoutType kind- return $ (lrdrNameToText lrdrName, Just $ d)- docSeq- $ List.intersperse docSeparator- $ tyVarDocs- <&> \(vname, mKind) -> case mKind of- Nothing -> docLit vname- Just kind -> docSeq- [ docLitS "("- , docLit vname- , docSeparator- , docLitS "::"- , docSeparator- , kind- , docLitS ")"- ]--createDerivingPar- :: HsDeriving GhcPs -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered-createDerivingPar derivs mainDoc = do- case derivs of- (L _ []) -> mainDoc- (L _ types) ->- docPar mainDoc- $ docEnsureIndent BrIndentRegular- $ docLines- $ docWrapNode derivs- $ derivingClauseDoc- <$> types--derivingClauseDoc :: LHsDerivingClause GhcPs -> ToBriDocM BriDocNumbered-derivingClauseDoc (L _ (HsDerivingClause _ext mStrategy types)) = case types of- (L _ []) -> docSeq []- (L _ ts) ->- let- tsLength = length ts- whenMoreThan1Type val =- if tsLength > 1 then docLitS val else docLitS ""- (lhsStrategy, rhsStrategy) = maybe (docEmpty, docEmpty) strategyLeftRight mStrategy- in- docSeq- [ docDeriving- , docWrapNodePrior types $ lhsStrategy- , docSeparator- , whenMoreThan1Type "("- , docWrapNodeRest types- $ docSeq- $ List.intersperse docCommaSep- $ ts <&> \case- HsIB _ t -> layoutType t- , whenMoreThan1Type ")"- , rhsStrategy- ]- where- strategyLeftRight = \case- (L _ StockStrategy ) -> (docLitS " stock", docEmpty)- (L _ AnyclassStrategy ) -> (docLitS " anyclass", docEmpty)- (L _ NewtypeStrategy ) -> (docLitS " newtype", docEmpty)- lVia@(L _ (ViaStrategy viaTypes) ) ->- ( docEmpty- , case viaTypes of- HsIB _ext t -> docSeq- [ docWrapNode lVia $ docLitS " via"- , docSeparator- , layoutType t- ]- )--docDeriving :: ToBriDocM BriDocNumbered-docDeriving = docLitS "deriving"--createDetailsDoc- :: Text -> HsConDeclDetails GhcPs -> (ToBriDocM BriDocNumbered)-createDetailsDoc consNameStr details = case details of- PrefixCon args -> do- indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack- let- singleLine = docSeq- [ docLit consNameStr- , docSeparator- , docForceSingleline- $ docSeq- $ List.intersperse docSeparator- $ fmap hsScaledThing args <&> layoutType- ]- leftIndented = docSetParSpacing- . docAddBaseY BrIndentRegular- . docPar (docLit consNameStr)- . docLines- $ layoutType <$> fmap hsScaledThing args- multiAppended = docSeq- [ docLit consNameStr- , docSeparator- , docSetBaseY $ docLines $ layoutType <$> fmap hsScaledThing args- ]- multiIndented = docSetBaseY $ docAddBaseY BrIndentRegular $ docPar- (docLit consNameStr)- (docLines $ layoutType <$> fmap hsScaledThing args)- case indentPolicy of- IndentPolicyLeft -> docAlt [singleLine, leftIndented]- IndentPolicyMultiple -> docAlt [singleLine, multiAppended, leftIndented]- IndentPolicyFree ->- docAlt [singleLine, multiAppended, multiIndented, leftIndented]- RecCon (L _ []) -> docSeq [docLit consNameStr, docSeparator, docLit $ Text.pack "{}"]- RecCon lRec@(L _ fields@(_:_)) -> do- let ((fName1, fType1) : fDocR) = mkFieldDocs fields- -- allowSingleline <- mAsk <&> _conf_layout .> _lconfig_allowSinglelineRecord .> confUnpack- let allowSingleline = False- docAddBaseY BrIndentRegular- $ runFilteredAlternative- $ do- -- single-line: { i :: Int, b :: Bool }- addAlternativeCond allowSingleline $ docSeq- [ docLit consNameStr- , docSeparator- , docWrapNodePrior lRec $ docLitS "{"- , docSeparator- , docWrapNodeRest lRec- $ docForceSingleline- $ docSeq- $ join- $ [fName1, docSeparator, docLitS "::", docSeparator, fType1]- : [ [ docLitS ","- , docSeparator- , fName- , docSeparator- , docLitS "::"- , docSeparator- , fType- ]- | (fName, fType) <- fDocR- ]- , docSeparator- , docLitS "}"- ]- addAlternative $ docPar- (docLit consNameStr)- (docWrapNodePrior lRec $ docNonBottomSpacingS $ docLines- [ docAlt- [ docCols ColRecDecl- [ appSep (docLitS "{")- , appSep $ docForceSingleline fName1- , docSeq [docLitS "::", docSeparator]- , docForceSingleline $ fType1- ]- , docSeq- [ docLitS "{"- , docSeparator- , docSetBaseY $ docAddBaseY BrIndentRegular $ docPar- fName1- (docSeq [docLitS "::", docSeparator, fType1])- ]- ]- , docWrapNodeRest lRec $ docLines $ fDocR <&> \(fName, fType) ->- docAlt- [ docCols ColRecDecl- [ docCommaSep- , appSep $ docForceSingleline fName- , docSeq [docLitS "::", docSeparator]- , docForceSingleline fType- ]- , docSeq- [ docLitS ","- , docSeparator- , docSetBaseY $ docAddBaseY BrIndentRegular $ docPar- fName- (docSeq [docLitS "::", docSeparator, fType])- ]- ]- , docLitS "}"- ]- )- InfixCon arg1 arg2 -> docSeq- [ layoutType $ hsScaledThing arg1- , docSeparator- , docLit consNameStr- , docSeparator- , layoutType $ hsScaledThing arg2- ]- where- mkFieldDocs- :: [LConDeclField GhcPs]- -> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]- mkFieldDocs = fmap $ \lField -> case lField of- L _ (ConDeclField _ext names t _) -> createNamesAndTypeDoc lField names t--createForallDoc :: [LHsTyVarBndr flag GhcPs] -> Maybe (ToBriDocM BriDocNumbered)-createForallDoc [] = Nothing-createForallDoc lhsTyVarBndrs = Just $ docSeq- [docLitS "forall ", createBndrDoc lhsTyVarBndrs]--createNamesAndTypeDoc- :: Data.Data.Data ast- => Located ast- -> [GenLocated t (FieldOcc GhcPs)]- -> Located (HsType GhcPs)- -> (ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)-createNamesAndTypeDoc lField names t =- ( docNodeAnnKW lField Nothing $ docWrapNodePrior lField $ docSeq- [ docSeq- $ List.intersperse docCommaSep- $ names- <&> \case- L _ (FieldOcc _ fieldName) ->- docLit =<< lrdrNameToTextAnn fieldName- ]- , docWrapNodeRest lField $ layoutType t- )
− source/library/Language/Haskell/Brittany/Internal/Layouters/Decl.hs
@@ -1,975 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal.Layouters.Decl where--import qualified Data.Data-import qualified Data.Foldable-import qualified Data.Maybe-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import GHC (AnnKeywordId(..), GenLocated(L))-import GHC.Data.Bag (bagToList, emptyBag)-import qualified GHC.Data.FastString as FastString-import GHC.Hs-import qualified GHC.OldList as List-import GHC.Types.Basic- ( Activation(..)- , InlinePragma(..)- , InlineSpec(..)- , LexicalFixity(..)- , RuleMatchInfo(..)- )-import GHC.Types.SrcLoc (Located, SrcSpan, getLoc, unLoc)-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.ExactPrintUtils-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.DataDecl-import {-# SOURCE #-} Language.Haskell.Brittany.Internal.Layouters.Expr-import Language.Haskell.Brittany.Internal.Layouters.Pattern-import {-# SOURCE #-} Language.Haskell.Brittany.Internal.Layouters.Stmt-import Language.Haskell.Brittany.Internal.Layouters.Type-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import Language.Haskell.GHC.ExactPrint.Types (mkAnnKey)-import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint----layoutDecl :: ToBriDoc HsDecl-layoutDecl d@(L loc decl) = case decl of- SigD _ sig -> withTransformedAnns d $ layoutSig (L loc sig)- ValD _ bind -> withTransformedAnns d $ layoutBind (L loc bind) >>= \case- Left ns -> docLines $ return <$> ns- Right n -> return n- TyClD _ tycl -> withTransformedAnns d $ layoutTyCl (L loc tycl)- InstD _ (TyFamInstD _ tfid) ->- withTransformedAnns d $ layoutTyFamInstDecl False d tfid- InstD _ (ClsInstD _ inst) ->- withTransformedAnns d $ layoutClsInst (L loc inst)- _ -> briDocByExactNoComment d------------------------------------------------------------------------------------- Sig-----------------------------------------------------------------------------------layoutSig :: ToBriDoc Sig-layoutSig lsig@(L _loc sig) = case sig of- TypeSig _ names (HsWC _ (HsIB _ typ)) -> layoutNamesAndType Nothing names typ- InlineSig _ name (InlinePragma _ spec _arity phaseAct conlike) ->- docWrapNode lsig $ do- nameStr <- lrdrNameToTextAnn name- specStr <- specStringCompat lsig spec- let phaseStr = case phaseAct of- NeverActive -> "" -- not [] - for NOINLINE NeverActive is- -- in fact the default- AlwaysActive -> ""- ActiveBefore _ i -> "[~" ++ show i ++ "] "- ActiveAfter _ i -> "[" ++ show i ++ "] "- FinalActive -> error "brittany internal error: FinalActive"- let conlikeStr = case conlike of- FunLike -> ""- ConLike -> "CONLIKE "- docLit- $ Text.pack ("{-# " ++ specStr ++ conlikeStr ++ phaseStr)- <> nameStr- <> Text.pack " #-}"- ClassOpSig _ False names (HsIB _ typ) -> layoutNamesAndType Nothing names typ- PatSynSig _ names (HsIB _ typ) -> layoutNamesAndType (Just "pattern") names typ- _ -> briDocByExactNoComment lsig -- TODO- where- layoutNamesAndType mKeyword names typ = docWrapNode lsig $ do- let keyDoc = case mKeyword of- Just key -> [appSep . docLit $ Text.pack key]- Nothing -> []- nameStrs <- names `forM` lrdrNameToTextAnn- let nameStr = Text.intercalate (Text.pack ", ") $ nameStrs- typeDoc <- docSharedWrapper layoutType typ- hasComments <- hasAnyCommentsBelow lsig- shouldBeHanging <- mAsk- <&> _conf_layout- .> _lconfig_hangingTypeSignature- .> confUnpack- if shouldBeHanging- then docSeq $- [ appSep $ docWrapNodeRest lsig $ docSeq $ keyDoc <> [docLit nameStr]- , docSetBaseY $ docLines- [ docCols- ColTyOpPrefix- [ docLit $ Text.pack ":: "- , docAddBaseY (BrIndentSpecial 3) $ typeDoc- ]- ]- ]- else layoutLhsAndType- hasComments- (appSep . docWrapNodeRest lsig . docSeq $ keyDoc <> [docLit nameStr])- "::"- typeDoc--specStringCompat- :: MonadMultiWriter [BrittanyError] m => LSig GhcPs -> InlineSpec -> m String-specStringCompat ast = \case- NoUserInline -> mTell [ErrorUnknownNode "NoUserInline" ast] $> ""- Inline -> pure "INLINE "- Inlinable -> pure "INLINABLE "- NoInline -> pure "NOINLINE "--layoutGuardLStmt :: ToBriDoc' (Stmt GhcPs (LHsExpr GhcPs))-layoutGuardLStmt lgstmt@(L _ stmtLR) = docWrapNode lgstmt $ case stmtLR of- BodyStmt _ body _ _ -> layoutExpr body- BindStmt _ lPat expr -> do- patDoc <- docSharedWrapper layoutPat lPat- expDoc <- docSharedWrapper layoutExpr expr- docCols ColBindStmt- [ appSep $ colsWrapPat =<< patDoc- , docSeq [appSep $ docLit $ Text.pack "<-", expDoc]- ]- _ -> unknownNodeError "" lgstmt -- TODO-------------------------------------------------------------------------------------- HsBind-----------------------------------------------------------------------------------layoutBind- :: ToBriDocC- (HsBindLR GhcPs GhcPs)- (Either [BriDocNumbered] BriDocNumbered)-layoutBind lbind@(L _ bind) = case bind of- FunBind _ fId (MG _ lmatches@(L _ matches) _) [] -> do- idStr <- lrdrNameToTextAnn fId- binderDoc <- docLit $ Text.pack "="- funcPatDocs <-- docWrapNode lbind- $ docWrapNode lmatches- $ layoutPatternBind (Just idStr) binderDoc- `mapM` matches- return $ Left $ funcPatDocs- PatBind _ pat (GRHSs _ grhss whereBinds) ([], []) -> do- patDocs <- colsWrapPat =<< layoutPat pat- clauseDocs <- layoutGrhs `mapM` grhss- mWhereDocs <- layoutLocalBinds whereBinds- let mWhereArg = mWhereDocs <&> (,) (mkAnnKey lbind) -- TODO: is this the right AnnKey?- binderDoc <- docLit $ Text.pack "="- hasComments <- hasAnyCommentsBelow lbind- fmap Right $ docWrapNode lbind $ layoutPatternBindFinal Nothing- binderDoc- (Just patDocs)- clauseDocs- mWhereArg- hasComments- PatSynBind _ (PSB _ patID lpat rpat dir) -> do- fmap Right $ docWrapNode lbind $ layoutPatSynBind patID- lpat- dir- rpat- _ -> Right <$> unknownNodeError "" lbind-layoutIPBind :: ToBriDoc IPBind-layoutIPBind lipbind@(L _ bind) = case bind of- IPBind _ (Right _) _ -> error "brittany internal error: IPBind Right"- IPBind _ (Left (L _ (HsIPName name))) expr -> do- ipName <- docLit $ Text.pack $ '?' : FastString.unpackFS name- binderDoc <- docLit $ Text.pack "="- exprDoc <- layoutExpr expr- hasComments <- hasAnyCommentsBelow lipbind- layoutPatternBindFinal Nothing binderDoc (Just ipName) [([], exprDoc, expr)] Nothing hasComments---data BagBindOrSig = BagBind (LHsBindLR GhcPs GhcPs)- | BagSig (LSig GhcPs)--bindOrSigtoSrcSpan :: BagBindOrSig -> SrcSpan-bindOrSigtoSrcSpan (BagBind (L l _)) = l-bindOrSigtoSrcSpan (BagSig (L l _)) = l--layoutLocalBinds- :: ToBriDocC (HsLocalBindsLR GhcPs GhcPs) (Maybe [BriDocNumbered])-layoutLocalBinds lbinds@(L _ binds) = case binds of- -- HsValBinds (ValBindsIn lhsBindsLR []) ->- -- Just . (>>= either id return) . Data.Foldable.toList <$> mapBagM layoutBind lhsBindsLR -- TODO: fix ordering- -- x@(HsValBinds (ValBindsIn{})) ->- -- Just . (:[]) <$> unknownNodeError "HsValBinds (ValBindsIn _ (_:_))" x- HsValBinds _ (ValBinds _ bindlrs sigs) -> do- let unordered =- [ BagBind b | b <- Data.Foldable.toList bindlrs ]- ++ [ BagSig s | s <- sigs ]- ordered = List.sortOn (ExactPrint.rs . bindOrSigtoSrcSpan) unordered- docs <- docWrapNode lbinds $ join <$> ordered `forM` \case- BagBind b -> either id return <$> layoutBind b- BagSig s -> return <$> layoutSig s- return $ Just $ docs--- x@(HsValBinds (ValBindsOut _binds _lsigs)) ->- HsValBinds _ (XValBindsLR{}) -> error "brittany internal error: XValBindsLR"- HsIPBinds _ (IPBinds _ bb) ->- Just <$> mapM layoutIPBind bb- EmptyLocalBinds{} -> return $ Nothing---- TODO: we don't need the `LHsExpr GhcPs` anymore, now that there is--- parSpacing stuff.B-layoutGrhs- :: LGRHS GhcPs (LHsExpr GhcPs)- -> ToBriDocM ([BriDocNumbered], BriDocNumbered, LHsExpr GhcPs)-layoutGrhs lgrhs@(L _ (GRHS _ guards body)) = do- guardDocs <- docWrapNode lgrhs $ layoutStmt `mapM` guards- bodyDoc <- layoutExpr body- return (guardDocs, bodyDoc, body)--layoutPatternBind- :: Maybe Text- -> BriDocNumbered- -> LMatch GhcPs (LHsExpr GhcPs)- -> ToBriDocM BriDocNumbered-layoutPatternBind funId binderDoc lmatch@(L _ match) = do- let pats = m_pats match- let (GRHSs _ grhss whereBinds) = m_grhss match- patDocs <- pats `forM` \p -> fmap return $ colsWrapPat =<< layoutPat p- let isInfix = isInfixMatch match- mIdStr <- case match of- Match _ (FunRhs matchId _ _) _ _ -> Just <$> lrdrNameToTextAnn matchId- _ -> pure Nothing- let mIdStr' = fixPatternBindIdentifier match <$> mIdStr- patDoc <- docWrapNodePrior lmatch $ case (mIdStr', patDocs) of- (Just idStr, p1:p2:pr) | isInfix -> if null pr- then- docCols ColPatternsFuncInfix- [ appSep $ docForceSingleline p1- , appSep $ docLit $ idStr- , docForceSingleline p2- ]- else- docCols ColPatternsFuncInfix- ( [docCols ColPatterns- [ docParenL- , appSep $ docForceSingleline p1- , appSep $ docLit $ idStr- , docForceSingleline p2- , appSep $ docParenR- ]- ]- ++ (spacifyDocs $ docForceSingleline <$> pr)- )- (Just idStr, []) -> docLit idStr- (Just idStr, ps) ->- docCols ColPatternsFuncPrefix- $ appSep (docLit $ idStr)- : (spacifyDocs $ docForceSingleline <$> ps)- (Nothing, ps) ->- docCols ColPatterns- $ (List.intersperse docSeparator $ docForceSingleline <$> ps)- clauseDocs <- docWrapNodeRest lmatch $ layoutGrhs `mapM` grhss- mWhereDocs <- layoutLocalBinds whereBinds- let mWhereArg = mWhereDocs <&> (,) (mkAnnKey lmatch)- let alignmentToken = if null pats then Nothing else funId- hasComments <- hasAnyCommentsBelow lmatch- layoutPatternBindFinal alignmentToken- binderDoc- (Just patDoc)- clauseDocs- mWhereArg- hasComments--fixPatternBindIdentifier- :: Match GhcPs (LHsExpr GhcPs) -> Text -> Text-fixPatternBindIdentifier match idStr = go $ m_ctxt match- where- go = \case- (FunRhs _ _ SrcLazy ) -> Text.cons '~' idStr- (FunRhs _ _ SrcStrict ) -> Text.cons '!' idStr- (FunRhs _ _ NoSrcStrict) -> idStr- (StmtCtxt ctx1 ) -> goInner ctx1- _ -> idStr- -- I have really no idea if this path ever occurs, but better safe than- -- risking another "drop bangpatterns" bugs.- goInner = \case- (PatGuard ctx1) -> go ctx1- (ParStmtCtxt ctx1) -> goInner ctx1- (TransStmtCtxt ctx1) -> goInner ctx1- _ -> idStr--layoutPatternBindFinal- :: Maybe Text- -> BriDocNumbered- -> Maybe BriDocNumbered- -> [([BriDocNumbered], BriDocNumbered, LHsExpr GhcPs)]- -> Maybe (ExactPrint.AnnKey, [BriDocNumbered])- -- ^ AnnKey for the node that contains the AnnWhere position annotation- -> Bool- -> ToBriDocM BriDocNumbered-layoutPatternBindFinal alignmentToken binderDoc mPatDoc clauseDocs mWhereDocs hasComments = do- let patPartInline = case mPatDoc of- Nothing -> []- Just patDoc -> [appSep $ docForceSingleline $ return patDoc]- patPartParWrap = case mPatDoc of- Nothing -> id- Just patDoc -> docPar (return patDoc)- whereIndent <- do- shouldSpecial <- mAsk- <&> _conf_layout- .> _lconfig_indentWhereSpecial- .> confUnpack- regularIndentAmount <- mAsk- <&> _conf_layout- .> _lconfig_indentAmount- .> confUnpack- pure $ if shouldSpecial- then BrIndentSpecial (max 1 (regularIndentAmount `div` 2))- else BrIndentRegular- -- TODO: apart from this, there probably are more nodes below which could- -- be shared between alternatives.- wherePartMultiLine :: [ToBriDocM BriDocNumbered] <- case mWhereDocs of- Nothing -> return $ []- Just (annKeyWhere, [w]) -> pure . pure <$> docAlt- [ docEnsureIndent BrIndentRegular- $ docSeq- [ docLit $ Text.pack "where"- , docSeparator- , docForceSingleline $ return w- ]- , docMoveToKWDP annKeyWhere AnnWhere False- $ docEnsureIndent whereIndent- $ docLines- [ docLit $ Text.pack "where"- , docEnsureIndent whereIndent- $ docSetIndentLevel- $ docNonBottomSpacing- $ return w- ]- ]- Just (annKeyWhere, ws) ->- fmap (pure . pure)- $ docMoveToKWDP annKeyWhere AnnWhere False- $ docEnsureIndent whereIndent- $ docLines- [ docLit $ Text.pack "where"- , docEnsureIndent whereIndent- $ docSetIndentLevel- $ docNonBottomSpacing- $ docLines- $ return- <$> ws- ]- let singleLineGuardsDoc guards = appSep $ case guards of- [] -> docEmpty- [g] -> docSeq- [appSep $ docLit $ Text.pack "|", docForceSingleline $ return g]- gs -> docSeq- $ [appSep $ docLit $ Text.pack "|"]- ++ (List.intersperse docCommaSep- (docForceSingleline . return <$> gs)- )- wherePart = case mWhereDocs of- Nothing -> Just docEmpty- Just (_, [w]) -> Just $ docSeq- [ docSeparator- , appSep $ docLit $ Text.pack "where"- , docSetIndentLevel $ docForceSingleline $ return w- ]- _ -> Nothing-- indentPolicy <- mAsk- <&> _conf_layout- .> _lconfig_indentPolicy- .> confUnpack-- runFilteredAlternative $ do-- case clauseDocs of- [(guards, body, _bodyRaw)] -> do- let guardPart = singleLineGuardsDoc guards- forM_ wherePart $ \wherePart' ->- -- one-line solution- addAlternativeCond (not hasComments) $ docCols- (ColBindingLine alignmentToken)- [ docSeq (patPartInline ++ [guardPart])- , docSeq- [ appSep $ return binderDoc- , docForceSingleline $ return body- , wherePart'- ]- ]- -- one-line solution + where in next line(s)- addAlternativeCond (Data.Maybe.isJust mWhereDocs)- $ docLines- $ [ docCols- (ColBindingLine alignmentToken)- [ docSeq (patPartInline ++ [guardPart])- , docSeq- [ appSep $ return binderDoc- , docForceParSpacing $ docAddBaseY BrIndentRegular $ return body- ]- ]- ]- ++ wherePartMultiLine- -- two-line solution + where in next line(s)- addAlternative- $ docLines- $ [ docForceSingleline- $ docSeq (patPartInline ++ [guardPart, return binderDoc])- , docEnsureIndent BrIndentRegular $ docForceSingleline $ return body- ]- ++ wherePartMultiLine- -- pattern and exactly one clause in single line, body as par;- -- where in following lines- addAlternative- $ docLines- $ [ docCols- (ColBindingLine alignmentToken)- [ docSeq (patPartInline ++ [guardPart])- , docSeq- [ appSep $ return binderDoc- , docForceParSpacing $ docAddBaseY BrIndentRegular $ return body- ]- ]- ]- -- , lineMod $ docAlt- -- [ docSetBaseY $ return body- -- , docAddBaseY BrIndentRegular $ return body- -- ]- ++ wherePartMultiLine- -- pattern and exactly one clause in single line, body in new line.- addAlternative- $ docLines- $ [ docSeq (patPartInline ++ [guardPart, return binderDoc])- , docNonBottomSpacing- $ docEnsureIndent BrIndentRegular- $ docAddBaseY BrIndentRegular- $ return body- ]- ++ wherePartMultiLine-- _ -> return () -- no alternatives exclusively when `length clauseDocs /= 1`-- case mPatDoc of- Nothing -> return ()- Just patDoc ->- -- multiple clauses added in-paragraph, each in a single line- -- example: foo | bar = baz- -- | lll = asd- addAlternativeCond (indentPolicy == IndentPolicyFree)- $ docLines- $ [ docSeq- [ appSep $ docForceSingleline $ return patDoc- , docSetBaseY- $ docLines- $ clauseDocs- <&> \(guardDocs, bodyDoc, _) -> do- let guardPart = singleLineGuardsDoc guardDocs- -- the docForceSingleline might seems superflous, but it- -- helps the alternative resolving impl.- docForceSingleline $ docCols- ColGuardedBody- [ guardPart- , docSeq- [ appSep $ return binderDoc- , docForceSingleline $ return bodyDoc- -- i am not sure if there is a benefit to using- -- docForceParSpacing additionally here:- -- , docAddBaseY BrIndentRegular $ return bodyDoc- ]- ]- ]- ]- ++ wherePartMultiLine- -- multiple clauses, each in a separate, single line- addAlternative- $ docLines- $ [ docAddBaseY BrIndentRegular- $ patPartParWrap- $ docLines- $ map docSetBaseY- $ clauseDocs- <&> \(guardDocs, bodyDoc, _) -> do- let guardPart = singleLineGuardsDoc guardDocs- -- the docForceSingleline might seems superflous, but it- -- helps the alternative resolving impl.- docForceSingleline $ docCols- ColGuardedBody- [ guardPart- , docSeq- [ appSep $ return binderDoc- , docForceSingleline $ return bodyDoc- -- i am not sure if there is a benefit to using- -- docForceParSpacing additionally here:- -- , docAddBaseY BrIndentRegular $ return bodyDoc- ]- ]- ]- ++ wherePartMultiLine- -- multiple clauses, each with the guard(s) in a single line, body- -- as a paragraph- addAlternative- $ docLines- $ [ docAddBaseY BrIndentRegular- $ patPartParWrap- $ docLines- $ map docSetBaseY- $ clauseDocs- <&> \(guardDocs, bodyDoc, _) ->- docSeq- $ ( case guardDocs of- [] -> []- [g] ->- [ docForceSingleline- $ docSeq [appSep $ docLit $ Text.pack "|", return g]- ]- gs ->- [ docForceSingleline- $ docSeq- $ [appSep $ docLit $ Text.pack "|"]- ++ List.intersperse docCommaSep (return <$> gs)- ]- )- ++ [ docSeparator- , docCols- ColOpPrefix- [ appSep $ return binderDoc- , docAddBaseY BrIndentRegular- $ docForceParSpacing- $ return bodyDoc- ]- ]- ]- ++ wherePartMultiLine- -- multiple clauses, each with the guard(s) in a single line, body- -- in a new line as a paragraph- addAlternative- $ docLines- $ [ docAddBaseY BrIndentRegular- $ patPartParWrap- $ docLines- $ map docSetBaseY- $ clauseDocs- >>= \(guardDocs, bodyDoc, _) ->- ( case guardDocs of- [] -> []- [g] ->- [ docForceSingleline- $ docSeq [appSep $ docLit $ Text.pack "|", return g]- ]- gs ->- [ docForceSingleline- $ docSeq- $ [appSep $ docLit $ Text.pack "|"]- ++ List.intersperse docCommaSep (return <$> gs)- ]- )- ++ [ docCols- ColOpPrefix- [ appSep $ return binderDoc- , docAddBaseY BrIndentRegular- $ docForceParSpacing- $ return bodyDoc- ]- ]- ]- ++ wherePartMultiLine- -- conservative approach: everything starts on the left.- addAlternative- $ docLines- $ [ docAddBaseY BrIndentRegular- $ patPartParWrap- $ docLines- $ map docSetBaseY- $ clauseDocs- >>= \(guardDocs, bodyDoc, _) ->- ( case guardDocs of- [] -> []- [g] ->- [docSeq [appSep $ docLit $ Text.pack "|", return g]]- (g1:gr) ->- ( docSeq [appSep $ docLit $ Text.pack "|", return g1]- : ( gr- <&> \g ->- docSeq- [appSep $ docLit $ Text.pack ",", return g]- )- )- )- ++ [ docCols- ColOpPrefix- [ appSep $ return binderDoc- , docAddBaseY BrIndentRegular $ return bodyDoc- ]- ]- ]- ++ wherePartMultiLine---- | Layout a pattern synonym binding-layoutPatSynBind- :: Located (IdP GhcPs)- -> HsPatSynDetails (Located (IdP GhcPs))- -> HsPatSynDir GhcPs- -> LPat GhcPs- -> ToBriDocM BriDocNumbered-layoutPatSynBind name patSynDetails patDir rpat = do- let patDoc = docLit $ Text.pack "pattern"- binderDoc = case patDir of- ImplicitBidirectional -> docLit $ Text.pack "="- _ -> docLit $ Text.pack "<-"- body = colsWrapPat =<< layoutPat rpat- whereDoc = docLit $ Text.pack "where"- mWhereDocs <- layoutPatSynWhere patDir- headDoc <- fmap pure $ docSeq $- [ patDoc- , docSeparator- , layoutLPatSyn name patSynDetails- , docSeparator- , binderDoc- ]- runFilteredAlternative $ do- addAlternative $- -- pattern .. where- -- ..- -- ..- docAddBaseY BrIndentRegular $ docSeq- ( [headDoc, docSeparator, body]- ++ case mWhereDocs of- Just ds -> [docSeparator, docPar whereDoc (docLines ds)]- Nothing -> []- )- addAlternative $- -- pattern .. =- -- ..- -- pattern .. <-- -- .. where- -- ..- -- ..- docAddBaseY BrIndentRegular $ docPar- headDoc- (case mWhereDocs of- Nothing -> body- Just ds -> docLines ([ docSeq [body, docSeparator, whereDoc] ] ++ ds)- )---- | Helper method for the left hand side of a pattern synonym-layoutLPatSyn- :: Located (IdP GhcPs)- -> HsPatSynDetails (Located (IdP GhcPs))- -> ToBriDocM BriDocNumbered-layoutLPatSyn name (PrefixCon vars) = do- docName <- lrdrNameToTextAnn name- names <- mapM lrdrNameToTextAnn vars- docSeq . fmap appSep $ docLit docName : (docLit <$> names)-layoutLPatSyn name (InfixCon left right) = do- leftDoc <- lrdrNameToTextAnn left- docName <- lrdrNameToTextAnn name- rightDoc <- lrdrNameToTextAnn right- docSeq . fmap (appSep . docLit) $ [leftDoc, docName, rightDoc]-layoutLPatSyn name (RecCon recArgs) = do- docName <- lrdrNameToTextAnn name- args <- mapM (lrdrNameToTextAnn . recordPatSynSelectorId) recArgs- docSeq . fmap docLit- $ [docName, Text.pack " { " ]- <> intersperse (Text.pack ", ") args- <> [Text.pack " }"]---- | Helper method to get the where clause from of explicitly bidirectional--- pattern synonyms-layoutPatSynWhere :: HsPatSynDir GhcPs -> ToBriDocM (Maybe [ToBriDocM BriDocNumbered])-layoutPatSynWhere hs = case hs of- ExplicitBidirectional (MG _ (L _ lbinds) _) -> do- binderDoc <- docLit $ Text.pack "="- Just <$> mapM (docSharedWrapper $ layoutPatternBind Nothing binderDoc) lbinds- _ -> pure Nothing------------------------------------------------------------------------------------- TyClDecl-----------------------------------------------------------------------------------layoutTyCl :: ToBriDoc TyClDecl-layoutTyCl ltycl@(L _loc tycl) = case tycl of- SynDecl _ name vars fixity typ -> do- let isInfix = case fixity of- Prefix -> False- Infix -> True- -- hasTrailingParen <- hasAnnKeywordComment ltycl AnnCloseP- -- let parenWrapper = if hasTrailingParen- -- then appSep . docWrapNodeRest ltycl- -- else id- let wrapNodeRest = docWrapNodeRest ltycl- docWrapNodePrior ltycl- $ layoutSynDecl isInfix wrapNodeRest name (hsq_explicit vars) typ- DataDecl _ext name tyVars _ dataDefn ->- layoutDataDecl ltycl name tyVars dataDefn- _ -> briDocByExactNoComment ltycl--layoutSynDecl- :: Bool- -> (ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered)- -> Located (IdP GhcPs)- -> [LHsTyVarBndr () GhcPs]- -> LHsType GhcPs- -> ToBriDocM BriDocNumbered-layoutSynDecl isInfix wrapNodeRest name vars typ = do- nameStr <- lrdrNameToTextAnn name- let- lhs = appSep . wrapNodeRest $ if isInfix- then do- let (a : b : rest) = vars- hasOwnParens <- hasAnnKeywordComment a AnnOpenP- -- This isn't quite right, but does give syntactically valid results- let needsParens = not (null rest) || hasOwnParens- docSeq- $ [ docLit $ Text.pack "type"- , docSeparator- ]- ++ [ docParenL | needsParens ]- ++ [ layoutTyVarBndr False a- , docSeparator- , docLit nameStr- , docSeparator- , layoutTyVarBndr False b- ]- ++ [ docParenR | needsParens ]- ++ fmap (layoutTyVarBndr True) rest- else- docSeq- $ [ docLit $ Text.pack "type"- , docSeparator- , docWrapNode name $ docLit nameStr- ]- ++ fmap (layoutTyVarBndr True) vars- sharedLhs <- docSharedWrapper id lhs- typeDoc <- docSharedWrapper layoutType typ- hasComments <- hasAnyCommentsConnected typ- layoutLhsAndType hasComments sharedLhs "=" typeDoc--layoutTyVarBndr :: Bool -> ToBriDoc (HsTyVarBndr ())-layoutTyVarBndr needsSep lbndr@(L _ bndr) = do- docWrapNodePrior lbndr $ case bndr of- UserTyVar _ _ name -> do- nameStr <- lrdrNameToTextAnn name- docSeq $ [docSeparator | needsSep] ++ [docLit nameStr]- KindedTyVar _ _ name kind -> do- nameStr <- lrdrNameToTextAnn name- docSeq- $ [ docSeparator | needsSep ]- ++ [ docLit $ Text.pack "("- , appSep $ docLit nameStr- , appSep . docLit $ Text.pack "::"- , docForceSingleline $ layoutType kind- , docLit $ Text.pack ")"- ]-------------------------------------------------------------------------------------- TyFamInstDecl-------------------------------------------------------------------------------------layoutTyFamInstDecl- :: Data.Data.Data a- => Bool- -> Located a- -> TyFamInstDecl GhcPs- -> ToBriDocM BriDocNumbered-layoutTyFamInstDecl inClass outerNode tfid = do- let- FamEqn _ name bndrsMay pats _fixity typ = hsib_body $ tfid_eqn tfid- -- bndrsMay isJust e.g. with- -- type instance forall a . MyType (Maybe a) = Either () a- innerNode = outerNode- docWrapNodePrior outerNode $ do- nameStr <- lrdrNameToTextAnn name- needsParens <- hasAnnKeyword outerNode AnnOpenP- let- instanceDoc = if inClass- then docLit $ Text.pack "type"- else docSeq- [appSep . docLit $ Text.pack "type", docLit $ Text.pack "instance"]- makeForallDoc :: [LHsTyVarBndr () GhcPs] -> ToBriDocM BriDocNumbered- makeForallDoc bndrs = do- bndrDocs <- layoutTyVarBndrs bndrs- docSeq- ( [docLit (Text.pack "forall")]- ++ processTyVarBndrsSingleline bndrDocs- )- lhs =- docWrapNode innerNode- . docSeq- $ [appSep instanceDoc]- ++ [ makeForallDoc foralls | Just foralls <- [bndrsMay] ]- ++ [ docParenL | needsParens ]- ++ [appSep $ docWrapNode name $ docLit nameStr]- ++ intersperse docSeparator (layoutHsTyPats pats)- ++ [ docParenR | needsParens ]- hasComments <- (||)- <$> hasAnyRegularCommentsConnected outerNode- <*> hasAnyRegularCommentsRest innerNode- typeDoc <- docSharedWrapper layoutType typ- layoutLhsAndType hasComments lhs "=" typeDoc---layoutHsTyPats :: [HsArg (LHsType GhcPs) (LHsKind GhcPs)] -> [ToBriDocM BriDocNumbered]-layoutHsTyPats pats = pats <&> \case- HsValArg tm -> layoutType tm- HsTypeArg _l ty -> docSeq [docLit $ Text.pack "@", layoutType ty]- -- we ignore the SourceLoc here.. this LPat not being (L _ Pat{}) change- -- is a bit strange. Hopefully this does not ignore any important- -- annotations.- HsArgPar _l -> error "brittany internal error: HsArgPar{}"------------------------------------------------------------------------------------- ClsInstDecl------------------------------------------------------------------------------------- | Layout an @instance@ declaration------ Layout signatures and bindings using the corresponding layouters from the--- top-level. Layout the instance head, type family instances, and data family--- instances using ExactPrint.-layoutClsInst :: ToBriDoc ClsInstDecl-layoutClsInst lcid@(L _ cid) = docLines- [ layoutInstanceHead- , docEnsureIndent BrIndentRegular- $ docSetIndentLevel- $ docSortedLines- $ fmap layoutAndLocateSig (cid_sigs cid)- ++ fmap layoutAndLocateBind (bagToList $ cid_binds cid)- ++ fmap layoutAndLocateTyFamInsts (cid_tyfam_insts cid)- ++ fmap layoutAndLocateDataFamInsts (cid_datafam_insts cid)- ]- where- layoutInstanceHead :: ToBriDocM BriDocNumbered- layoutInstanceHead =- briDocByExactNoComment- $ InstD NoExtField- . ClsInstD NoExtField- . removeChildren- <$> lcid-- removeChildren :: ClsInstDecl GhcPs -> ClsInstDecl GhcPs- removeChildren c = c- { cid_binds = emptyBag- , cid_sigs = []- , cid_tyfam_insts = []- , cid_datafam_insts = []- }-- -- | Like 'docLines', but sorts the lines based on location- docSortedLines- :: [ToBriDocM (Located BriDocNumbered)] -> ToBriDocM BriDocNumbered- docSortedLines l =- allocateNode . BDFLines . fmap unLoc . List.sortOn (ExactPrint.rs . getLoc) =<< sequence l-- layoutAndLocateSig :: ToBriDocC (Sig GhcPs) (Located BriDocNumbered)- layoutAndLocateSig lsig@(L loc _) = L loc <$> layoutSig lsig-- layoutAndLocateBind :: ToBriDocC (HsBind GhcPs) (Located BriDocNumbered)- layoutAndLocateBind lbind@(L loc _) =- L loc <$> (joinBinds =<< layoutBind lbind)-- joinBinds- :: Either [BriDocNumbered] BriDocNumbered -> ToBriDocM BriDocNumbered- joinBinds = \case- Left ns -> docLines $ return <$> ns- Right n -> return n-- layoutAndLocateTyFamInsts- :: ToBriDocC (TyFamInstDecl GhcPs) (Located BriDocNumbered)- layoutAndLocateTyFamInsts ltfid@(L loc tfid) =- L loc <$> layoutTyFamInstDecl True ltfid tfid-- layoutAndLocateDataFamInsts- :: ToBriDocC (DataFamInstDecl GhcPs) (Located BriDocNumbered)- layoutAndLocateDataFamInsts ldfid@(L loc _) =- L loc <$> layoutDataFamInstDecl ldfid-- -- | Send to ExactPrint then remove unecessary whitespace- layoutDataFamInstDecl :: ToBriDoc DataFamInstDecl- layoutDataFamInstDecl ldfid =- fmap stripWhitespace <$> briDocByExactNoComment ldfid-- -- | ExactPrint adds indentation/newlines to @data@/@type@ declarations- stripWhitespace :: BriDocF f -> BriDocF f- stripWhitespace (BDFExternal ann anns b t) =- BDFExternal ann anns b $ stripWhitespace' t- stripWhitespace b = b-- -- | This fixes two issues of output coming from Exactprinting- -- associated (data) type decls. Firstly we place the output into docLines,- -- so one newline coming from Exactprint is superfluous, so we drop the- -- first (empty) line. The second issue is Exactprint indents the first- -- member in a strange fashion:- --- -- input:- --- -- > instance MyClass Int where- -- > -- | This data is very important- -- > data MyData = IntData- -- > { intData :: String- -- > , intData2 :: Int- -- > }- --- -- output of just exactprinting the associated data type syntax node- --- -- >- -- > -- | This data is very important- -- > data MyData = IntData- -- > { intData :: String- -- > , intData2 :: Int- -- > }- --- -- To fix this, we strip whitespace from the start of the comments and the- -- first line of the declaration, stopping when we see "data" or "type" at- -- the start of a line. I.e., this function yields- --- -- > -- | This data is very important- -- > data MyData = IntData- -- > { intData :: String- -- > , intData2 :: Int- -- > }- --- -- Downside apart from being a hacky and brittle fix is that this removes- -- possible additional indentation from comments before the first member.- --- -- But the whole thing is just a temporary measure until brittany learns- -- to layout data/type decls.- stripWhitespace' :: Text -> Text- stripWhitespace' t =- Text.intercalate (Text.pack "\n") $ go $ List.drop 1 $ Text.lines t- where- go [] = []- go (line1 : lineR) = case Text.stripStart line1 of- st | isTypeOrData st -> st : lineR- | otherwise -> st : go lineR- isTypeOrData t' =- (Text.pack "type" `Text.isPrefixOf` t')- || (Text.pack "newtype" `Text.isPrefixOf` t')- || (Text.pack "data" `Text.isPrefixOf` t')-------------------------------------------------------------------------------------- Common Helpers-----------------------------------------------------------------------------------layoutLhsAndType- :: Bool- -> ToBriDocM BriDocNumbered- -> String- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered-layoutLhsAndType hasComments lhs sep typeDoc = do- runFilteredAlternative $ do- -- (separators probably are "=" or "::")- -- lhs = type- -- lhs :: type- addAlternativeCond (not hasComments) $ docSeq- [lhs, docSeparator, docLitS sep, docSeparator, docForceSingleline typeDoc]- -- lhs- -- :: typeA- -- -> typeB- -- lhs- -- = typeA- -- -> typeB- addAlternative $ docAddBaseY BrIndentRegular $ docPar lhs $ docCols- ColTyOpPrefix- [ appSep $ docLitS sep- , docAddBaseY (BrIndentSpecial (length sep + 1)) typeDoc- ]
− source/library/Language/Haskell/Brittany/Internal/Layouters/Expr.hs
@@ -1,1078 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MonadComprehensions #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Expr where--import qualified Data.Data-import qualified Data.Semigroup as Semigroup-import qualified Data.Sequence as Seq-import qualified Data.Text as Text-import GHC (AnnKeywordId(..), GenLocated(L), RdrName(..), SrcSpan)-import qualified GHC.Data.FastString as FastString-import GHC.Hs-import qualified GHC.OldList as List-import GHC.Types.Basic-import GHC.Types.Name-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.Decl-import Language.Haskell.Brittany.Internal.Layouters.Pattern-import Language.Haskell.Brittany.Internal.Layouters.Stmt-import Language.Haskell.Brittany.Internal.Layouters.Type-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils----layoutExpr :: ToBriDoc HsExpr-layoutExpr lexpr@(L _ expr) = do- indentPolicy <- mAsk- <&> _conf_layout- .> _lconfig_indentPolicy- .> confUnpack- let allowFreeIndent = indentPolicy == IndentPolicyFree- docWrapNode lexpr $ case expr of- HsVar _ vname -> do- docLit =<< lrdrNameToTextAnn vname- HsUnboundVar _ oname ->- docLit $ Text.pack $ occNameString oname- HsRecFld{} -> do- -- TODO- briDocByExactInlineOnly "HsRecFld" lexpr- HsOverLabel _ext _reboundFromLabel name ->- let label = FastString.unpackFS name- in docLit . Text.pack $ '#' : label- HsIPVar _ext (HsIPName name) ->- let label = FastString.unpackFS name- in docLit . Text.pack $ '?' : label- HsOverLit _ olit -> do- allocateNode $ overLitValBriDoc $ ol_val olit- HsLit _ lit -> do- allocateNode $ litBriDoc lit- HsLam _ (MG _ (L _ [lmatch@(L _ match)]) _)- | pats <- m_pats match- , GRHSs _ [lgrhs] llocals <- m_grhss match- , L _ EmptyLocalBinds {} <- llocals- , L _ (GRHS _ [] body) <- lgrhs- -> do- patDocs <- zip (True : repeat False) pats `forM` \(isFirst, p) ->- fmap return $ do- -- this code could be as simple as `colsWrapPat =<< layoutPat p`- -- if it was not for the following two cases:- -- \ !x -> x- -- \ ~x -> x- -- These make it necessary to special-case an additional separator.- -- (TODO: we create a BDCols here, but then make it ineffective- -- by wrapping it in docSeq below. We _could_ add alignments for- -- stuff like lists-of-lambdas. Nothing terribly important..)- let shouldPrefixSeparator = case p of- L _ LazyPat{} -> isFirst- L _ BangPat{} -> isFirst- _ -> False- patDocSeq <- layoutPat p- fixed <- case Seq.viewl patDocSeq of- p1 Seq.:< pr | shouldPrefixSeparator -> do- p1' <- docSeq [docSeparator, pure p1]- pure (p1' Seq.<| pr)- _ -> pure patDocSeq- colsWrapPat fixed- bodyDoc <- docAddBaseY BrIndentRegular <$> docSharedWrapper layoutExpr body- let funcPatternPartLine =- docCols ColCasePattern- (patDocs <&> (\p -> docSeq [docForceSingleline p, docSeparator]))- docAlt- [ -- single line- docSeq- [ docLit $ Text.pack "\\"- , docWrapNode lmatch $ docForceSingleline funcPatternPartLine- , appSep $ docLit $ Text.pack "->"- , docWrapNode lgrhs $ docForceSingleline bodyDoc- ]- -- double line- , docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docSeq- [ docLit $ Text.pack "\\"- , docWrapNode lmatch $ appSep $ docForceSingleline funcPatternPartLine- , docLit $ Text.pack "->"- ])- (docWrapNode lgrhs $ docForceSingleline bodyDoc)- -- wrapped par spacing- , docSetParSpacing- $ docSeq- [ docLit $ Text.pack "\\"- , docWrapNode lmatch $ docForceSingleline funcPatternPartLine- , appSep $ docLit $ Text.pack "->"- , docWrapNode lgrhs $ docForceParSpacing bodyDoc- ]- -- conservative- , docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docSeq- [ docLit $ Text.pack "\\"- , docWrapNode lmatch $ appSep $ docForceSingleline funcPatternPartLine- , docLit $ Text.pack "->"- ])- (docWrapNode lgrhs $ docNonBottomSpacing bodyDoc)- ]- HsLam{} ->- unknownNodeError "HsLam too complex" lexpr- HsLamCase _ (MG _ (L _ []) _) -> do- docSetParSpacing $ docAddBaseY BrIndentRegular $- (docLit $ Text.pack "\\case {}")- HsLamCase _ (MG _ lmatches@(L _ matches) _) -> do- binderDoc <- docLit $ Text.pack "->"- funcPatDocs <- docWrapNode lmatches- $ layoutPatternBind Nothing binderDoc `mapM` matches- docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar- (docLit $ Text.pack "\\case")- (docSetBaseAndIndent $ docNonBottomSpacing $ docLines $ return <$> funcPatDocs)- HsApp _ exp1@(L _ HsApp{}) exp2 -> do- let gather :: [LHsExpr GhcPs] -> LHsExpr GhcPs -> (LHsExpr GhcPs, [LHsExpr GhcPs])- gather list = \case- L _ (HsApp _ l r) -> gather (r:list) l- x -> (x, list)- let (headE, paramEs) = gather [exp2] exp1- let colsOrSequence = case headE of- L _ (HsVar _ (L _ (Unqual occname))) ->- docCols (ColApp $ Text.pack $ occNameString occname)- _ -> docSeq- headDoc <- docSharedWrapper layoutExpr headE- paramDocs <- docSharedWrapper layoutExpr `mapM` paramEs- hasComments <- hasAnyCommentsConnected exp2- runFilteredAlternative $ do- -- foo x y- addAlternativeCond (not hasComments)- $ colsOrSequence- $ appSep (docForceSingleline headDoc)- : spacifyDocs (docForceSingleline <$> paramDocs)- -- foo x- -- y- addAlternativeCond allowFreeIndent- $ docSeq- [ appSep (docForceSingleline headDoc)- , docSetBaseY- $ docAddBaseY BrIndentRegular- $ docLines- $ docForceSingleline <$> paramDocs- ]- -- foo- -- x- -- y- addAlternative- $ docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docForceSingleline headDoc)- ( docNonBottomSpacing- $ docLines paramDocs- )- -- ( multi- -- line- -- function- -- )- -- x- -- y- addAlternative- $ docAddBaseY BrIndentRegular- $ docPar- headDoc- ( docNonBottomSpacing- $ docLines paramDocs- )- HsApp _ exp1 exp2 -> do- -- TODO: if expDoc1 is some literal, we may want to create a docCols here.- expDoc1 <- docSharedWrapper layoutExpr exp1- expDoc2 <- docSharedWrapper layoutExpr exp2- docAlt- [ -- func arg- docSeq [appSep $ docForceSingleline expDoc1, docForceSingleline expDoc2]- , -- func argline1- -- arglines- -- e.g.- -- func Abc- -- { member1 = True- -- , member2 = 13- -- }- docSetParSpacing -- this is most likely superfluous because- -- this is a sequence of a one-line and a par-space- -- anyways, so it is _always_ par-spaced.- $ docAddBaseY BrIndentRegular- $ docSeq- [ appSep $ docForceSingleline expDoc1- , docForceParSpacing expDoc2- ]- , -- func- -- arg- docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docForceSingleline expDoc1)- (docNonBottomSpacing expDoc2)- , -- fu- -- nc- -- ar- -- gument- docAddBaseY BrIndentRegular- $ docPar- expDoc1- expDoc2- ]- HsAppType _ exp1 (HsWC _ ty1) -> do- t <- docSharedWrapper layoutType ty1- e <- docSharedWrapper layoutExpr exp1- docAlt- [ docSeq- [ docForceSingleline e- , docSeparator- , docLit $ Text.pack "@"- , docForceSingleline t- ]- , docPar- e- (docSeq [docLit $ Text.pack "@", t ])- ]- OpApp _ expLeft@(L _ OpApp{}) expOp expRight -> do- let gather :: [(LHsExpr GhcPs, LHsExpr GhcPs)] -> LHsExpr GhcPs -> (LHsExpr GhcPs, [(LHsExpr GhcPs, LHsExpr GhcPs)])- gather opExprList = \case- (L _ (OpApp _ l1 op1 r1)) -> gather ((op1, r1): opExprList) l1- final -> (final, opExprList)- (leftOperand, appList) = gather [] expLeft- leftOperandDoc <- docSharedWrapper layoutExpr leftOperand- appListDocs <- appList `forM` \(x,y) -> [ (xD, yD)- | xD <- docSharedWrapper layoutExpr x- , yD <- docSharedWrapper layoutExpr y- ]- opLastDoc <- docSharedWrapper layoutExpr expOp- expLastDoc <- docSharedWrapper layoutExpr expRight- allowSinglelinePar <- do- hasComLeft <- hasAnyCommentsConnected expLeft- hasComOp <- hasAnyCommentsConnected expOp- pure $ not hasComLeft && not hasComOp- let allowPar = case (expOp, expRight) of- (L _ (HsVar _ (L _ (Unqual occname))), _)- | occNameString occname == "$" -> True- (_, L _ (HsApp _ _ (L _ HsVar{}))) -> False- _ -> True- runFilteredAlternative $ do- -- > one + two + three- -- or- -- > one + two + case x of- -- > _ -> three- addAlternativeCond allowSinglelinePar- $ docSeq- [ appSep $ docForceSingleline leftOperandDoc- , docSeq- $ appListDocs <&> \(od, ed) -> docSeq- [ appSep $ docForceSingleline od- , appSep $ docForceSingleline ed- ]- , appSep $ docForceSingleline opLastDoc- , (if allowPar then docForceParSpacing else docForceSingleline)- expLastDoc- ]- -- this case rather leads to some unfortunate layouting than to anything- -- useful; disabling for now. (it interfers with cols stuff.)- -- addAlternative- -- $ docSetBaseY- -- $ docPar- -- leftOperandDoc- -- ( docLines- -- $ (appListDocs <&> \(od, ed) -> docCols ColOpPrefix [appSep od, docSetBaseY ed])- -- ++ [docCols ColOpPrefix [appSep opLastDoc, docSetBaseY expLastDoc]]- -- )- -- > one- -- > + two- -- > + three- addAlternative $- docPar- leftOperandDoc- ( docLines- $ (appListDocs <&> \(od, ed) -> docCols ColOpPrefix [appSep od, docSetBaseY ed])- ++ [docCols ColOpPrefix [appSep opLastDoc, docSetBaseY expLastDoc]]- )- OpApp _ expLeft expOp expRight -> do- expDocLeft <- docSharedWrapper layoutExpr expLeft- expDocOp <- docSharedWrapper layoutExpr expOp- expDocRight <- docSharedWrapper layoutExpr expRight- let allowPar = case (expOp, expRight) of- (L _ (HsVar _ (L _ (Unqual occname))), _)- | occNameString occname == "$" -> True- (_, L _ (HsApp _ _ (L _ HsVar{}))) -> False- _ -> True- let leftIsDoBlock = case expLeft of- L _ HsDo{} -> True- _ -> False- runFilteredAlternative $ do- -- one-line- addAlternative- $ docSeq- [ appSep $ docForceSingleline expDocLeft- , appSep $ docForceSingleline expDocOp- , docForceSingleline expDocRight- ]- -- -- line + freely indented block for right expression- -- addAlternative- -- $ docSeq- -- [ appSep $ docForceSingleline expDocLeft- -- , appSep $ docForceSingleline expDocOp- -- , docSetBaseY $ docAddBaseY BrIndentRegular expDocRight- -- ]- -- two-line- addAlternative $ do- let- expDocOpAndRight = docForceSingleline- $ docCols ColOpPrefix [appSep $ expDocOp, docSetBaseY expDocRight]- if leftIsDoBlock- then docLines [expDocLeft, expDocOpAndRight]- else docAddBaseY BrIndentRegular $ docPar expDocLeft expDocOpAndRight- -- TODO: in both cases, we don't force expDocLeft to be- -- single-line, which has certain.. interesting consequences.- -- At least, the "two-line" label is not entirely- -- accurate.- -- one-line + par- addAlternativeCond allowPar- $ docSeq- [ appSep $ docForceSingleline expDocLeft- , appSep $ docForceSingleline expDocOp- , docForceParSpacing expDocRight- ]- -- more lines- addAlternative $ do- let expDocOpAndRight =- docCols ColOpPrefix [appSep expDocOp, docSetBaseY expDocRight]- if leftIsDoBlock- then docLines [expDocLeft, expDocOpAndRight]- else docAddBaseY BrIndentRegular- $ docPar expDocLeft expDocOpAndRight- NegApp _ op _ -> do- opDoc <- docSharedWrapper layoutExpr op- docSeq [ docLit $ Text.pack "-"- , opDoc- ]- HsPar _ innerExp -> do- innerExpDoc <- docSharedWrapper (docWrapNode lexpr . layoutExpr) innerExp- docAlt- [ docSeq- [ docLit $ Text.pack "("- , docForceSingleline innerExpDoc- , docLit $ Text.pack ")"- ]- , docSetBaseY $ docLines- [ docCols ColOpPrefix- [ docLit $ Text.pack "("- , docAddBaseY (BrIndentSpecial 2) innerExpDoc- ]- , docLit $ Text.pack ")"- ]- ]- SectionL _ left op -> do -- TODO: add to testsuite- leftDoc <- docSharedWrapper layoutExpr left- opDoc <- docSharedWrapper layoutExpr op- docSeq [leftDoc, docSeparator, opDoc]- SectionR _ op right -> do -- TODO: add to testsuite- opDoc <- docSharedWrapper layoutExpr op- rightDoc <- docSharedWrapper layoutExpr right- docSeq [opDoc, docSeparator, rightDoc]- ExplicitTuple _ args boxity -> do- let argExprs = args <&> \arg -> case arg of- (L _ (Present _ e)) -> (arg, Just e);- (L _ (Missing NoExtField)) -> (arg, Nothing)- argDocs <- forM argExprs- $ docSharedWrapper- $ \(arg, exprM) -> docWrapNode arg $ maybe docEmpty layoutExpr exprM- hasComments <- orM- ( hasCommentsBetween lexpr AnnOpenP AnnCloseP- : map hasAnyCommentsBelow args- )- let (openLit, closeLit) = case boxity of- Boxed -> (docLit $ Text.pack "(", docLit $ Text.pack ")")- Unboxed -> (docParenHashLSep, docParenHashRSep)- case splitFirstLast argDocs of- FirstLastEmpty -> docSeq- [ openLit- , docNodeAnnKW lexpr (Just AnnOpenP) closeLit- ]- FirstLastSingleton e -> docAlt- [ docCols ColTuple- [ openLit- , docNodeAnnKW lexpr (Just AnnOpenP) $ docForceSingleline e- , closeLit- ]- , docSetBaseY $ docLines- [ docSeq- [ openLit- , docNodeAnnKW lexpr (Just AnnOpenP) $ docForceSingleline e- ]- , closeLit- ]- ]- FirstLast e1 ems eN -> runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docCols ColTuple- $ [docSeq [openLit, docForceSingleline e1]]- ++ (ems <&> \e -> docSeq [docCommaSep, docForceSingleline e])- ++ [docSeq [docCommaSep, docNodeAnnKW lexpr (Just AnnOpenP) (docForceSingleline eN), closeLit]]- addAlternative $- let- start = docCols ColTuples- [appSep openLit, e1]- linesM = ems <&> \d ->- docCols ColTuples [docCommaSep, d]- lineN = docCols ColTuples [docCommaSep, docNodeAnnKW lexpr (Just AnnOpenP) eN]- end = closeLit- in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN, end]- HsCase _ cExp (MG _ (L _ []) _) -> do- cExpDoc <- docSharedWrapper layoutExpr cExp- docAlt- [ docAddBaseY BrIndentRegular- $ docSeq- [ appSep $ docLit $ Text.pack "case"- , appSep $ docForceSingleline cExpDoc- , docLit $ Text.pack "of {}"- ]- , docPar- ( docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "case") cExpDoc- )- (docLit $ Text.pack "of {}")- ]- HsCase _ cExp (MG _ lmatches@(L _ matches) _) -> do- cExpDoc <- docSharedWrapper layoutExpr cExp- binderDoc <- docLit $ Text.pack "->"- funcPatDocs <- docWrapNode lmatches- $ layoutPatternBind Nothing binderDoc `mapM` matches- docAlt- [ docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- ( docSeq- [ appSep $ docLit $ Text.pack "case"- , appSep $ docForceSingleline cExpDoc- , docLit $ Text.pack "of"- ])- (docSetBaseAndIndent $ docNonBottomSpacing $ docLines $ return <$> funcPatDocs)- , docPar- ( docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "case") cExpDoc- )- ( docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "of")- (docSetBaseAndIndent $ docNonBottomSpacing $ docLines $ return <$> funcPatDocs)- )- ]- HsIf _ ifExpr thenExpr elseExpr -> do- ifExprDoc <- docSharedWrapper layoutExpr ifExpr- thenExprDoc <- docSharedWrapper layoutExpr thenExpr- elseExprDoc <- docSharedWrapper layoutExpr elseExpr- hasComments <- hasAnyCommentsBelow lexpr- let maySpecialIndent =- case indentPolicy of- IndentPolicyLeft -> BrIndentRegular- IndentPolicyMultiple -> BrIndentRegular- IndentPolicyFree -> BrIndentSpecial 3- -- TODO: some of the alternatives (especially last and last-but-one)- -- overlap.- docSetIndentLevel $ runFilteredAlternative $ do- -- if _ then _ else _- addAlternativeCond (not hasComments)- $ docSeq- [ appSep $ docLit $ Text.pack "if"- , appSep $ docForceSingleline ifExprDoc- , appSep $ docLit $ Text.pack "then"- , appSep $ docForceSingleline thenExprDoc- , appSep $ docLit $ Text.pack "else"- , docForceSingleline elseExprDoc- ]- -- either- -- if expr- -- then foo- -- bar- -- else foo- -- bar- -- or- -- if expr- -- then- -- stuff- -- else- -- stuff- -- note that this has par-spacing- addAlternative- $ docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- ( docSeq- [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"- , docNodeAnnKW lexpr (Just AnnIf) $ docForceSingleline ifExprDoc- ])- (docLines- [ docAddBaseY BrIndentRegular- $ docNodeAnnKW lexpr (Just AnnThen)- $ docNonBottomSpacing $ docAlt- [ docSeq [appSep $ docLit $ Text.pack "then", docForceParSpacing thenExprDoc]- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "then") thenExprDoc- ]- , docAddBaseY BrIndentRegular- $ docNonBottomSpacing $ docAlt- [ docSeq [appSep $ docLit $ Text.pack "else", docForceParSpacing elseExprDoc]- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "else") elseExprDoc- ]- ])- -- either- -- if multi- -- line- -- condition- -- then foo- -- bar- -- else foo- -- bar- -- or- -- if multi- -- line- -- condition- -- then- -- stuff- -- else- -- stuff- -- note that this does _not_ have par-spacing- addAlternative- $ docAddBaseY BrIndentRegular- $ docPar- ( docAddBaseY maySpecialIndent- $ docSeq- [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"- , docNodeAnnKW lexpr (Just AnnIf) $ ifExprDoc- ])- (docLines- [ docAddBaseY BrIndentRegular- $ docNodeAnnKW lexpr (Just AnnThen)- $ docAlt- [ docSeq [appSep $ docLit $ Text.pack "then", docForceParSpacing thenExprDoc]- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "then") thenExprDoc- ]- , docAddBaseY BrIndentRegular- $ docAlt- [ docSeq [appSep $ docLit $ Text.pack "else", docForceParSpacing elseExprDoc]- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "else") elseExprDoc- ]- ])- addAlternative- $ docSetBaseY- $ docLines- [ docAddBaseY maySpecialIndent- $ docSeq- [ docNodeAnnKW lexpr Nothing $ appSep $ docLit $ Text.pack "if"- , docNodeAnnKW lexpr (Just AnnIf) $ ifExprDoc- ]- , docNodeAnnKW lexpr (Just AnnThen)- $ docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "then") thenExprDoc- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "else") elseExprDoc- ]- HsMultiIf _ cases -> do- clauseDocs <- cases `forM` layoutGrhs- binderDoc <- docLit $ Text.pack "->"- hasComments <- hasAnyCommentsBelow lexpr- docSetParSpacing $ docAddBaseY BrIndentRegular $ docPar- (docLit $ Text.pack "if")- (layoutPatternBindFinal Nothing binderDoc Nothing clauseDocs Nothing hasComments)- HsLet _ binds exp1 -> do- expDoc1 <- docSharedWrapper layoutExpr exp1- -- We jump through some ugly hoops here to ensure proper sharing.- hasComments <- hasAnyCommentsBelow lexpr- mBindDocs <- fmap (fmap pure) <$> layoutLocalBinds binds- let- ifIndentFreeElse :: a -> a -> a- ifIndentFreeElse x y =- case indentPolicy of- IndentPolicyLeft -> y- IndentPolicyMultiple -> y- IndentPolicyFree -> x- -- this `docSetBaseAndIndent` might seem out of place (especially the- -- Indent part; setBase is necessary due to the use of docLines below),- -- but is here due to ghc-exactprint's DP handling of "let" in- -- particular.- -- Just pushing another indentation level is a straightforward approach- -- to making brittany idempotent, even though the result is non-optimal- -- if "let" is moved horizontally as part of the transformation, as the- -- comments before the first let item are moved horizontally with it.- docSetBaseAndIndent $ case mBindDocs of- Just [bindDoc] -> runFilteredAlternative $ do- addAlternativeCond (not hasComments) $ docSeq- [ appSep $ docLit $ Text.pack "let"- , docNodeAnnKW lexpr (Just AnnLet)- $ appSep $ docForceSingleline bindDoc- , appSep $ docLit $ Text.pack "in"- , docForceSingleline expDoc1- ]- addAlternative $ docLines- [ docNodeAnnKW lexpr (Just AnnLet)- $ docAlt- [ docSeq- [ appSep $ docLit $ Text.pack "let"- , ifIndentFreeElse docSetBaseAndIndent docForceSingleline- $ bindDoc- ]- , docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "let")- (docSetBaseAndIndent bindDoc)- ]- , docAlt- [ docSeq- [ appSep $ docLit $ Text.pack $ ifIndentFreeElse "in " "in"- , ifIndentFreeElse docSetBaseAndIndent docForceSingleline expDoc1- ]- , docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "in")- (docSetBaseY expDoc1)- ]- ]- Just bindDocs@(_:_) -> runFilteredAlternative $ do- --either- -- let- -- a = b- -- c = d- -- in foo- -- bar- -- baz- --or- -- let- -- a = b- -- c = d- -- in- -- fooooooooooooooooooo- let noHangingBinds =- [ docNonBottomSpacing $ docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "let")- (docSetBaseAndIndent $ docLines bindDocs)- , docSeq- [ docLit $ Text.pack "in "- , docAddBaseY BrIndentRegular- $ docForceParSpacing expDoc1- ]- ]- addAlternative $ case indentPolicy of- IndentPolicyLeft -> docLines noHangingBinds- IndentPolicyMultiple -> docLines noHangingBinds- IndentPolicyFree -> docLines- [ docNodeAnnKW lexpr (Just AnnLet)- $ docSeq- [ appSep $ docLit $ Text.pack "let"- , docSetBaseAndIndent $ docLines bindDocs- ]- , docSeq- [ appSep $ docLit $ Text.pack "in "- , docSetBaseY expDoc1- ]- ]- addAlternative- $ docLines- [ docNodeAnnKW lexpr (Just AnnLet)- $ docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "let")- (docSetBaseAndIndent $ docLines $ bindDocs)- , docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "in")- (docSetBaseY $ expDoc1)- ]- _ -> docSeq [appSep $ docLit $ Text.pack "let in", expDoc1]- -- docSeq [appSep $ docLit "let in", expDoc1]- HsDo _ stmtCtx (L _ stmts) -> case stmtCtx of- DoExpr _ -> do- stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts- docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "do")- (docSetBaseAndIndent $ docNonBottomSpacing $ docLines stmtDocs)- MDoExpr _ -> do- stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts- docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docLit $ Text.pack "mdo")- (docSetBaseAndIndent $ docNonBottomSpacing $ docLines stmtDocs)- x | case x of { ListComp -> True- ; MonadComp -> True- ; _ -> False } -> do- stmtDocs <- docSharedWrapper layoutStmt `mapM` stmts- hasComments <- hasAnyCommentsBelow lexpr- runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docSeq- [ docNodeAnnKW lexpr Nothing- $ appSep- $ docLit- $ Text.pack "["- , docNodeAnnKW lexpr (Just AnnOpenS)- $ appSep- $ docForceSingleline- $ List.last stmtDocs- , appSep $ docLit $ Text.pack "|"- , docSeq $ List.intersperse docCommaSep- $ docForceSingleline <$> List.init stmtDocs- , docLit $ Text.pack " ]"- ]- addAlternative $- let- start = docCols ColListComp- [ docNodeAnnKW lexpr Nothing- $ appSep $ docLit $ Text.pack "["- , docSetBaseY- $ docNodeAnnKW lexpr (Just AnnOpenS)- $ List.last stmtDocs- ]- (s1:sM) = List.init stmtDocs- line1 = docCols ColListComp- [appSep $ docLit $ Text.pack "|", s1]- lineM = sM <&> \d ->- docCols ColListComp [docCommaSep, d]- end = docLit $ Text.pack "]"- in docSetBaseY $ docLines $ [start, line1] ++ lineM ++ [end]- _ -> do- -- TODO- unknownNodeError "HsDo{} unknown stmtCtx" lexpr- ExplicitList _ _ elems@(_:_) -> do- elemDocs <- elems `forM` docSharedWrapper layoutExpr- hasComments <- hasAnyCommentsBelow lexpr- case splitFirstLast elemDocs of- FirstLastEmpty -> docSeq- [ docLit $ Text.pack "["- , docNodeAnnKW lexpr (Just AnnOpenS) $ docLit $ Text.pack "]"- ]- FirstLastSingleton e -> docAlt- [ docSeq- [ docLit $ Text.pack "["- , docNodeAnnKW lexpr (Just AnnOpenS) $ docForceSingleline e- , docLit $ Text.pack "]"- ]- , docSetBaseY $ docLines- [ docSeq- [ docLit $ Text.pack "["- , docSeparator- , docSetBaseY $ docNodeAnnKW lexpr (Just AnnOpenS) e- ]- , docLit $ Text.pack "]"- ]- ]- FirstLast e1 ems eN -> runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docSeq- $ [docLit $ Text.pack "["]- ++ List.intersperse docCommaSep (docForceSingleline <$> (e1:ems ++ [docNodeAnnKW lexpr (Just AnnOpenS) eN]))- ++ [docLit $ Text.pack "]"]- addAlternative $- let- start = docCols ColList- [appSep $ docLit $ Text.pack "[", e1]- linesM = ems <&> \d ->- docCols ColList [docCommaSep, d]- lineN = docCols ColList [docCommaSep, docNodeAnnKW lexpr (Just AnnOpenS) eN]- end = docLit $ Text.pack "]"- in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN] ++ [end]- ExplicitList _ _ [] ->- docLit $ Text.pack "[]"- RecordCon _ lname fields ->- case fields of- HsRecFields fs Nothing -> do- let nameDoc = docWrapNode lname $ docLit $ lrdrNameToText lname- rFs <- fs- `forM` \lfield@(L _ (HsRecField (L _ fieldOcc) rFExpr pun)) -> do- let FieldOcc _ lnameF = fieldOcc- rFExpDoc <- if pun- then return Nothing- else Just <$> docSharedWrapper layoutExpr rFExpr- return $ (lfield, lrdrNameToText lnameF, rFExpDoc)- recordExpression False indentPolicy lexpr nameDoc rFs- HsRecFields [] (Just (L _ 0)) -> do- let t = lrdrNameToText lname- docWrapNode lname $ docLit $ t <> Text.pack " { .. }"- HsRecFields fs@(_:_) (Just (L _ dotdoti)) | dotdoti == length fs -> do- let nameDoc = docWrapNode lname $ docLit $ lrdrNameToText lname- fieldDocs <- fs `forM` \fieldl@(L _ (HsRecField (L _ fieldOcc) fExpr pun)) -> do- let FieldOcc _ lnameF = fieldOcc- fExpDoc <- if pun- then return Nothing- else Just <$> docSharedWrapper layoutExpr fExpr- return (fieldl, lrdrNameToText lnameF, fExpDoc)- recordExpression True indentPolicy lexpr nameDoc fieldDocs- _ -> unknownNodeError "RecordCon with puns" lexpr- RecordUpd _ rExpr fields -> do- rExprDoc <- docSharedWrapper layoutExpr rExpr- rFs <- fields- `forM` \lfield@(L _ (HsRecField (L _ ambName) rFExpr pun)) -> do- rFExpDoc <- if pun- then return Nothing- else Just <$> docSharedWrapper layoutExpr rFExpr- return $ case ambName of- Unambiguous _ n -> (lfield, lrdrNameToText n, rFExpDoc)- Ambiguous _ n -> (lfield, lrdrNameToText n, rFExpDoc)- recordExpression False indentPolicy lexpr rExprDoc rFs- ExprWithTySig _ exp1 (HsWC _ (HsIB _ typ1)) -> do- expDoc <- docSharedWrapper layoutExpr exp1- typDoc <- docSharedWrapper layoutType typ1- docSeq- [ appSep expDoc- , appSep $ docLit $ Text.pack "::"- , typDoc- ]- ArithSeq _ Nothing info ->- case info of- From e1 -> do- e1Doc <- docSharedWrapper layoutExpr e1- docSeq- [ docLit $ Text.pack "["- , appSep $ docForceSingleline e1Doc- , docLit $ Text.pack "..]"- ]- FromThen e1 e2 -> do- e1Doc <- docSharedWrapper layoutExpr e1- e2Doc <- docSharedWrapper layoutExpr e2- docSeq- [ docLit $ Text.pack "["- , docForceSingleline e1Doc- , appSep $ docLit $ Text.pack ","- , appSep $ docForceSingleline e2Doc- , docLit $ Text.pack "..]"- ]- FromTo e1 eN -> do- e1Doc <- docSharedWrapper layoutExpr e1- eNDoc <- docSharedWrapper layoutExpr eN- docSeq- [ docLit $ Text.pack "["- , appSep $ docForceSingleline e1Doc- , appSep $ docLit $ Text.pack ".."- , docForceSingleline eNDoc- , docLit $ Text.pack "]"- ]- FromThenTo e1 e2 eN -> do- e1Doc <- docSharedWrapper layoutExpr e1- e2Doc <- docSharedWrapper layoutExpr e2- eNDoc <- docSharedWrapper layoutExpr eN- docSeq- [ docLit $ Text.pack "["- , docForceSingleline e1Doc- , appSep $ docLit $ Text.pack ","- , appSep $ docForceSingleline e2Doc- , appSep $ docLit $ Text.pack ".."- , docForceSingleline eNDoc- , docLit $ Text.pack "]"- ]- ArithSeq{} ->- briDocByExactInlineOnly "ArithSeq" lexpr- HsBracket{} -> do- -- TODO- briDocByExactInlineOnly "HsBracket{}" lexpr- HsRnBracketOut{} -> do- -- TODO- briDocByExactInlineOnly "HsRnBracketOut{}" lexpr- HsTcBracketOut{} -> do- -- TODO- briDocByExactInlineOnly "HsTcBracketOut{}" lexpr- HsSpliceE _ (HsQuasiQuote _ _ quoter _loc content) -> do- allocateNode $ BDFPlain- (Text.pack- $ "["- ++ showOutputable quoter- ++ "|"- ++ showOutputable content- ++ "|]")- HsSpliceE{} -> do- -- TODO- briDocByExactInlineOnly "HsSpliceE{}" lexpr- HsProc{} -> do- -- TODO- briDocByExactInlineOnly "HsProc{}" lexpr- HsStatic{} -> do- -- TODO- briDocByExactInlineOnly "HsStatic{}" lexpr- HsTick{} -> do- -- TODO- briDocByExactInlineOnly "HsTick{}" lexpr- HsBinTick{} -> do- -- TODO- briDocByExactInlineOnly "HsBinTick{}" lexpr- HsConLikeOut{} -> do- -- TODO- briDocByExactInlineOnly "HsWrap{}" lexpr- ExplicitSum{} -> do- -- TODO- briDocByExactInlineOnly "ExplicitSum{}" lexpr- HsPragE{} -> do- -- TODO- briDocByExactInlineOnly "HsPragE{}" lexpr--recordExpression- :: (Data.Data.Data lExpr, Data.Data.Data name)- => Bool- -> IndentPolicy- -> GenLocated SrcSpan lExpr- -> ToBriDocM BriDocNumbered- -> [(GenLocated SrcSpan name, Text, Maybe (ToBriDocM BriDocNumbered))]- -> ToBriDocM BriDocNumbered-recordExpression False _ lexpr nameDoc [] =- docSeq- [ docNodeAnnKW lexpr (Just AnnOpenC) $ docSeq [nameDoc, docLit $ Text.pack "{"]- , docLit $ Text.pack "}"- ]-recordExpression True _ lexpr nameDoc [] =- docSeq -- this case might still be incomplete, and is probably not used- -- atm anyway.- [ docNodeAnnKW lexpr (Just AnnOpenC) $ docSeq [nameDoc, docLit $ Text.pack "{"]- , docLit $ Text.pack " .. }"- ]-recordExpression dotdot indentPolicy lexpr nameDoc rFs@(rF1:rFr) = do- let (rF1f, rF1n, rF1e) = rF1- runFilteredAlternative $ do- -- container { fieldA = blub, fieldB = blub }- addAlternative- $ docSeq- [ docNodeAnnKW lexpr Nothing $ appSep $ docForceSingleline nameDoc- , appSep $ docLit $ Text.pack "{"- , docSeq $ List.intersperse docCommaSep- $ rFs <&> \case- (lfield, fieldStr, Just fieldDoc) ->- docWrapNode lfield $ docSeq- [ appSep $ docLit fieldStr- , appSep $ docLit $ Text.pack "="- , docForceSingleline fieldDoc- ]- (lfield, fieldStr, Nothing) ->- docWrapNode lfield $ docLit fieldStr- , if dotdot- then docSeq [ docCommaSep, docLit $ Text.pack "..", docSeparator]- else docSeparator- , docLit $ Text.pack "}"- ]- -- hanging single-line fields- -- container { fieldA = blub- -- , fieldB = blub- -- }- addAlternativeCond (indentPolicy == IndentPolicyFree)- $ docSeq- [ docNodeAnnKW lexpr Nothing $ docForceSingleline $ appSep nameDoc- , docSetBaseY $ docLines $ let- line1 = docCols ColRec- [ appSep $ docLit $ Text.pack "{"- , docWrapNodePrior rF1f $ appSep $ docLit rF1n- , case rF1e of- Just x -> docWrapNodeRest rF1f $ docSeq- [ appSep $ docLit $ Text.pack "="- , docForceSingleline x- ]- Nothing -> docEmpty- ]- lineR = rFr <&> \(lfield, fText, fDoc) -> docWrapNode lfield $ docCols ColRec- [ docCommaSep- , appSep $ docLit fText- , case fDoc of- Just x -> docSeq [ appSep $ docLit $ Text.pack "="- , docForceSingleline x- ]- Nothing -> docEmpty- ]- dotdotLine = if dotdot- then docCols ColRec- [ docNodeAnnKW lexpr (Just AnnOpenC) docCommaSep- , docNodeAnnKW lexpr (Just AnnDotdot)- $ docLit $ Text.pack ".."- ]- else docNodeAnnKW lexpr (Just AnnOpenC) docEmpty- lineN = docLit $ Text.pack "}"- in [line1] ++ lineR ++ [dotdotLine, lineN]- ]- -- non-hanging with expressions placed to the right of the names- -- container- -- { fieldA = blub- -- , fieldB = potentially- -- multiline- -- }- addAlternative- $ docSetParSpacing- $ docAddBaseY BrIndentRegular- $ docPar- (docNodeAnnKW lexpr Nothing nameDoc)- (docNonBottomSpacing $ docLines $ let- line1 = docCols ColRec- [ appSep $ docLit $ Text.pack "{"- , docWrapNodePrior rF1f $ appSep $ docLit rF1n- , docWrapNodeRest rF1f $ case rF1e of- Just x -> runFilteredAlternative $ do- addAlternativeCond (indentPolicy == IndentPolicyFree) $ do- docSeq- [appSep $ docLit $ Text.pack "=", docSetBaseY x]- addAlternative $ do- docSeq- [appSep $ docLit $ Text.pack "=", docForceParSpacing x]- addAlternative $ do- docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "=") x- Nothing -> docEmpty- ]- lineR = rFr <&> \(lfield, fText, fDoc) -> docWrapNode lfield- $ docCols ColRec- [ docCommaSep- , appSep $ docLit fText- , case fDoc of- Just x -> runFilteredAlternative $ do- addAlternativeCond (indentPolicy == IndentPolicyFree) $ do- docSeq- [appSep $ docLit $ Text.pack "=", docSetBaseY x]- addAlternative $ do- docSeq [ appSep $ docLit $ Text.pack "="- , docForceParSpacing x- ]- addAlternative $ do- docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "=") x- Nothing -> docEmpty- ]- dotdotLine = if dotdot- then docCols ColRec- [ docNodeAnnKW lexpr (Just AnnOpenC) docCommaSep- , docNodeAnnKW lexpr (Just AnnDotdot)- $ docLit $ Text.pack ".."- ]- else docNodeAnnKW lexpr (Just AnnOpenC) docEmpty- lineN = docLit $ Text.pack "}"- in [line1] ++ lineR ++ [dotdotLine, lineN]- )--litBriDoc :: HsLit GhcPs -> BriDocFInt-litBriDoc = \case- HsChar (SourceText t) _c -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ ['\'', c, '\'']- HsCharPrim (SourceText t) _c -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ ['\'', c, '\'']- HsString (SourceText t) _fastString -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ FastString.unpackFS fastString- HsStringPrim (SourceText t) _byteString -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ Data.ByteString.Char8.unpack byteString- HsInt _ (IL (SourceText t) _ _) -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsIntPrim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsWordPrim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsInt64Prim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsWord64Prim (SourceText t) _i -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsInteger (SourceText t) _i _type -> BDFLit $ Text.pack t -- BDFLit $ Text.pack $ show i- HsRat _ (FL (SourceText t) _ _) _type -> BDFLit $ Text.pack t- HsFloatPrim _ (FL (SourceText t) _ _) -> BDFLit $ Text.pack t- HsDoublePrim _ (FL (SourceText t) _ _) -> BDFLit $ Text.pack t- _ -> error "litBriDoc: literal with no SourceText"--overLitValBriDoc :: OverLitVal -> BriDocFInt-overLitValBriDoc = \case- HsIntegral (IL (SourceText t) _ _) -> BDFLit $ Text.pack t- HsFractional (FL (SourceText t) _ _) -> BDFLit $ Text.pack t- HsIsString (SourceText t) _ -> BDFLit $ Text.pack t- _ -> error "overLitValBriDoc: literal with no SourceText"
− source/library/Language/Haskell/Brittany/Internal/Layouters/Expr.hs-boot
@@ -1,16 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Expr where--import GHC.Hs-import Language.Haskell.Brittany.Internal.Types----layoutExpr :: ToBriDoc HsExpr---- layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))--litBriDoc :: HsLit GhcPs -> BriDocFInt--overLitValBriDoc :: OverLitVal -> BriDocFInt
− source/library/Language/Haskell/Brittany/Internal/Layouters/IE.hs
@@ -1,214 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.IE where--import qualified Data.List.Extra-import qualified Data.Text as Text-import GHC- ( AnnKeywordId(..)- , GenLocated(L)- , Located- , ModuleName- , moduleNameString- , unLoc- )-import GHC.Hs-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils----prepareName :: LIEWrappedName name -> Located name-prepareName = ieLWrappedName--layoutIE :: ToBriDoc IE-layoutIE lie@(L _ ie) = docWrapNode lie $ case ie of- IEVar _ x -> layoutWrapped lie x- IEThingAbs _ x -> layoutWrapped lie x- IEThingAll _ x -> docSeq [layoutWrapped lie x, docLit $ Text.pack "(..)"]- IEThingWith _ x (IEWildcard _) _ _ ->- docSeq [layoutWrapped lie x, docLit $ Text.pack "(..)"]- IEThingWith _ x _ ns _ -> do- hasComments <- orM- ( hasCommentsBetween lie AnnOpenP AnnCloseP- : hasAnyCommentsBelow x- : map hasAnyCommentsBelow ns- )- let sortedNs = List.sortOn wrappedNameToText ns- runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docSeq- $ [layoutWrapped lie x, docLit $ Text.pack "("]- ++ intersperse docCommaSep (map nameDoc sortedNs)- ++ [docParenR]- addAlternative- $ docWrapNodeRest lie- $ docAddBaseY BrIndentRegular- $ docPar- (layoutWrapped lie x)- (layoutItems (splitFirstLast sortedNs))- where- nameDoc = docLit <=< lrdrNameToTextAnn . prepareName- layoutItem n = docSeq [docCommaSep, docWrapNode n $ nameDoc n]- layoutItems FirstLastEmpty = docSetBaseY $ docLines- [docSeq [docParenLSep, docNodeAnnKW lie (Just AnnOpenP) docEmpty], docParenR]- layoutItems (FirstLastSingleton n) = docSetBaseY $ docLines- [docSeq [docParenLSep, docNodeAnnKW lie (Just AnnOpenP) $ nameDoc n], docParenR]- layoutItems (FirstLast n1 nMs nN) =- docSetBaseY- $ docLines- $ [docSeq [docParenLSep, docWrapNode n1 $ nameDoc n1]]- ++ map layoutItem nMs- ++ [docSeq [docCommaSep, docNodeAnnKW lie (Just AnnOpenP) $ nameDoc nN], docParenR]- IEModuleContents _ n -> docSeq- [ docLit $ Text.pack "module"- , docSeparator- , docLit . Text.pack . moduleNameString $ unLoc n- ]- _ -> docEmpty- where- layoutWrapped _ = \case- L _ (IEName n) -> docLit =<< lrdrNameToTextAnn n- L _ (IEPattern n) -> do- name <- lrdrNameToTextAnn n- docLit $ Text.pack "pattern " <> name- L _ (IEType n) -> do- name <- lrdrNameToTextAnn n- docLit $ Text.pack "type " <> name--data SortItemsFlag = ShouldSortItems | KeepItemsUnsorted--- Helper function to deal with Located lists of LIEs.--- In particular this will also associate documentation--- from the located list that actually belongs to the last IE.--- It also adds docCommaSep to all but the first element--- This configuration allows both vertical and horizontal--- handling of the resulting list. Adding parens is--- left to the caller since that is context sensitive-layoutAnnAndSepLLIEs- :: SortItemsFlag -> Located [LIE GhcPs] -> ToBriDocM [ToBriDocM BriDocNumbered]-layoutAnnAndSepLLIEs shouldSort llies@(L _ lies) = do- let makeIENode ie = docSeq [docCommaSep, ie]- let sortedLies =- [ items- | group <- Data.List.Extra.groupOn lieToText- $ List.sortOn lieToText lies- , items <- mergeGroup group- ]- let ieDocs = fmap layoutIE $ case shouldSort of- ShouldSortItems -> sortedLies- KeepItemsUnsorted -> lies- ieCommaDocs <-- docWrapNodeRest llies $ sequence $ case splitFirstLast ieDocs of- FirstLastEmpty -> []- FirstLastSingleton ie -> [ie]- FirstLast ie1 ieMs ieN ->- [ie1] ++ map makeIENode ieMs ++ [makeIENode ieN]- pure $ fmap pure ieCommaDocs -- returned shared nodes- where- mergeGroup :: [LIE GhcPs] -> [LIE GhcPs]- mergeGroup [] = []- mergeGroup items@[_] = items- mergeGroup items = if- | all isProperIEThing items -> [List.foldl1' thingFolder items]- | all isIEVar items -> [List.foldl1' thingFolder items]- | otherwise -> items- -- proper means that if it is a ThingWith, it does not contain a wildcard- -- (because I don't know what a wildcard means if it is not already a- -- IEThingAll).- isProperIEThing :: LIE GhcPs -> Bool- isProperIEThing = \case- L _ (IEThingAbs _ _wn) -> True- L _ (IEThingAll _ _wn) -> True- L _ (IEThingWith _ _wn NoIEWildcard _ _) -> True- _ -> False- isIEVar :: LIE GhcPs -> Bool- isIEVar = \case- L _ IEVar{} -> True- _ -> False- thingFolder :: LIE GhcPs -> LIE GhcPs -> LIE GhcPs- thingFolder l1@(L _ IEVar{} ) _ = l1- thingFolder l1@(L _ IEThingAll{}) _ = l1- thingFolder _ l2@(L _ IEThingAll{}) = l2- thingFolder l1 ( L _ IEThingAbs{}) = l1- thingFolder (L _ IEThingAbs{}) l2 = l2- thingFolder (L l (IEThingWith x wn _ consItems1 fieldLbls1)) (L _ (IEThingWith _ _ _ consItems2 fieldLbls2))- = L- l- (IEThingWith x- wn- NoIEWildcard- (consItems1 ++ consItems2)- (fieldLbls1 ++ fieldLbls2)- )- thingFolder _ _ =- error "thingFolder should be exhaustive because we have a guard above"----- Builds a complete layout for the given located--- list of LIEs. The layout provides two alternatives:--- (item, item, ..., item)--- ( item--- , item--- ...--- , item--- )--- If the llies contains comments the list will--- always expand over multiple lines, even when empty:--- () -- no comments--- ( -- a comment--- )-layoutLLIEs :: Bool -> SortItemsFlag -> Located [LIE GhcPs] -> ToBriDocM BriDocNumbered-layoutLLIEs enableSingleline shouldSort llies = do- ieDs <- layoutAnnAndSepLLIEs shouldSort llies- hasComments <- hasAnyCommentsBelow llies- runFilteredAlternative $ case ieDs of- [] -> do- addAlternativeCond (not hasComments) $ docLit $ Text.pack "()"- addAlternativeCond hasComments $ docPar- (docSeq [docParenLSep, docWrapNodeRest llies docEmpty])- docParenR- (ieDsH : ieDsT) -> do- addAlternativeCond (not hasComments && enableSingleline)- $ docSeq- $ [docLit (Text.pack "(")]- ++ (docForceSingleline <$> ieDs)- ++ [docParenR]- addAlternative- $ docPar (docSetBaseY $ docSeq [docParenLSep, ieDsH])- $ docLines- $ ieDsT- ++ [docParenR]---- | Returns a "fingerprint string", not a full text representation, nor even--- a source code representation of this syntax node.--- Used for sorting, not for printing the formatter's output source code.-wrappedNameToText :: LIEWrappedName RdrName -> Text-wrappedNameToText = \case- L _ (IEName n) -> lrdrNameToText n- L _ (IEPattern n) -> lrdrNameToText n- L _ (IEType n) -> lrdrNameToText n---- | Returns a "fingerprint string", not a full text representation, nor even--- a source code representation of this syntax node.--- Used for sorting, not for printing the formatter's output source code.-lieToText :: LIE GhcPs -> Text-lieToText = \case- L _ (IEVar _ wn ) -> wrappedNameToText wn- L _ (IEThingAbs _ wn ) -> wrappedNameToText wn- L _ (IEThingAll _ wn ) -> wrappedNameToText wn- L _ (IEThingWith _ wn _ _ _) -> wrappedNameToText wn- -- TODO: These _may_ appear in exports!- -- Need to check, and either put them at the top (for module) or do some- -- other clever thing.- L _ (IEModuleContents _ n) -> moduleNameToText n- L _ IEGroup{} -> Text.pack "@IEGroup"- L _ IEDoc{} -> Text.pack "@IEDoc"- L _ IEDocNamed{} -> Text.pack "@IEDocNamed"- where- moduleNameToText :: Located ModuleName -> Text- moduleNameToText (L _ name) = Text.pack ("@IEModuleContents" ++ moduleNameString name)
− source/library/Language/Haskell/Brittany/Internal/Layouters/Import.hs
@@ -1,142 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Import where--import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import GHC (GenLocated(L), Located, moduleNameString, unLoc)-import GHC.Hs-import GHC.Types.Basic-import GHC.Unit.Types (IsBootInterface(..))-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.IE-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types----prepPkg :: SourceText -> String-prepPkg rawN = case rawN of- SourceText n -> n- -- This would be odd to encounter and the- -- result will most certainly be wrong- NoSourceText -> ""-prepModName :: Located e -> e-prepModName = unLoc--layoutImport :: ImportDecl GhcPs -> ToBriDocM BriDocNumbered-layoutImport importD = case importD of- ImportDecl _ _ (L _ modName) pkg src safe q False mas mllies -> do- importCol <- mAsk <&> _conf_layout .> _lconfig_importColumn .> confUnpack- importAsCol <- mAsk <&> _conf_layout .> _lconfig_importAsColumn .> confUnpack- indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack- let- compact = indentPolicy /= IndentPolicyFree- modNameT = Text.pack $ moduleNameString modName- pkgNameT = Text.pack . prepPkg . sl_st <$> pkg- masT = Text.pack . moduleNameString . prepModName <$> mas- hiding = maybe False fst mllies- minQLength = length "import qualified "- qLengthReal =- let qualifiedPart = if q /= NotQualified then length "qualified " else 0- safePart = if safe then length "safe " else 0- pkgPart = maybe 0 ((+ 1) . Text.length) pkgNameT- srcPart = case src of { IsBoot -> length "{-# SOURCE #-} "; NotBoot -> 0 }- in length "import " + srcPart + safePart + qualifiedPart + pkgPart- qLength = max minQLength qLengthReal- -- Cost in columns of importColumn- asCost = length "as "- hidingParenCost = if hiding then length "hiding ( " else length "( "- nameCost = Text.length modNameT + qLength- importQualifiers = docSeq- [ appSep $ docLit $ Text.pack "import"- , case src of { IsBoot -> appSep $ docLit $ Text.pack "{-# SOURCE #-}"; NotBoot -> docEmpty }- , if safe then appSep $ docLit $ Text.pack "safe" else docEmpty- , if q /= NotQualified then appSep $ docLit $ Text.pack "qualified" else docEmpty- , maybe docEmpty (appSep . docLit) pkgNameT- ]- indentName =- if compact then id else docEnsureIndent (BrIndentSpecial qLength)- modNameD =- indentName $ appSep $ docLit modNameT- hidDocCol = if hiding then importCol - hidingParenCost else importCol - 2- hidDocColDiff = importCol - 2 - hidDocCol- hidDoc = if hiding- then appSep $ docLit $ Text.pack "hiding"- else docEmpty- importHead = docSeq [importQualifiers, modNameD]- bindingsD = case mllies of- Nothing -> docEmpty- Just (_, llies) -> do- hasComments <- hasAnyCommentsBelow llies- if compact- then docAlt- [ docSeq [hidDoc, docForceSingleline $ layoutLLIEs True ShouldSortItems llies]- , let makeParIfHiding = if hiding- then docAddBaseY BrIndentRegular . docPar hidDoc- else id- in makeParIfHiding (layoutLLIEs True ShouldSortItems llies)- ]- else do- ieDs <- layoutAnnAndSepLLIEs ShouldSortItems llies- docWrapNodeRest llies- $ docEnsureIndent (BrIndentSpecial hidDocCol)- $ case ieDs of- -- ..[hiding].( )- [] -> if hasComments- then docPar- (docSeq [hidDoc, docParenLSep, docWrapNode llies docEmpty])- (docEnsureIndent (BrIndentSpecial hidDocColDiff) docParenR)- else docSeq [hidDoc, docParenLSep, docSeparator, docParenR]- -- ..[hiding].( b )- [ieD] -> runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docSeq- [ hidDoc- , docParenLSep- , docForceSingleline ieD- , docSeparator- , docParenR- ]- addAlternative $ docPar- (docSeq [hidDoc, docParenLSep, docNonBottomSpacing ieD])- (docEnsureIndent (BrIndentSpecial hidDocColDiff) docParenR)- -- ..[hiding].( b- -- , b'- -- )- (ieD:ieDs') ->- docPar- (docSeq [hidDoc, docSetBaseY $ docSeq [docParenLSep, ieD]])- ( docEnsureIndent (BrIndentSpecial hidDocColDiff)- $ docLines- $ ieDs'- ++ [docParenR]- )- makeAsDoc asT =- docSeq [appSep $ docLit $ Text.pack "as", appSep $ docLit asT]- if compact- then- let asDoc = maybe docEmpty makeAsDoc masT- in docAlt- [ docForceSingleline $ docSeq [importHead, asDoc, bindingsD]- , docAddBaseY BrIndentRegular $- docPar (docSeq [importHead, asDoc]) bindingsD- ]- else- case masT of- Just n -> if enoughRoom- then docLines- [ docSeq [importHead, asDoc], bindingsD]- else docLines [importHead, asDoc, bindingsD]- where- enoughRoom = nameCost < importAsCol - asCost- asDoc =- docEnsureIndent (BrIndentSpecial (importAsCol - asCost))- $ makeAsDoc n- Nothing -> if enoughRoom- then docSeq [importHead, bindingsD]- else docLines [importHead, bindingsD]- where enoughRoom = nameCost < importCol - hidingParenCost- _ -> docEmpty
− source/library/Language/Haskell/Brittany/Internal/Layouters/Module.hs
@@ -1,204 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Module where--import qualified Data.Maybe-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import GHC (AnnKeywordId(..), GenLocated(L), moduleNameString, unLoc)-import GHC.Hs-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.IE-import Language.Haskell.Brittany.Internal.Layouters.Import-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.GHC.ExactPrint as ExactPrint-import Language.Haskell.GHC.ExactPrint.Types- (DeltaPos(..), commentContents, deltaRow)----layoutModule :: ToBriDoc' HsModule-layoutModule lmod@(L _ mod') = case mod' of- -- Implicit module Main- HsModule _ Nothing _ imports _ _ _ -> do- commentedImports <- transformToCommentedImport imports- -- groupify commentedImports `forM_` tellDebugMessShow- docLines (commentedImportsToDoc <$> sortCommentedImports commentedImports)- -- sortedImports <- sortImports imports- -- docLines $ [layoutImport y i | (y, i) <- sortedImports]- HsModule _ (Just n) les imports _ _ _ -> do- commentedImports <- transformToCommentedImport imports- -- groupify commentedImports `forM_` tellDebugMessShow- -- sortedImports <- sortImports imports- let tn = Text.pack $ moduleNameString $ unLoc n- allowSingleLineExportList <- mAsk- <&> _conf_layout- .> _lconfig_allowSingleLineExportList- .> confUnpack- -- the config should not prevent single-line layout when there is no- -- export list- let allowSingleLine = allowSingleLineExportList || Data.Maybe.isNothing les- docLines- $ docSeq- [ docNodeAnnKW lmod Nothing docEmpty- -- A pseudo node that serves merely to force documentation- -- before the node- , docNodeMoveToKWDP lmod AnnModule True $ runFilteredAlternative $ do- addAlternativeCond allowSingleLine $- docForceSingleline- $ docSeq- [ appSep $ docLit $ Text.pack "module"- , appSep $ docLit tn- , docWrapNode lmod $ appSep $ case les of- Nothing -> docEmpty- Just x -> layoutLLIEs True KeepItemsUnsorted x- , docSeparator- , docLit $ Text.pack "where"- ]- addAlternative- $ docLines- [ docAddBaseY BrIndentRegular $ docPar- (docSeq [appSep $ docLit $ Text.pack "module", docLit tn]- )- (docSeq [- docWrapNode lmod $ case les of- Nothing -> docEmpty- Just x -> layoutLLIEs False KeepItemsUnsorted x- , docSeparator- , docLit $ Text.pack "where"- ]- )- ]- ]- : (commentedImportsToDoc <$> sortCommentedImports commentedImports) -- [layoutImport y i | (y, i) <- sortedImports]--data CommentedImport- = EmptyLine- | IndependentComment (Comment, DeltaPos)- | ImportStatement ImportStatementRecord--instance Show CommentedImport where- show = \case- EmptyLine -> "EmptyLine"- IndependentComment _ -> "IndependentComment"- ImportStatement r ->- "ImportStatement " ++ show (length $ commentsBefore r) ++ " " ++ show- (length $ commentsAfter r)--data ImportStatementRecord = ImportStatementRecord- { commentsBefore :: [(Comment, DeltaPos)]- , commentsAfter :: [(Comment, DeltaPos)]- , importStatement :: ImportDecl GhcPs- }--instance Show ImportStatementRecord where- show r = "ImportStatement " ++ show (length $ commentsBefore r) ++ " " ++ show- (length $ commentsAfter r)--transformToCommentedImport- :: [LImportDecl GhcPs] -> ToBriDocM [CommentedImport]-transformToCommentedImport is = do- nodeWithAnnotations <- is `forM` \i@(L _ rawImport) -> do- annotionMay <- astAnn i- pure (annotionMay, rawImport)- let- convertComment (c, DP (y, x)) =- replicate (y - 1) EmptyLine ++ [IndependentComment (c, DP (1, x))]- accumF- :: [(Comment, DeltaPos)]- -> (Maybe Annotation, ImportDecl GhcPs)- -> ([(Comment, DeltaPos)], [CommentedImport])- accumF accConnectedComm (annMay, decl) = case annMay of- Nothing ->- ( []- , [ ImportStatement ImportStatementRecord { commentsBefore = []- , commentsAfter = []- , importStatement = decl- }- ]- )- Just ann ->- let- blanksBeforeImportDecl = deltaRow (annEntryDelta ann) - 1- (newAccumulator, priorComments') =- List.span ((== 0) . deltaRow . snd) (annPriorComments ann)- go- :: [(Comment, DeltaPos)]- -> [(Comment, DeltaPos)]- -> ([CommentedImport], [(Comment, DeltaPos)], Int)- go acc [] = ([], acc, 0)- go acc [c1@(_, DP (y, _))] = ([], c1 : acc, y - 1)- go acc (c1@(_, DP (1, _)) : xs) = go (c1 : acc) xs- go acc ((c1, DP (y, x)) : xs) =- ( (convertComment =<< xs) ++ replicate (y - 1) EmptyLine- , (c1, DP (1, x)) : acc- , 0- )- (convertedIndependentComments, beforeComments, initialBlanks) =- if blanksBeforeImportDecl /= 0- then (convertComment =<< priorComments', [], 0)- else go [] (reverse priorComments')- in- ( newAccumulator- , convertedIndependentComments- ++ replicate (blanksBeforeImportDecl + initialBlanks) EmptyLine- ++ [ ImportStatement ImportStatementRecord- { commentsBefore = beforeComments- , commentsAfter = accConnectedComm- , importStatement = decl- }- ]- )- let (finalAcc, finalList) = mapAccumR accumF [] nodeWithAnnotations- pure $ join $ (convertComment =<< finalAcc) : finalList--sortCommentedImports :: [CommentedImport] -> [CommentedImport]-sortCommentedImports =- unpackImports . mergeGroups . map (fmap (sortGroups)) . groupify- where- unpackImports :: [CommentedImport] -> [CommentedImport]- unpackImports xs = xs >>= \case- l@EmptyLine -> [l]- l@IndependentComment{} -> [l]- ImportStatement r ->- map IndependentComment (commentsBefore r) ++ [ImportStatement r]- mergeGroups- :: [Either CommentedImport [ImportStatementRecord]] -> [CommentedImport]- mergeGroups xs = xs >>= \case- Left x -> [x]- Right y -> ImportStatement <$> y- sortGroups :: [ImportStatementRecord] -> [ImportStatementRecord]- sortGroups =- List.sortOn (moduleNameString . unLoc . ideclName . importStatement)- groupify- :: [CommentedImport] -> [Either CommentedImport [ImportStatementRecord]]- groupify cs = go [] cs- where- go [] = \case- (l@EmptyLine : rest) -> Left l : go [] rest- (l@IndependentComment{} : rest) -> Left l : go [] rest- (ImportStatement r : rest) -> go [r] rest- [] -> []- go acc = \case- (l@EmptyLine : rest) -> Right (reverse acc) : Left l : go [] rest- (l@IndependentComment{} : rest) ->- Left l : Right (reverse acc) : go [] rest- (ImportStatement r : rest) -> go (r : acc) rest- [] -> [Right (reverse acc)]--commentedImportsToDoc :: CommentedImport -> ToBriDocM BriDocNumbered-commentedImportsToDoc = \case- EmptyLine -> docLitS ""- IndependentComment c -> commentToDoc c- ImportStatement r ->- docSeq- ( layoutImport (importStatement r)- : map commentToDoc (commentsAfter r)- )- where- commentToDoc (c, DP (_y, x)) = docLitS (replicate x ' ' ++ commentContents c)
− source/library/Language/Haskell/Brittany/Internal/Layouters/Pattern.hs
@@ -1,220 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Pattern where--import qualified Data.Foldable as Foldable-import qualified Data.Sequence as Seq-import qualified Data.Text as Text-import GHC (GenLocated(L), ol_val)-import GHC.Hs-import qualified GHC.OldList as List-import GHC.Types.Basic-import Language.Haskell.Brittany.Internal.LayouterBasics-import {-# SOURCE #-} Language.Haskell.Brittany.Internal.Layouters.Expr-import Language.Haskell.Brittany.Internal.Layouters.Type-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types------ | layouts patterns (inside function bindings, case alternatives, let--- bindings or do notation). E.g. for input--- > case computation of--- > (warnings, Success a b) -> ..--- This part ^^^^^^^^^^^^^^^^^^^^^^^ of the syntax tree is layouted by--- 'layoutPat'. Similarly for--- > func abc True 0 = []--- ^^^^^^^^^^ this part--- We will use `case .. of` as the imagined prefix to the examples used in--- the different cases below.-layoutPat :: LPat GhcPs -> ToBriDocM (Seq BriDocNumbered)-layoutPat lpat@(L _ pat) = docWrapNode lpat $ case pat of- WildPat _ -> fmap Seq.singleton $ docLit $ Text.pack "_"- -- _ -> expr- VarPat _ n ->- fmap Seq.singleton $ docLit $ lrdrNameToText n- -- abc -> expr- LitPat _ lit ->- fmap Seq.singleton $ allocateNode $ litBriDoc lit- -- 0 -> expr- ParPat _ inner -> do- -- (nestedpat) -> expr- left <- docLit $ Text.pack "("- right <- docLit $ Text.pack ")"- innerDocs <- colsWrapPat =<< layoutPat inner- return $ Seq.empty Seq.|> left Seq.|> innerDocs Seq.|> right- -- return $ (left Seq.<| innerDocs) Seq.|> right- -- case Seq.viewl innerDocs of- -- Seq.EmptyL -> fmap return $ docLit $ Text.pack "()" -- this should never occur..- -- x1 Seq.:< rest -> case Seq.viewr rest of- -- Seq.EmptyR ->- -- fmap return $ docSeq- -- [ docLit $ Text.pack "("- -- , return x1- -- , docLit $ Text.pack ")"- -- ]- -- middle Seq.:> xN -> do- -- x1' <- docSeq [docLit $ Text.pack "(", return x1]- -- xN' <- docSeq [return xN, docLit $ Text.pack ")"]- -- return $ (x1' Seq.<| middle) Seq.|> xN'- ConPat _ lname (PrefixCon args) -> do- -- Abc a b c -> expr- nameDoc <- lrdrNameToTextAnn lname- argDocs <- layoutPat `mapM` args- if null argDocs- then return <$> docLit nameDoc- else do- x1 <- appSep (docLit nameDoc)- xR <- fmap Seq.fromList- $ sequence- $ spacifyDocs- $ fmap colsWrapPat argDocs- return $ x1 Seq.<| xR- ConPat _ lname (InfixCon left right) -> do- -- a :< b -> expr- nameDoc <- lrdrNameToTextAnn lname- leftDoc <- appSep . colsWrapPat =<< layoutPat left- rightDoc <- colsWrapPat =<< layoutPat right- middle <- appSep $ docLit nameDoc- return $ Seq.empty Seq.|> leftDoc Seq.|> middle Seq.|> rightDoc- ConPat _ lname (RecCon (HsRecFields [] Nothing)) -> do- -- Abc{} -> expr- let t = lrdrNameToText lname- fmap Seq.singleton $ docLit $ t <> Text.pack "{}"- ConPat _ lname (RecCon (HsRecFields fs@(_:_) Nothing)) -> do- -- Abc { a = locA, b = locB, c = locC } -> expr1- -- Abc { a, b, c } -> expr2- let t = lrdrNameToText lname- fds <- fs `forM` \(L _ (HsRecField (L _ fieldOcc) fPat pun)) -> do- let FieldOcc _ lnameF = fieldOcc- fExpDoc <- if pun- then return Nothing- else Just <$> docSharedWrapper layoutPat fPat- return (lrdrNameToText lnameF, fExpDoc)- Seq.singleton <$> docSeq- [ appSep $ docLit t- , appSep $ docLit $ Text.pack "{"- , docSeq $ List.intersperse docCommaSep- $ fds <&> \case- (fieldName, Just fieldDoc) -> docSeq- [ appSep $ docLit fieldName- , appSep $ docLit $ Text.pack "="- , fieldDoc >>= colsWrapPat- ]- (fieldName, Nothing) -> docLit fieldName- , docSeparator- , docLit $ Text.pack "}"- ]- ConPat _ lname (RecCon (HsRecFields [] (Just (L _ 0)))) -> do- -- Abc { .. } -> expr- let t = lrdrNameToText lname- Seq.singleton <$> docSeq- [ appSep $ docLit t- , docLit $ Text.pack "{..}"- ]- ConPat _ lname (RecCon (HsRecFields fs@(_:_) (Just (L _ dotdoti)))) | dotdoti == length fs -> do- -- Abc { a = locA, .. }- let t = lrdrNameToText lname- fds <- fs `forM` \(L _ (HsRecField (L _ fieldOcc) fPat pun)) -> do- let FieldOcc _ lnameF = fieldOcc- fExpDoc <- if pun- then return Nothing- else Just <$> docSharedWrapper layoutPat fPat- return (lrdrNameToText lnameF, fExpDoc)- 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- -- (nestedpat1, nestedpat2, nestedpat3) -> expr- -- (#nestedpat1, nestedpat2, nestedpat3#) -> expr- case boxity of- Boxed -> wrapPatListy args "()" docParenL docParenR- Unboxed -> wrapPatListy args "(##)" docParenHashLSep docParenHashRSep- AsPat _ asName asPat -> do- -- bind@nestedpat -> expr- wrapPatPrepend asPat (docLit $ lrdrNameToText asName <> Text.pack "@")- SigPat _ pat1 (HsPS _ ty1) -> do- -- i :: Int -> expr- patDocs <- layoutPat pat1- tyDoc <- docSharedWrapper layoutType ty1- case Seq.viewr patDocs of- Seq.EmptyR -> error "cannot happen ljoiuxoasdcoviuasd"- xR Seq.:> xN -> do- xN' <- -- at the moment, we don't support splitting patterns into- -- multiple lines. but we cannot enforce pasting everything- -- into one line either, because the type signature will ignore- -- this if we overflow sufficiently.- -- In order to prevent syntactically invalid results in such- -- cases, we need the AddBaseY here.- -- This can all change when patterns get multiline support.- docAddBaseY BrIndentRegular $ docSeq- [ appSep $ return xN- , appSep $ docLit $ Text.pack "::"- , docForceSingleline tyDoc- ]- return $ xR Seq.|> xN'- ListPat _ elems ->- -- [] -> expr1- -- [nestedpat1, nestedpat2, nestedpat3] -> expr2- wrapPatListy elems "[]" docBracketL docBracketR- BangPat _ pat1 -> do- -- !nestedpat -> expr- wrapPatPrepend pat1 (docLit $ Text.pack "!")- LazyPat _ pat1 -> do- -- ~nestedpat -> expr- wrapPatPrepend pat1 (docLit $ Text.pack "~")- NPat _ llit@(L _ ol) mNegative _ -> do- -- -13 -> expr- litDoc <- docWrapNode llit $ allocateNode $ overLitValBriDoc $ GHC.ol_val ol- negDoc <- docLit $ Text.pack "-"- pure $ case mNegative of- Just{} -> Seq.fromList [negDoc, litDoc]- Nothing -> Seq.singleton litDoc-- _ -> return <$> briDocByExactInlineOnly "some unknown pattern" lpat--colsWrapPat :: Seq BriDocNumbered -> ToBriDocM BriDocNumbered-colsWrapPat = docCols ColPatterns . fmap return . Foldable.toList--wrapPatPrepend- :: LPat GhcPs- -> ToBriDocM BriDocNumbered- -> ToBriDocM (Seq BriDocNumbered)-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--wrapPatListy- :: [LPat GhcPs]- -> String- -> ToBriDocM BriDocNumbered- -> ToBriDocM BriDocNumbered- -> ToBriDocM (Seq BriDocNumbered)-wrapPatListy elems both start end = do- elemDocs <- Seq.fromList elems `forM` (layoutPat >=> colsWrapPat)- case Seq.viewl elemDocs of- Seq.EmptyL -> fmap Seq.singleton $ docLit $ Text.pack both- x1 Seq.:< rest -> do- sDoc <- start- eDoc <- end- rest' <- rest `forM` \bd -> docSeq- [ docCommaSep- , return bd- ]- return $ (sDoc Seq.<| x1 Seq.<| rest') Seq.|> eDoc
− source/library/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs
@@ -1,113 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal.Layouters.Stmt where--import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import GHC (GenLocated(L))-import GHC.Hs-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Layouters.Decl-import {-# SOURCE #-} Language.Haskell.Brittany.Internal.Layouters.Expr-import Language.Haskell.Brittany.Internal.Layouters.Pattern-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types----layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))-layoutStmt lstmt@(L _ stmt) = do- indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack- indentAmount :: Int <-- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack- docWrapNode lstmt $ case stmt of- LastStmt _ body Nothing _ -> do- layoutExpr body- BindStmt _ lPat expr -> do- patDoc <- fmap return $ colsWrapPat =<< layoutPat lPat- expDoc <- docSharedWrapper layoutExpr expr- docAlt- [ docCols- ColBindStmt- [ appSep patDoc- , docSeq- [ appSep $ docLit $ Text.pack "<-"- , docAddBaseY BrIndentRegular $ docForceParSpacing expDoc- ]- ]- , docCols- ColBindStmt- [ appSep patDoc- , docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "<-") (expDoc)- ]- ]- LetStmt _ binds -> do- let isFree = indentPolicy == IndentPolicyFree- let indentFourPlus = indentAmount >= 4- layoutLocalBinds binds >>= \case- Nothing -> docLit $ Text.pack "let"- -- i just tested the above, and it is indeed allowed. heh.- Just [] -> docLit $ Text.pack "let" -- this probably never happens- Just [bindDoc] -> docAlt- [ -- let bind = expr- docCols- ColDoLet- [ appSep $ docLit $ Text.pack "let"- , let- f = case indentPolicy of- IndentPolicyFree -> docSetBaseAndIndent- IndentPolicyLeft -> docForceSingleline- IndentPolicyMultiple | indentFourPlus -> docSetBaseAndIndent- | otherwise -> docForceSingleline- in f $ return bindDoc- ]- , -- let- -- bind = expr- docAddBaseY BrIndentRegular $ docPar- (docLit $ Text.pack "let")- (docSetBaseAndIndent $ return bindDoc)- ]- Just bindDocs -> runFilteredAlternative $ do- -- let aaa = expra- -- bbb = exprb- -- ccc = exprc- addAlternativeCond (isFree || indentFourPlus) $ docSeq- [ appSep $ docLit $ Text.pack "let"- , let f = if indentFourPlus- then docEnsureIndent BrIndentRegular- else docSetBaseAndIndent- in f $ docLines $ return <$> bindDocs- ]- -- let- -- aaa = expra- -- bbb = exprb- -- ccc = exprc- addAlternativeCond (not indentFourPlus)- $ docAddBaseY BrIndentRegular- $ docPar (docLit $ Text.pack "let")- (docSetBaseAndIndent $ docLines $ return <$> bindDocs)- RecStmt _ stmts _ _ _ _ _ -> runFilteredAlternative $ do- -- rec stmt1- -- stmt2- -- stmt3- addAlternativeCond (indentPolicy == IndentPolicyFree) $ docSeq- [ docLit (Text.pack "rec")- , docSeparator- , docSetBaseAndIndent $ docLines $ layoutStmt <$> stmts- ]- -- rec- -- stmt1- -- stmt2- -- stmt3- addAlternative $ docAddBaseY BrIndentRegular $ docPar- (docLit (Text.pack "rec"))- (docLines $ layoutStmt <$> stmts)- BodyStmt _ expr _ _ -> do- expDoc <- docSharedWrapper layoutExpr expr- docAddBaseY BrIndentRegular $ expDoc- _ -> briDocByExactInlineOnly "some unknown statement" lstmt
− source/library/Language/Haskell/Brittany/Internal/Layouters/Stmt.hs-boot
@@ -1,10 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Stmt where--import GHC.Hs-import Language.Haskell.Brittany.Internal.Types----layoutStmt :: ToBriDoc' (StmtLR GhcPs GhcPs (LHsExpr GhcPs))
− source/library/Language/Haskell/Brittany/Internal/Layouters/Type.hs
@@ -1,643 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Layouters.Type where--import qualified Data.Text as Text-import GHC (AnnKeywordId(..), GenLocated(L))-import GHC.Hs-import qualified GHC.OldList as List-import GHC.Types.Basic-import GHC.Utils.Outputable (ftext, showSDocUnsafe)-import Language.Haskell.Brittany.Internal.LayouterBasics-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils- (FirstLastView(..), splitFirstLast)----layoutType :: ToBriDoc HsType-layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of- -- _ | traceShow (ExactPrint.Types.mkAnnKey ltype) False -> error "impossible"- HsTyVar _ promoted name -> do- t <- lrdrNameToTextAnnTypeEqualityIsSpecial name- case promoted of- IsPromoted -> docSeq- [ docSeparator- , docTick- , docWrapNode name $ docLit t- ]- NotPromoted -> docWrapNode name $ docLit t- HsForAllTy _ hsf (L _ (HsQualTy _ (L _ cntxts) typ2)) -> do- let bndrs = getBinders hsf- typeDoc <- docSharedWrapper layoutType typ2- tyVarDocs <- layoutTyVarBndrs bndrs- cntxtDocs <- cntxts `forM` docSharedWrapper layoutType- let maybeForceML = case typ2 of- (L _ HsFunTy{}) -> docForceMultiline- _ -> id- let- tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs- forallDoc = docAlt- [ let- open = docLit $ Text.pack "forall"- in docSeq ([open]++tyVarDocLineList)- , docPar- (docLit (Text.pack "forall"))- (docLines- $ tyVarDocs <&> \case- (tname, Nothing) -> docEnsureIndent BrIndentRegular $ docLit tname- (tname, Just doc) -> docEnsureIndent BrIndentRegular- $ docLines- [ docCols ColTyOpPrefix- [ docParenLSep- , docLit tname- ]- , docCols ColTyOpPrefix- [ docLit $ Text.pack ":: "- , doc- ]- , docLit $ Text.pack ")"- ])- ]- contextDoc = case cntxtDocs of- [] -> docLit $ Text.pack "()"- [x] -> x- _ -> docAlt- [ let- open = docLit $ Text.pack "("- close = docLit $ Text.pack ")"- list = List.intersperse docCommaSep- $ docForceSingleline <$> cntxtDocs- in docSeq ([open]++list++[close])- , let- open = docCols ColTyOpPrefix- [ docParenLSep- , docAddBaseY (BrIndentSpecial 2) $ head cntxtDocs- ]- close = docLit $ Text.pack ")"- list = List.tail cntxtDocs <&> \cntxtDoc ->- docCols ColTyOpPrefix- [ docCommaSep- , docAddBaseY (BrIndentSpecial 2) cntxtDoc- ]- in docPar open $ docLines $ list ++ [close]- ]- docAlt- -- :: forall a b c . (Foo a b c) => a b -> c- [ docSeq- [ if null bndrs- then docEmpty- else let- open = docLit $ Text.pack "forall"- close = docLit $ Text.pack " . "- in docSeq ([open, docSeparator]++tyVarDocLineList++[close])- , docForceSingleline contextDoc- , docLit $ Text.pack " => "- , docForceSingleline typeDoc- ]- -- :: forall a b c- -- . (Foo a b c)- -- => a b- -- -> c- , docPar- forallDoc- ( docLines- [ docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ docLit $ Text.pack " . "- , docAddBaseY (BrIndentSpecial 3)- $ contextDoc- ]- , docCols ColTyOpPrefix- [ docLit $ Text.pack "=> "- , docAddBaseY (BrIndentSpecial 3) $ maybeForceML $ typeDoc- ]- ]- )- ]- HsForAllTy _ hsf typ2 -> do- let bndrs = getBinders hsf- typeDoc <- layoutType typ2- tyVarDocs <- layoutTyVarBndrs bndrs- let maybeForceML = case typ2 of- (L _ HsFunTy{}) -> docForceMultiline- _ -> id- let tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs- docAlt- -- forall x . x- [ docSeq- [ if null bndrs- then docEmpty- else let- open = docLit $ Text.pack "forall"- close = docLit $ Text.pack " . "- in docSeq ([open]++tyVarDocLineList++[close])- , docForceSingleline $ return $ typeDoc- ]- -- :: forall x- -- . x- , docPar- (docSeq $ docLit (Text.pack "forall") : tyVarDocLineList)- ( docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ docLit $ Text.pack " . "- , maybeForceML $ return typeDoc- ]- )- -- :: forall- -- (x :: *)- -- . x- , docPar- (docLit (Text.pack "forall"))- (docLines- $ (tyVarDocs <&> \case- (tname, Nothing) -> docEnsureIndent BrIndentRegular $ docLit tname- (tname, Just doc) -> docEnsureIndent BrIndentRegular- $ docLines- [ docCols ColTyOpPrefix- [ docParenLSep- , docLit tname- ]- , docCols ColTyOpPrefix- [ docLit $ Text.pack ":: "- , doc- ]- , docLit $ Text.pack ")"- ]- )- ++[ docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ docLit $ Text.pack " . "- , maybeForceML $ return typeDoc- ]- ]- )- ]- HsQualTy _ lcntxts@(L _ cntxts) typ1 -> do- typeDoc <- docSharedWrapper layoutType typ1- cntxtDocs <- cntxts `forM` docSharedWrapper layoutType- let- contextDoc = docWrapNode lcntxts $ case cntxtDocs of- [] -> docLit $ Text.pack "()"- [x] -> x- _ -> docAlt- [ let- open = docLit $ Text.pack "("- close = docLit $ Text.pack ")"- list = List.intersperse docCommaSep- $ docForceSingleline <$> cntxtDocs- in docSeq ([open]++list++[close])- , let- open = docCols ColTyOpPrefix- [ docParenLSep- , docAddBaseY (BrIndentSpecial 2)- $ head cntxtDocs- ]- close = docLit $ Text.pack ")"- list = List.tail cntxtDocs <&> \cntxtDoc ->- docCols ColTyOpPrefix- [ docCommaSep- , docAddBaseY (BrIndentSpecial 2)- $ cntxtDoc- ]- in docPar open $ docLines $ list ++ [close]- ]- let maybeForceML = case typ1 of- (L _ HsFunTy{}) -> docForceMultiline- _ -> id- docAlt- -- (Foo a b c) => a b -> c- [ docSeq- [ docForceSingleline contextDoc- , docLit $ Text.pack " => "- , docForceSingleline typeDoc- ]- -- (Foo a b c)- -- => a b- -- -> c- , docPar- (docForceSingleline contextDoc)- ( docCols ColTyOpPrefix- [ docLit $ Text.pack "=> "- , docAddBaseY (BrIndentSpecial 3) $ maybeForceML typeDoc- ]- )- ]- HsFunTy _ _ typ1 typ2 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- typeDoc2 <- docSharedWrapper layoutType typ2- let maybeForceML = case typ2 of- (L _ HsFunTy{}) -> docForceMultiline- _ -> id- hasComments <- hasAnyCommentsBelow ltype- docAlt $- [ docSeq- [ appSep $ docForceSingleline typeDoc1- , appSep $ docLit $ Text.pack "->"- , docForceSingleline typeDoc2- ]- | not hasComments- ] ++- [ docPar- (docNodeAnnKW ltype Nothing typeDoc1)- ( docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ appSep $ docLit $ Text.pack "->"- , docAddBaseY (BrIndentSpecial 3)- $ maybeForceML typeDoc2- ]- )- ]- HsParTy _ typ1 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- docAlt- [ docSeq- [ docWrapNodeRest ltype $ docLit $ Text.pack "("- , docForceSingleline typeDoc1- , docLit $ Text.pack ")"- ]- , docPar- ( docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ docParenLSep- , docAddBaseY (BrIndentSpecial 2) $ typeDoc1- ])- (docLit $ Text.pack ")")- ]- HsAppTy _ typ1@(L _ HsAppTy{}) typ2 -> do- let gather :: [LHsType GhcPs] -> LHsType GhcPs -> (LHsType GhcPs, [LHsType GhcPs])- gather list = \case- L _ (HsAppTy _ ty1 ty2) -> gather (ty2:list) ty1- final -> (final, list)- let (typHead, typRest) = gather [typ2] typ1- docHead <- docSharedWrapper layoutType typHead- docRest <- docSharedWrapper layoutType `mapM` typRest- docAlt- [ docSeq- $ docForceSingleline docHead : (docRest >>= \d ->- [ docSeparator, docForceSingleline d ])- , docPar docHead (docLines $ docEnsureIndent BrIndentRegular <$> docRest)- ]- HsAppTy _ typ1 typ2 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- typeDoc2 <- docSharedWrapper layoutType typ2- docAlt- [ docSeq- [ docForceSingleline typeDoc1- , docSeparator- , docForceSingleline typeDoc2- ]- , docPar- typeDoc1- (docEnsureIndent BrIndentRegular typeDoc2)- ]- HsListTy _ typ1 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- docAlt- [ docSeq- [ docWrapNodeRest ltype $ docLit $ Text.pack "["- , docForceSingleline typeDoc1- , docLit $ Text.pack "]"- ]- , docPar- ( docCols ColTyOpPrefix- [ docWrapNodeRest ltype $ docLit $ Text.pack "[ "- , docAddBaseY (BrIndentSpecial 2) $ typeDoc1- ])- (docLit $ Text.pack "]")- ]- HsTupleTy _ tupleSort typs -> case tupleSort of- HsUnboxedTuple -> unboxed- HsBoxedTuple -> simple- HsConstraintTuple -> simple- HsBoxedOrConstraintTuple -> simple- where- unboxed = if null typs then error "brittany internal error: unboxed unit"- else unboxedL- simple = if null typs then unitL else simpleL- unitL = docLit $ Text.pack "()"- simpleL = do- docs <- docSharedWrapper layoutType `mapM` typs- let end = docLit $ Text.pack ")"- lines = List.tail docs <&> \d ->- docAddBaseY (BrIndentSpecial 2)- $ docCols ColTyOpPrefix [docCommaSep, d]- commaDocs = List.intersperse docCommaSep (docForceSingleline <$> docs)- docAlt- [ docSeq $ [docLit $ Text.pack "("]- ++ docWrapNodeRest ltype commaDocs- ++ [end]- , let line1 = docCols ColTyOpPrefix [docParenLSep, head docs]- in docPar- (docAddBaseY (BrIndentSpecial 2) $ line1)- (docLines $ docWrapNodeRest ltype lines ++ [end])- ]- unboxedL = do- docs <- docSharedWrapper layoutType `mapM` typs- let start = docParenHashLSep- end = docParenHashRSep- docAlt- [ docSeq $ [start]- ++ docWrapNodeRest ltype (List.intersperse docCommaSep docs)- ++ [end]- , let- line1 = docCols ColTyOpPrefix [start, head docs]- lines = List.tail docs <&> \d ->- docAddBaseY (BrIndentSpecial 2)- $ docCols ColTyOpPrefix [docCommaSep, d]- in docPar- (docAddBaseY (BrIndentSpecial 2) line1)- (docLines $ lines ++ [end])- ]- HsOpTy{} -> -- TODO- briDocByExactInlineOnly "HsOpTy{}" ltype- -- HsOpTy typ1 opName typ2 -> do- -- -- TODO: these need some proper fixing. precedences don't add up.- -- -- maybe the parser just returns some trivial right recursion- -- -- parse result for any type level operators.- -- -- need to check how things are handled on the expression level.- -- let opStr = lrdrNameToText opName- -- let opLen = Text.length opStr- -- layouter1@(Layouter desc1 _ _) <- layoutType typ1- -- layouter2@(Layouter desc2 _ _) <- layoutType typ2- -- let line = do -- Maybe- -- l1 <- _ldesc_line desc1- -- l2 <- _ldesc_line desc2- -- let len1 = _lColumns_min l1- -- let len2 = _lColumns_min l2- -- let len = 2 + opLen + len1 + len2- -- return $ LayoutColumns- -- { _lColumns_key = ColumnKeyUnique- -- , _lColumns_lengths = [len]- -- , _lColumns_min = len- -- }- -- let block = do -- Maybe- -- rol1 <- descToBlockStart desc1- -- (min2, max2) <- descToMinMax (1+opLen) desc2- -- let (minR, maxR) = case descToBlockMinMax desc1 of- -- Nothing -> (min2, max2)- -- Just (min1, max1) -> (max min1 min2, max max1 max2)- -- return $ BlockDesc- -- { _bdesc_blockStart = rol1- -- , _bdesc_min = minR- -- , _bdesc_max = maxR- -- , _bdesc_opIndentFloatUp = Just (1+opLen)- -- }- -- return $ Layouter- -- { _layouter_desc = LayoutDesc- -- { _ldesc_line = line- -- , _ldesc_block = block- -- }- -- , _layouter_func = \params -> do- -- remaining <- getCurRemaining- -- let allowSameLine = _params_sepLines params /= SepLineTypeOp- -- case line of- -- Just (LayoutColumns _ _ m) | m <= remaining && allowSameLine -> do- -- applyLayouterRestore layouter1 defaultParams- -- layoutWriteAppend $ Text.pack " " <> opStr <> Text.pack " "- -- applyLayouterRestore layouter2 defaultParams- -- _ -> do- -- let upIndent = maybe (1+opLen) (max (1+opLen)) $ _params_opIndent params- -- let downIndent = maybe upIndent (max upIndent) $ _bdesc_opIndentFloatUp =<< _ldesc_block desc2- -- layoutWithAddIndentN downIndent $ applyLayouterRestore layouter1 defaultParams- -- layoutWriteNewline- -- layoutWriteAppend $ opStr <> Text.pack " "- -- layoutWriteEnsureBlockPlusN downIndent- -- applyLayouterRestore layouter2 defaultParams- -- { _params_sepLines = SepLineTypeOp- -- , _params_opIndent = Just downIndent- -- }- -- , _layouter_ast = ltype- -- }- HsIParamTy _ (L _ (HsIPName ipName)) typ1 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- docAlt- [ docSeq- [ docWrapNodeRest ltype- $ docLit- $ Text.pack ("?" ++ showSDocUnsafe (ftext ipName) ++ "::")- , docForceSingleline typeDoc1- ]- , docPar- ( docLit- $ Text.pack ("?" ++ showSDocUnsafe (ftext ipName))- )- (docCols ColTyOpPrefix- [ docWrapNodeRest ltype- $ docLit $ Text.pack ":: "- , docAddBaseY (BrIndentSpecial 2) typeDoc1- ])- ]- -- TODO: test KindSig- HsKindSig _ typ1 kind1 -> do- typeDoc1 <- docSharedWrapper layoutType typ1- kindDoc1 <- docSharedWrapper layoutType kind1- hasParens <- hasAnnKeyword ltype AnnOpenP- docAlt- [ 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- ]- )- ]- HsBangTy{} -> -- TODO- briDocByExactInlineOnly "HsBangTy{}" ltype- -- HsBangTy bang typ1 -> do- -- let bangStr = case bang of- -- HsSrcBang _ unpackness strictness ->- -- (++)- -- (case unpackness of- -- SrcUnpack -> "{-# UNPACK -#} "- -- SrcNoUnpack -> "{-# NOUNPACK -#} "- -- NoSrcUnpack -> ""- -- )- -- (case strictness of- -- SrcLazy -> "~"- -- SrcStrict -> "!"- -- NoSrcStrict -> ""- -- )- -- let bangLen = length bangStr- -- layouter@(Layouter desc _ _) <- layoutType typ1- -- let line = do -- Maybe- -- l <- _ldesc_line desc- -- let len = bangLen + _lColumns_min l- -- return $ LayoutColumns- -- { _lColumns_key = ColumnKeyUnique- -- , _lColumns_lengths = [len]- -- , _lColumns_min = len- -- }- -- let block = do -- Maybe- -- rol <- descToBlockStart desc- -- (minR,maxR) <- descToBlockMinMax desc- -- return $ BlockDesc- -- { _bdesc_blockStart = rol- -- , _bdesc_min = minR- -- , _bdesc_max = maxR- -- , _bdesc_opIndentFloatUp = Nothing- -- }- -- return $ Layouter- -- { _layouter_desc = LayoutDesc- -- { _ldesc_line = line- -- , _ldesc_block = block- -- }- -- , _layouter_func = \_params -> do- -- remaining <- getCurRemaining- -- case line of- -- Just (LayoutColumns _ _ m) | m <= remaining -> do- -- layoutWriteAppend $ Text.pack $ bangStr- -- applyLayouterRestore layouter defaultParams- -- _ -> do- -- layoutWriteAppend $ Text.pack $ bangStr- -- layoutWritePostCommentsRestore ltype- -- applyLayouterRestore layouter defaultParams- -- , _layouter_ast = ltype- -- }- HsSpliceTy{} -> -- TODO- briDocByExactInlineOnly "HsSpliceTy{}" ltype- HsDocTy{} -> -- TODO- briDocByExactInlineOnly "HsDocTy{}" ltype- HsRecTy{} -> -- TODO- briDocByExactInlineOnly "HsRecTy{}" ltype- HsExplicitListTy _ _ typs -> do- typDocs <- docSharedWrapper layoutType `mapM` typs- hasComments <- hasAnyCommentsBelow ltype- let specialCommaSep = appSep $ docLit $ Text.pack " ,"- docAlt- [ docSeq- $ [docLit $ Text.pack "'["]- ++ List.intersperse specialCommaSep (docForceSingleline <$> typDocs)- ++ [docLit $ Text.pack "]"]- , case splitFirstLast typDocs of- FirstLastEmpty -> docSeq- [ docLit $ Text.pack "'["- , docNodeAnnKW ltype (Just AnnOpenS) $ docLit $ Text.pack "]"- ]- FirstLastSingleton e -> docAlt- [ docSeq- [ docLit $ Text.pack "'["- , docNodeAnnKW ltype (Just AnnOpenS) $ docForceSingleline e- , docLit $ Text.pack "]"- ]- , docSetBaseY $ docLines- [ docSeq- [ docLit $ Text.pack "'["- , docSeparator- , docSetBaseY $ docNodeAnnKW ltype (Just AnnOpenS) e- ]- , docLit $ Text.pack " ]"- ]- ]- FirstLast e1 ems eN -> runFilteredAlternative $ do- addAlternativeCond (not hasComments)- $ docSeq- $ [docLit $ Text.pack "'["]- ++ List.intersperse specialCommaSep (docForceSingleline <$> (e1:ems ++ [docNodeAnnKW ltype (Just AnnOpenS) eN]))- ++ [docLit $ Text.pack " ]"]- addAlternative $- let- start = docCols ColList- [appSep $ docLit $ Text.pack "'[", e1]- linesM = ems <&> \d ->- docCols ColList [specialCommaSep, d]- lineN = docCols ColList [specialCommaSep, docNodeAnnKW ltype (Just AnnOpenS) eN]- end = docLit $ Text.pack " ]"- in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN] ++ [end]- ]- HsExplicitTupleTy{} -> -- TODO- briDocByExactInlineOnly "HsExplicitTupleTy{}" ltype- HsTyLit _ lit -> case lit of- HsNumTy (SourceText srctext) _ -> docLit $ Text.pack srctext- HsNumTy NoSourceText _ ->- error "overLitValBriDoc: literal with no SourceText"- HsStrTy (SourceText srctext) _ -> docLit $ Text.pack srctext- HsStrTy NoSourceText _ ->- error "overLitValBriDoc: literal with no SourceText"- HsWildCardTy _ ->- docLit $ Text.pack "_"- HsSumTy{} -> -- TODO- briDocByExactInlineOnly "HsSumTy{}" ltype- HsStarTy _ isUnicode -> do- if isUnicode- then docLit $ Text.pack "\x2605" -- Unicode star- else docLit $ Text.pack "*"- XHsType{} -> error "brittany internal error: XHsType"- HsAppKindTy _ ty kind -> do- t <- docSharedWrapper layoutType ty- k <- docSharedWrapper layoutType kind- docAlt- [ docSeq- [ docForceSingleline t- , docSeparator- , docLit $ Text.pack "@"- , docForceSingleline k- ]- , docPar- t- (docSeq [docLit $ Text.pack "@", k ])- ]--layoutTyVarBndrs- :: [LHsTyVarBndr () GhcPs]- -> ToBriDocM [(Text, Maybe (ToBriDocM BriDocNumbered))]-layoutTyVarBndrs = mapM $ \case- (L _ (UserTyVar _ _ name)) -> return $ (lrdrNameToText name, Nothing)- (L _ (KindedTyVar _ _ lrdrName kind)) -> do- d <- docSharedWrapper layoutType kind- return $ (lrdrNameToText lrdrName, Just $ d)---- there is no specific reason this returns a list instead of a single--- BriDoc node.-processTyVarBndrsSingleline- :: [(Text, Maybe (ToBriDocM BriDocNumbered))] -> [ToBriDocM BriDocNumbered]-processTyVarBndrsSingleline bndrDocs = bndrDocs >>= \case- (tname, Nothing) -> [docSeparator, docLit tname]- (tname, Just doc) ->- [ docSeparator- , docLit $ Text.pack "(" <> tname <> Text.pack " :: "- , docForceSingleline $ doc- , docLit $ Text.pack ")"- ]--getBinders :: HsForAllTelescope pass -> [LHsTyVarBndr () pass]-getBinders x = case x of- HsForAllVis _ b -> b- HsForAllInvis _ b -> fmap withoutSpecificity b- XHsForAllTelescope _ -> []--withoutSpecificity :: LHsTyVarBndr flag pass -> LHsTyVarBndr () pass-withoutSpecificity = fmap $ \case- UserTyVar a _ c -> UserTyVar a () c- KindedTyVar a _ c d -> KindedTyVar a () c d- XTyVarBndr a -> XTyVarBndr a
− source/library/Language/Haskell/Brittany/Internal/Obfuscation.hs
@@ -1,101 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Obfuscation where--import Data.Char-import qualified Data.Map as Map-import qualified Data.Set as Set-import qualified Data.Text as Text-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import System.Random----obfuscate :: Text -> IO Text-obfuscate input = do- let predi x = isAlphaNum x || x `elem` "_'"- let groups = List.groupBy (\a b -> predi a && predi b) (Text.unpack input)- let idents = Set.toList $ Set.fromList $ filter (all predi) groups- let exceptionFilter x | x `elem` keywords = False- exceptionFilter x | x `elem` extraKWs = False- exceptionFilter x = not $ null $ drop 1 x- let filtered = filter exceptionFilter idents- mappings <- fmap Map.fromList $ filtered `forM` \x -> do- r <- createAlias x- pure (x, r)- let groups' = groups <&> \w -> fromMaybe w (Map.lookup w mappings)- pure $ Text.concat $ fmap Text.pack groups'--keywords :: [String]-keywords =- [ "case"- , "class"- , "data"- , "default"- , "deriving"- , "do"- , "mdo"- , "else"- , "forall"- , "if"- , "import"- , "in"- , "infix"- , "infixl"- , "infixr"- , "instance"- , "let"- , "module"- , "newtype"- , "of"- , "qualified"- , "then"- , "type"- , "where"- , "_"- , "foreign"- , "ccall"- , "as"- , "safe"- , "unsafe"- , "hiding"- , "proc"- , "rec"- , "family"- ]--extraKWs :: [String]-extraKWs = ["return", "pure", "Int", "True", "False", "otherwise"]--createAlias :: String -> IO String-createAlias xs = go NoHint xs- where- go _hint "" = pure ""- go hint (c : cr) = do- c' <- case hint of- VocalHint | isUpper c -> randomFrom $ "AAAEEEOOOIIIUUU" ++ ['A' .. 'Z']- _ | isUpper c -> randomFrom ['A' .. 'Z']- VocalHint | isLower c -> randomFrom $ "aaaeeeoooiiiuuu" ++ ['a' .. 'z']- _ | isLower c -> randomFrom ['a' .. 'z']- _ -> pure c- cr' <- go (if c' `elem` "aeuioAEUIO" then NoVocalHint else VocalHint) cr- pure (c' : cr')--data Hint = NoHint | VocalHint | NoVocalHint--_randomRange :: Random a => a -> a -> IO a-_randomRange lo hi = do- gen <- getStdGen- let (x, gen') = randomR (lo, hi) gen- setStdGen gen'- pure x--randomFrom :: [a] -> IO a-randomFrom l = do- let hi = length l - 1- gen <- getStdGen- let (x, gen') = randomR (0, hi) gen- setStdGen gen'- pure $ l List.!! x
− source/library/Language/Haskell/Brittany/Internal/ParseModule.hs
@@ -1,500 +0,0 @@-{-# OPTIONS_GHC -Wno-implicit-prelude #-}--module Language.Haskell.Brittany.Internal.ParseModule where--import qualified Control.Monad as Monad-import qualified Control.Monad.IO.Class as IO-import qualified Control.Monad.Trans.Except as Except-import qualified Data.Set as Set-import qualified GHC-import qualified GHC.ByteOrder-import qualified GHC.Data.Bag-import qualified GHC.Data.EnumSet-import qualified GHC.Data.StringBuffer-import qualified GHC.Driver.Session-import qualified GHC.Parser.Header-import qualified GHC.Platform-import qualified GHC.Settings-import qualified GHC.Types.Basic-import qualified GHC.Types.SrcLoc-import qualified GHC.Unit.Module.Name-import qualified GHC.Unit.State-import qualified GHC.Unit.Types-import qualified GHC.Utils.Error-import qualified GHC.Utils.Fingerprint-import qualified GHC.Utils.Misc-import qualified GHC.Utils.Ppr.Colour-import qualified Language.Haskell.GHC.ExactPrint.Parsers as ExactPrint-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint---- | Parses a Haskell module. Although this nominally requires IO, it is--- morally pure. It should have no observable effects.-parseModule- :: IO.MonadIO io- => [String]- -> FilePath- -> (GHC.Driver.Session.DynFlags -> io (Either String a))- -> String- -> io (Either String (ExactPrint.Anns, GHC.ParsedSource, a))-parseModule arguments1 filePath checkDynFlags string = Except.runExceptT $ do- let- dynFlags1 = GHC.Driver.Session.gopt_set- -- It feels like this should be either @Sf_Ignore@ or @Sf_None@, but both- -- of those modes have trouble parsing safe imports (@import safe ...@).- -- Neither passing in @"-XUnsafe"@ as a command line argument nor having- -- @{-# LANGUAGE Unsafe #-}@ in the source file seem to help.- initialDynFlags- { GHC.Driver.Session.safeHaskell = GHC.Driver.Session.Sf_Unsafe- }- GHC.Driver.Session.Opt_KeepRawTokenStream- (dynFlags2, leftovers1, _) <-- GHC.Driver.Session.parseDynamicFlagsCmdLine dynFlags1- $ fmap GHC.Types.SrcLoc.noLoc arguments1- handleLeftovers leftovers1- let- stringBuffer = GHC.Data.StringBuffer.stringToStringBuffer string- arguments2 = GHC.Parser.Header.getOptions dynFlags2 stringBuffer filePath- (dynFlags3, leftovers2, _) <- GHC.Driver.Session.parseDynamicFilePragma- dynFlags2- arguments2- handleLeftovers leftovers2- dynFlagsResult <- Except.ExceptT $ checkDynFlags dynFlags3- let- parseResult =- ExactPrint.parseModuleFromStringInternal dynFlags3 filePath string- case parseResult of- Left errorMessages -> handleErrorMessages errorMessages- Right (anns, parsedSource) -> pure (anns, parsedSource, dynFlagsResult)--handleLeftovers- :: Monad m => [GHC.Types.SrcLoc.Located String] -> Except.ExceptT String m ()-handleLeftovers leftovers =- Monad.unless (null leftovers) . Except.throwE $ "leftovers: " <> show- (fmap GHC.Types.SrcLoc.unLoc leftovers)--handleErrorMessages- :: Monad m => GHC.Utils.Error.ErrorMessages -> Except.ExceptT String m a-handleErrorMessages =- Except.throwE . mappend "errorMessages: " . show . GHC.Data.Bag.bagToList--initialCfgWeights :: GHC.Driver.Session.CfgWeights-initialCfgWeights = GHC.Driver.Session.CFGWeights- { GHC.Driver.Session.backEdgeBonus = 0- , GHC.Driver.Session.callWeight = 0- , GHC.Driver.Session.condBranchWeight = 0- , GHC.Driver.Session.infoTablePenalty = 0- , GHC.Driver.Session.likelyCondWeight = 0- , GHC.Driver.Session.switchWeight = 0- , GHC.Driver.Session.uncondWeight = 0- , GHC.Driver.Session.unlikelyCondWeight = 0- }--initialDynFlags :: GHC.Driver.Session.DynFlags-initialDynFlags = GHC.Driver.Session.DynFlags- { GHC.Driver.Session.avx = False- , GHC.Driver.Session.avx2 = False- , GHC.Driver.Session.avx512cd = False- , GHC.Driver.Session.avx512er = False- , GHC.Driver.Session.avx512f = False- , GHC.Driver.Session.avx512pf = False- , GHC.Driver.Session.binBlobThreshold = 0- , GHC.Driver.Session.bmiVersion = Nothing- , GHC.Driver.Session.cachedPlugins = []- , GHC.Driver.Session.canGenerateDynamicToo = error "canGenerateDynamicToo"- , GHC.Driver.Session.canUseColor = False- , GHC.Driver.Session.cfgWeightInfo = initialCfgWeights- , GHC.Driver.Session.cmdlineFrameworks = []- , GHC.Driver.Session.cmmProcAlignment = Nothing- , GHC.Driver.Session.colScheme = GHC.Utils.Ppr.Colour.defaultScheme- , GHC.Driver.Session.debugLevel = 0- , GHC.Driver.Session.depExcludeMods = []- , GHC.Driver.Session.depIncludeCppDeps = False- , GHC.Driver.Session.depIncludePkgDeps = False- , GHC.Driver.Session.depMakefile = ""- , GHC.Driver.Session.depSuffixes = []- , GHC.Driver.Session.dirsToClean = error "dirsToClean"- , GHC.Driver.Session.dump_action = \_ _ _ _ _ _ -> pure ()- , GHC.Driver.Session.dumpDir = Nothing- , GHC.Driver.Session.dumpFlags = GHC.Data.EnumSet.fromList []- , GHC.Driver.Session.dumpPrefix = Nothing- , GHC.Driver.Session.dumpPrefixForce = Nothing- , GHC.Driver.Session.dylibInstallName = Nothing- , GHC.Driver.Session.dynHiSuf = ""- , GHC.Driver.Session.dynLibLoader = GHC.Driver.Session.Deployable- , GHC.Driver.Session.dynObjectSuf = ""- , GHC.Driver.Session.dynOutputFile = Nothing- , GHC.Driver.Session.enableTimeStats = False- , GHC.Driver.Session.extensionFlags = GHC.Data.EnumSet.fromList []- , GHC.Driver.Session.extensions = []- , GHC.Driver.Session.fatalWarningFlags = GHC.Data.EnumSet.fromList []- , GHC.Driver.Session.fileSettings = initialFileSettings- , GHC.Driver.Session.filesToClean = error "filesToClean"- , GHC.Driver.Session.floatLamArgs = Nothing- , GHC.Driver.Session.flushErr = GHC.Driver.Session.defaultFlushErr- , GHC.Driver.Session.flushOut = GHC.Driver.Session.defaultFlushOut- , GHC.Driver.Session.frameworkPaths = []- , GHC.Driver.Session.frontendPluginOpts = []- , GHC.Driver.Session.generalFlags = GHC.Data.EnumSet.fromList []- , GHC.Driver.Session.generatedDumps = error "generatedDumps"- , GHC.Driver.Session.ghcHeapSize = Nothing- , GHC.Driver.Session.ghciHistSize = 0- , GHC.Driver.Session.ghciScripts = []- , GHC.Driver.Session.ghcLink = GHC.Driver.Session.NoLink- , GHC.Driver.Session.ghcMode = GHC.Driver.Session.OneShot- , GHC.Driver.Session.ghcNameVersion = initialGhcNameVersion- , GHC.Driver.Session.ghcVersionFile = Nothing- , GHC.Driver.Session.haddockOptions = Nothing- , GHC.Driver.Session.hcSuf = ""- , GHC.Driver.Session.hiDir = Nothing- , GHC.Driver.Session.hieDir = Nothing- , GHC.Driver.Session.hieSuf = ""- , GHC.Driver.Session.historySize = 0- , GHC.Driver.Session.hiSuf = ""- , GHC.Driver.Session.homeUnitId = GHC.Unit.Types.stringToUnitId ""- , GHC.Driver.Session.homeUnitInstanceOfId = Nothing- , GHC.Driver.Session.homeUnitInstantiations = []- , GHC.Driver.Session.hooks = error "hooks"- , GHC.Driver.Session.hpcDir = ""- , GHC.Driver.Session.hscTarget = GHC.Driver.Session.HscNothing- , GHC.Driver.Session.ignorePackageFlags = []- , GHC.Driver.Session.importPaths = []- , GHC.Driver.Session.includePaths = initialIncludeSpecs- , GHC.Driver.Session.incoherentOnLoc = initialSrcSpan- , GHC.Driver.Session.initialUnique = 0- , GHC.Driver.Session.inlineCheck = Nothing- , GHC.Driver.Session.interactivePrint = Nothing- , GHC.Driver.Session.language = Nothing- , GHC.Driver.Session.ldInputs = []- , GHC.Driver.Session.liberateCaseThreshold = Nothing- , GHC.Driver.Session.libraryPaths = []- , GHC.Driver.Session.liftLamsKnown = False- , GHC.Driver.Session.liftLamsNonRecArgs = Nothing- , GHC.Driver.Session.liftLamsRecArgs = Nothing- , GHC.Driver.Session.llvmConfig = initialLlvmConfig- , GHC.Driver.Session.log_action = \_ _ _ _ _ -> pure ()- , GHC.Driver.Session.mainFunIs = Nothing- , GHC.Driver.Session.mainModIs = GHC.Unit.Types.mkModule- (GHC.Unit.Types.stringToUnit "")- (GHC.Unit.Module.Name.mkModuleName "")- , GHC.Driver.Session.maxErrors = Nothing- , GHC.Driver.Session.maxInlineAllocSize = 0- , GHC.Driver.Session.maxInlineMemcpyInsns = 0- , GHC.Driver.Session.maxInlineMemsetInsns = 0- , GHC.Driver.Session.maxPmCheckModels = 0- , GHC.Driver.Session.maxRefHoleFits = Nothing- , GHC.Driver.Session.maxRelevantBinds = Nothing- , GHC.Driver.Session.maxSimplIterations = 0- , GHC.Driver.Session.maxUncoveredPatterns = 0- , GHC.Driver.Session.maxValidHoleFits = Nothing- , GHC.Driver.Session.maxWorkerArgs = 0- , GHC.Driver.Session.newDerivOnLoc = initialSrcSpan- , GHC.Driver.Session.nextTempSuffix = error "nextTempSuffix"- , GHC.Driver.Session.nextWrapperNum = error "nextWrapperNum"- , GHC.Driver.Session.objectDir = Nothing- , GHC.Driver.Session.objectSuf = ""- , GHC.Driver.Session.optLevel = 0- , GHC.Driver.Session.outputFile = Nothing- , GHC.Driver.Session.outputHi = Nothing- , GHC.Driver.Session.overlapInstLoc = initialSrcSpan- , GHC.Driver.Session.packageDBFlags = []- , GHC.Driver.Session.packageEnv = Nothing- , GHC.Driver.Session.packageFlags = []- , GHC.Driver.Session.parMakeCount = Nothing- , GHC.Driver.Session.pkgTrustOnLoc = initialSrcSpan- , GHC.Driver.Session.platformConstants = initialPlatformConstants- , GHC.Driver.Session.platformMisc = initialPlatformMisc- , GHC.Driver.Session.pluginModNameOpts = []- , GHC.Driver.Session.pluginModNames = []- , GHC.Driver.Session.pluginPackageFlags = []- , GHC.Driver.Session.pprCols = 80- , GHC.Driver.Session.pprUserLength = 0- , GHC.Driver.Session.profAuto = GHC.Driver.Session.NoProfAuto- , GHC.Driver.Session.rawSettings = []- , GHC.Driver.Session.reductionDepth = GHC.Types.Basic.mkIntWithInf 0- , GHC.Driver.Session.refLevelHoleFits = Nothing- , GHC.Driver.Session.reverseErrors = False- , GHC.Driver.Session.rtccInfo = error "rtccInfo"- , GHC.Driver.Session.rtldInfo = error "rtldInfo"- , GHC.Driver.Session.rtsOpts = Nothing- , GHC.Driver.Session.rtsOptsEnabled = GHC.Driver.Session.RtsOptsNone- , GHC.Driver.Session.rtsOptsSuggestions = False- , GHC.Driver.Session.ruleCheck = Nothing- , GHC.Driver.Session.safeHaskell = GHC.Driver.Session.Sf_Ignore- , GHC.Driver.Session.safeInfer = False- , GHC.Driver.Session.safeInferred = False- , GHC.Driver.Session.simplPhases = 0- , GHC.Driver.Session.simplTickFactor = 0- , GHC.Driver.Session.solverIterations = GHC.Types.Basic.mkIntWithInf 0- , GHC.Driver.Session.specConstrCount = Nothing- , GHC.Driver.Session.specConstrRecursive = 0- , GHC.Driver.Session.specConstrThreshold = Nothing- , GHC.Driver.Session.splitInfo = Nothing- , GHC.Driver.Session.sseVersion = Nothing- , GHC.Driver.Session.staticPlugins = []- , GHC.Driver.Session.strictnessBefore = []- , GHC.Driver.Session.stubDir = Nothing- , GHC.Driver.Session.targetPlatform = initialTargetPlatform- , GHC.Driver.Session.thOnLoc = initialSrcSpan- , GHC.Driver.Session.toolSettings = initialToolSettings- , GHC.Driver.Session.trace_action = \_ _ _ a -> a- , GHC.Driver.Session.trustFlags = []- , GHC.Driver.Session.trustworthyOnLoc = initialSrcSpan- , GHC.Driver.Session.ufCreationThreshold = 0- , GHC.Driver.Session.ufDearOp = 0- , GHC.Driver.Session.ufDictDiscount = 0- , GHC.Driver.Session.ufFunAppDiscount = 0- , GHC.Driver.Session.ufUseThreshold = 0- , GHC.Driver.Session.ufVeryAggressive = False- , GHC.Driver.Session.uniqueIncrement = 0- , GHC.Driver.Session.unitDatabases = Nothing- , GHC.Driver.Session.unitState = GHC.Unit.State.emptyUnitState- , GHC.Driver.Session.useColor = GHC.Utils.Misc.Never- , GHC.Driver.Session.useUnicode = False- , GHC.Driver.Session.verbosity = 0- , GHC.Driver.Session.warningFlags = GHC.Data.EnumSet.fromList []- , GHC.Driver.Session.warnSafeOnLoc = initialSrcSpan- , GHC.Driver.Session.warnUnsafeOnLoc = initialSrcSpan- , GHC.Driver.Session.ways = Set.empty- }--initialFileSettings :: GHC.Driver.Session.FileSettings-initialFileSettings = GHC.Driver.Session.FileSettings- { GHC.Driver.Session.fileSettings_ghciUsagePath = ""- , GHC.Driver.Session.fileSettings_ghcUsagePath = ""- , GHC.Driver.Session.fileSettings_globalPackageDatabase = ""- , GHC.Driver.Session.fileSettings_tmpDir = ""- , GHC.Driver.Session.fileSettings_toolDir = Nothing- , GHC.Driver.Session.fileSettings_topDir = ""- }--initialGhcNameVersion :: GHC.Driver.Session.GhcNameVersion-initialGhcNameVersion = GHC.Driver.Session.GhcNameVersion- { GHC.Driver.Session.ghcNameVersion_programName = ""- , GHC.Driver.Session.ghcNameVersion_projectVersion = ""- }--initialIncludeSpecs :: GHC.Driver.Session.IncludeSpecs-initialIncludeSpecs = GHC.Driver.Session.IncludeSpecs- { GHC.Driver.Session.includePathsGlobal = []- , GHC.Driver.Session.includePathsQuote = []- }--initialLlvmConfig :: GHC.Driver.Session.LlvmConfig-initialLlvmConfig = GHC.Driver.Session.LlvmConfig- { GHC.Driver.Session.llvmPasses = []- , GHC.Driver.Session.llvmTargets = []- }--initialPlatformConstants :: GHC.Settings.PlatformConstants-initialPlatformConstants = GHC.Settings.PlatformConstants- { GHC.Settings.pc_AP_STACK_SPLIM = 0- , GHC.Settings.pc_BITMAP_BITS_SHIFT = 0- , GHC.Settings.pc_BLOCK_SIZE = 0- , GHC.Settings.pc_BLOCKS_PER_MBLOCK = 0- , GHC.Settings.pc_CINT_SIZE = 0- , GHC.Settings.pc_CLONG_LONG_SIZE = 0- , GHC.Settings.pc_CLONG_SIZE = 0- , GHC.Settings.pc_CONTROL_GROUP_CONST_291 = 0- , GHC.Settings.pc_DYNAMIC_BY_DEFAULT = False- , GHC.Settings.pc_ILDV_CREATE_MASK = 0- , GHC.Settings.pc_ILDV_STATE_CREATE = 0- , GHC.Settings.pc_ILDV_STATE_USE = 0- , GHC.Settings.pc_LDV_SHIFT = 0- , GHC.Settings.pc_MAX_CHARLIKE = 0- , GHC.Settings.pc_MAX_Double_REG = 0- , GHC.Settings.pc_MAX_Float_REG = 0- , GHC.Settings.pc_MAX_INTLIKE = 0- , GHC.Settings.pc_MAX_Long_REG = 0- , GHC.Settings.pc_MAX_Real_Double_REG = 0- , GHC.Settings.pc_MAX_Real_Float_REG = 0- , GHC.Settings.pc_MAX_Real_Long_REG = 0- , GHC.Settings.pc_MAX_Real_Vanilla_REG = 0- , GHC.Settings.pc_MAX_Real_XMM_REG = 0- , GHC.Settings.pc_MAX_SPEC_AP_SIZE = 0- , GHC.Settings.pc_MAX_SPEC_SELECTEE_SIZE = 0- , GHC.Settings.pc_MAX_Vanilla_REG = 0- , GHC.Settings.pc_MAX_XMM_REG = 0- , GHC.Settings.pc_MIN_CHARLIKE = 0- , GHC.Settings.pc_MIN_INTLIKE = 0- , GHC.Settings.pc_MIN_PAYLOAD_SIZE = 0- , GHC.Settings.pc_MUT_ARR_PTRS_CARD_BITS = 0- , GHC.Settings.pc_OFFSET_bdescr_blocks = 0- , GHC.Settings.pc_OFFSET_bdescr_flags = 0- , GHC.Settings.pc_OFFSET_bdescr_free = 0- , GHC.Settings.pc_OFFSET_bdescr_start = 0- , GHC.Settings.pc_OFFSET_Capability_r = 0- , GHC.Settings.pc_OFFSET_CostCentreStack_mem_alloc = 0- , GHC.Settings.pc_OFFSET_CostCentreStack_scc_count = 0- , GHC.Settings.pc_OFFSET_StgArrBytes_bytes = 0- , GHC.Settings.pc_OFFSET_stgEagerBlackholeInfo = 0- , GHC.Settings.pc_OFFSET_StgEntCounter_allocd = 0- , GHC.Settings.pc_OFFSET_StgEntCounter_allocs = 0- , GHC.Settings.pc_OFFSET_StgEntCounter_entry_count = 0- , GHC.Settings.pc_OFFSET_StgEntCounter_link = 0- , GHC.Settings.pc_OFFSET_StgEntCounter_registeredp = 0- , GHC.Settings.pc_OFFSET_StgFunInfoExtraFwd_arity = 0- , GHC.Settings.pc_OFFSET_StgFunInfoExtraRev_arity = 0- , GHC.Settings.pc_OFFSET_stgGCEnter1 = 0- , GHC.Settings.pc_OFFSET_stgGCFun = 0- , GHC.Settings.pc_OFFSET_StgHeader_ccs = 0- , GHC.Settings.pc_OFFSET_StgHeader_ldvw = 0- , GHC.Settings.pc_OFFSET_StgMutArrPtrs_ptrs = 0- , GHC.Settings.pc_OFFSET_StgMutArrPtrs_size = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rCCCS = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rCurrentNursery = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rCurrentTSO = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rD6 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rF6 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rHp = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rHpAlloc = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rHpLim = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rL1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR10 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR6 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR7 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR8 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rR9 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rSp = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rSpLim = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rXMM6 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rYMM6 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM1 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM2 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM3 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM4 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM5 = 0- , GHC.Settings.pc_OFFSET_StgRegTable_rZMM6 = 0- , GHC.Settings.pc_OFFSET_StgSmallMutArrPtrs_ptrs = 0- , GHC.Settings.pc_OFFSET_StgStack_sp = 0- , GHC.Settings.pc_OFFSET_StgStack_stack = 0- , GHC.Settings.pc_OFFSET_StgTSO_alloc_limit = 0- , GHC.Settings.pc_OFFSET_StgTSO_cccs = 0- , GHC.Settings.pc_OFFSET_StgTSO_stackobj = 0- , GHC.Settings.pc_OFFSET_StgUpdateFrame_updatee = 0- , GHC.Settings.pc_PROF_HDR_SIZE = 0- , GHC.Settings.pc_REP_CostCentreStack_mem_alloc = 0- , GHC.Settings.pc_REP_CostCentreStack_scc_count = 0- , GHC.Settings.pc_REP_StgEntCounter_allocd = 0- , GHC.Settings.pc_REP_StgEntCounter_allocs = 0- , GHC.Settings.pc_REP_StgFunInfoExtraFwd_arity = 0- , GHC.Settings.pc_REP_StgFunInfoExtraRev_arity = 0- , GHC.Settings.pc_RESERVED_C_STACK_BYTES = 0- , GHC.Settings.pc_RESERVED_STACK_WORDS = 0- , GHC.Settings.pc_SIZEOF_CostCentreStack = 0- , GHC.Settings.pc_SIZEOF_StgArrBytes_NoHdr = 0- , GHC.Settings.pc_SIZEOF_StgFunInfoExtraRev = 0- , GHC.Settings.pc_SIZEOF_StgMutArrPtrs_NoHdr = 0- , GHC.Settings.pc_SIZEOF_StgSmallMutArrPtrs_NoHdr = 0- , GHC.Settings.pc_SIZEOF_StgSMPThunkHeader = 0- , GHC.Settings.pc_SIZEOF_StgUpdateFrame_NoHdr = 0- , GHC.Settings.pc_STD_HDR_SIZE = 0- , GHC.Settings.pc_TAG_BITS = 0- , GHC.Settings.pc_TICKY_BIN_COUNT = 0- , GHC.Settings.pc_WORD_SIZE = 0- }--initialPlatformMini :: GHC.Settings.PlatformMini-initialPlatformMini = GHC.Settings.PlatformMini- { GHC.Settings.platformMini_arch = GHC.Platform.ArchX86_64- , GHC.Settings.platformMini_os = GHC.Platform.OSLinux- }--initialPlatformMisc :: GHC.Driver.Session.PlatformMisc-initialPlatformMisc = GHC.Driver.Session.PlatformMisc- { GHC.Driver.Session.platformMisc_ghcDebugged = False- , GHC.Driver.Session.platformMisc_ghcRTSWays = ""- , GHC.Driver.Session.platformMisc_ghcRtsWithLibdw = False- , GHC.Driver.Session.platformMisc_ghcThreaded = False- , GHC.Driver.Session.platformMisc_ghcWithInterpreter = False- , GHC.Driver.Session.platformMisc_ghcWithSMP = False- , GHC.Driver.Session.platformMisc_libFFI = False- , GHC.Driver.Session.platformMisc_llvmTarget = ""- , GHC.Driver.Session.platformMisc_targetPlatformString = ""- }--initialSrcSpan :: GHC.Types.SrcLoc.SrcSpan-initialSrcSpan =- GHC.Types.SrcLoc.UnhelpfulSpan GHC.Types.SrcLoc.UnhelpfulNoLocationInfo--initialTargetPlatform :: GHC.Settings.Platform-initialTargetPlatform = GHC.Settings.Platform- { GHC.Settings.platformByteOrder = GHC.ByteOrder.LittleEndian- , GHC.Settings.platformHasGnuNonexecStack = False- , GHC.Settings.platformHasIdentDirective = False- , GHC.Settings.platformHasSubsectionsViaSymbols = False- , GHC.Settings.platformIsCrossCompiling = False- , GHC.Settings.platformLeadingUnderscore = False- , GHC.Settings.platformMini = initialPlatformMini- , GHC.Settings.platformTablesNextToCode = False- , GHC.Settings.platformUnregisterised = False- , GHC.Settings.platformWordSize = GHC.Platform.PW8- }--initialToolSettings :: GHC.Settings.ToolSettings-initialToolSettings = GHC.Settings.ToolSettings- { GHC.Settings.toolSettings_ccSupportsNoPie = False- , GHC.Settings.toolSettings_extraGccViaCFlags = []- , GHC.Settings.toolSettings_ldIsGnuLd = False- , GHC.Settings.toolSettings_ldSupportsBuildId = False- , GHC.Settings.toolSettings_ldSupportsCompactUnwind = False- , GHC.Settings.toolSettings_ldSupportsFilelist = False- , GHC.Settings.toolSettings_opt_a = []- , GHC.Settings.toolSettings_opt_c = []- , GHC.Settings.toolSettings_opt_cxx = []- , GHC.Settings.toolSettings_opt_F = []- , GHC.Settings.toolSettings_opt_i = []- , GHC.Settings.toolSettings_opt_l = []- , GHC.Settings.toolSettings_opt_L = []- , GHC.Settings.toolSettings_opt_lc = []- , GHC.Settings.toolSettings_opt_lcc = []- , GHC.Settings.toolSettings_opt_lm = []- , GHC.Settings.toolSettings_opt_lo = []- , GHC.Settings.toolSettings_opt_P = []- , GHC.Settings.toolSettings_opt_P_fingerprint =- GHC.Utils.Fingerprint.fingerprint0- , GHC.Settings.toolSettings_opt_windres = []- , GHC.Settings.toolSettings_pgm_a = ("", [])- , GHC.Settings.toolSettings_pgm_ar = ""- , GHC.Settings.toolSettings_pgm_c = ""- , GHC.Settings.toolSettings_pgm_dll = ("", [])- , GHC.Settings.toolSettings_pgm_F = ""- , GHC.Settings.toolSettings_pgm_i = ""- , GHC.Settings.toolSettings_pgm_install_name_tool = ""- , GHC.Settings.toolSettings_pgm_l = ("", [])- , GHC.Settings.toolSettings_pgm_L = ""- , GHC.Settings.toolSettings_pgm_lc = ("", [])- , GHC.Settings.toolSettings_pgm_lcc = ("", [])- , GHC.Settings.toolSettings_pgm_libtool = ""- , GHC.Settings.toolSettings_pgm_lm = ("", [])- , GHC.Settings.toolSettings_pgm_lo = ("", [])- , GHC.Settings.toolSettings_pgm_otool = ""- , GHC.Settings.toolSettings_pgm_P = ("", [])- , GHC.Settings.toolSettings_pgm_ranlib = ""- , GHC.Settings.toolSettings_pgm_T = ""- , GHC.Settings.toolSettings_pgm_windres = ""- }
− source/library/Language/Haskell/Brittany/Internal/Prelude.hs
@@ -1,194 +0,0 @@-module Language.Haskell.Brittany.Internal.Prelude- ( module E- ) where--import Control.Applicative as E (Alternative(..), Applicative(..))-import Control.Arrow as E ((&&&), (***), (<<<), (>>>), first, second)-import Control.Concurrent as E (forkIO, forkOS, threadDelay)-import Control.Concurrent.Chan as E (Chan)-import Control.Concurrent.MVar as E- (MVar, newEmptyMVar, newMVar, putMVar, readMVar, swapMVar, takeMVar)-import Control.Exception as E (assert, bracket, evaluate)-import Control.Monad as E- ( (<$!>)- , (<=<)- , (=<<)- , (>=>)- , Functor(..)- , Monad(..)- , MonadPlus(..)- , filterM- , forM- , forM_- , forever- , guard- , join- , liftM- , liftM2- , liftM3- , liftM4- , liftM5- , mapM- , mapM_- , replicateM- , replicateM_- , sequence- , sequence_- , unless- , void- , when- )-import Control.Monad.Extra as E- (allM, andM, anyM, ifM, notM, orM, unlessM, whenM)-import Control.Monad.IO.Class as E (MonadIO(..))-import Control.Monad.ST as E (ST)-import Control.Monad.Trans.Class as E (lift)-import Control.Monad.Trans.Maybe as E (MaybeT(..))-import Control.Monad.Trans.MultiRWS as E- (MonadMultiReader(..), MonadMultiState(..), MonadMultiWriter(..), mGet)-import Data.Bifunctor as E (bimap)-import Data.Bool as E (Bool(..))-import Data.Char as E (Char, chr, ord)-import Data.Data as E (toConstr)-import Data.Either as E (Either(..), either)-import Data.Foldable as E (asum, fold, foldl', foldr')-import Data.Function as E ((&), fix)-import Data.Functor as E (($>))-import Data.Functor.Identity as E (Identity(..))-import Data.IORef as E (IORef)-import Data.Int as E (Int)-import Data.List as E- ( all- , break- , drop- , dropWhile- , elem- , filter- , find- , intercalate- , intersperse- , isPrefixOf- , isSuffixOf- , iterate- , length- , mapAccumL- , mapAccumR- , maximum- , minimum- , notElem- , nub- , null- , partition- , repeat- , replicate- , sortBy- , sum- , take- , takeWhile- , transpose- , uncons- , unzip- , zip- , zip3- , zipWith- )-import Data.List.Extra as E (nubOrd, stripSuffix)-import Data.List.NonEmpty as E (NonEmpty(..), nonEmpty)-import Data.Map as E (Map)-import Data.Maybe as E- (Maybe(..), catMaybes, fromMaybe, listToMaybe, maybe, maybeToList)-import Data.Monoid as E- ( All(..)- , Alt(..)- , Any(..)- , Endo(..)- , Monoid(..)- , Product(..)- , Sum(..)- , mconcat- )-import Data.Ord as E (Down(..), Ordering(..), comparing)-import Data.Proxy as E (Proxy(..))-import Data.Ratio as E ((%), Ratio, Rational, denominator, numerator)-import Data.Semigroup as E ((<>), Semigroup(..))-import Data.Sequence as E (Seq)-import Data.Set as E (Set)-import Data.String as E (String)-import Data.Text as E (Text)-import Data.Tree as E (Tree(..))-import Data.Tuple as E (swap)-import Data.Typeable as E (Typeable)-import Data.Version as E (showVersion)-import Data.Void as E (Void)-import Data.Word as E (Word, Word32)-import Debug.Trace as E- ( trace- , traceIO- , traceId- , traceM- , traceShow- , traceShowId- , traceShowM- , traceStack- )-import Foreign.ForeignPtr as E (ForeignPtr)-import Foreign.Storable as E (Storable)-import GHC.Exts as E (Constraint)-import GHC.Hs.Extension as E (GhcPs)-import GHC.Types.Name.Reader as E (RdrName)-import Prelude as E- ( ($)- , ($!)- , (&&)- , (++)- , (.)- , (<$>)- , Bounded(..)- , Double- , Enum(..)- , Eq(..)- , Float- , Floating(..)- , Foldable- , Fractional(..)- , Integer- , Integral(..)- , Num(..)- , Ord(..)- , RealFloat(..)- , RealFrac(..)- , Show(..)- , Traversable- , (^)- , and- , any- , const- , curry- , error- , flip- , foldl- , foldr- , foldr1- , fromIntegral- , fst- , head- , id- , map- , not- , or- , otherwise- , print- , putStr- , putStrLn- , realToFrac- , reverse- , seq- , snd- , subtract- , traverse- , uncurry- , undefined- , (||)- )-import System.IO as E (IO, hFlush, stdout)-import Text.Read as E (readMaybe)
− source/library/Language/Haskell/Brittany/Internal/PreludeUtils.hs
@@ -1,66 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Language.Haskell.Brittany.Internal.PreludeUtils where--import Control.Applicative-import Control.DeepSeq (NFData, force)-import Control.Exception.Base (evaluate)-import Control.Monad-import qualified Data.Strict.Maybe as Strict-import Debug.Trace-import Prelude-import System.IO----instance Applicative Strict.Maybe where- pure = Strict.Just- Strict.Just f <*> Strict.Just x = Strict.Just (f x)- _ <*> _ = Strict.Nothing--instance Monad Strict.Maybe where- Strict.Nothing >>= _ = Strict.Nothing- Strict.Just x >>= f = f x--instance Alternative Strict.Maybe where- empty = Strict.Nothing- x <|> Strict.Nothing = x- _ <|> x = x--traceFunctionWith :: String -> (a -> String) -> (b -> String) -> (a -> b) -> (a -> b)-traceFunctionWith name s1 s2 f x = trace traceStr y- where- y = f x- traceStr =- name ++ "\nBEFORE:\n" ++ s1 x ++ "\nAFTER:\n" ++ s2 y--(<&!>) :: Monad m => m a -> (a -> b) -> m b-(<&!>) = flip (<$!>)--putStrErrLn :: String -> IO ()-putStrErrLn s = hPutStrLn stderr s--putStrErr :: String -> IO ()-putStrErr s = hPutStr stderr s--printErr :: Show a => a -> IO ()-printErr = putStrErrLn . show--errorIf :: Bool -> a -> a-errorIf False = id-errorIf True = error "errorIf"--errorIfNote :: Maybe String -> a -> a-errorIfNote Nothing = id-errorIfNote (Just x) = error x--(<&>) :: Functor f => f a -> (a -> b) -> f b-(<&>) = flip fmap-infixl 4 <&>--(.>) :: (a -> b) -> (b -> c) -> (a -> c)-f .> g = g . f-infixl 9 .>--evaluateDeep :: NFData a => a -> IO a-evaluateDeep = evaluate . force
− source/library/Language/Haskell/Brittany/Internal/Transformations/Alt.hs
@@ -1,907 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MonadComprehensions #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}--module Language.Haskell.Brittany.Internal.Transformations.Alt where--import qualified Control.Monad.Memo as Memo-import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS-import Data.HList.ContainsType-import qualified Data.List.Extra-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils----data AltCurPos = AltCurPos- { _acp_line :: Int -- chars in the current line- , _acp_indent :: Int -- current indentation level- , _acp_indentPrep :: Int -- indentChange affecting the next Par- , _acp_forceMLFlag :: AltLineModeState- }- deriving (Show)--data AltLineModeState- = AltLineModeStateNone- | AltLineModeStateForceML Bool -- true ~ decays on next wrap- | AltLineModeStateForceSL- | AltLineModeStateContradiction- -- i.e. ForceX False -> ForceX True -> None- deriving (Show)--altLineModeRefresh :: AltLineModeState -> AltLineModeState-altLineModeRefresh AltLineModeStateNone = AltLineModeStateNone-altLineModeRefresh AltLineModeStateForceML{} = AltLineModeStateForceML False-altLineModeRefresh AltLineModeStateForceSL = AltLineModeStateForceSL-altLineModeRefresh AltLineModeStateContradiction = AltLineModeStateContradiction--altLineModeDecay :: AltLineModeState -> AltLineModeState-altLineModeDecay AltLineModeStateNone = AltLineModeStateNone-altLineModeDecay (AltLineModeStateForceML False) = AltLineModeStateForceML True-altLineModeDecay (AltLineModeStateForceML True ) = AltLineModeStateNone-altLineModeDecay AltLineModeStateForceSL = AltLineModeStateForceSL-altLineModeDecay AltLineModeStateContradiction = AltLineModeStateContradiction--mergeLineMode :: AltCurPos -> AltLineModeState -> AltCurPos-mergeLineMode acp s = case (_acp_forceMLFlag acp, s) of- (AltLineModeStateContradiction, _) -> acp- (AltLineModeStateNone, x) -> acp { _acp_forceMLFlag = x }- (AltLineModeStateForceSL, AltLineModeStateForceSL) -> acp- (AltLineModeStateForceML{}, AltLineModeStateForceML{}) ->- acp { _acp_forceMLFlag = s }- _ -> acp { _acp_forceMLFlag = AltLineModeStateContradiction }----- removes any BDAlt's from the BriDoc-transformAlts- :: forall r w s- . ( Data.HList.ContainsType.ContainsType Config r- , Data.HList.ContainsType.ContainsType (Seq String) w- )- => BriDocNumbered- -> MultiRWSS.MultiRWS r w s BriDoc-transformAlts =- MultiRWSS.withMultiStateA (AltCurPos 0 0 0 AltLineModeStateNone)- . Memo.startEvalMemoT- . fmap unwrapBriDocNumbered- . rec- where- -- this function is exponential by nature and cannot be improved in any- -- way i can think of, and i've tried. (stupid StableNames.)- -- transWrap :: BriDoc -> BriDocNumbered- -- transWrap brDc = flip StateS.evalState (1::Int)- -- $ Memo.startEvalMemoT- -- $ go brDc- -- where- -- incGet = StateS.get >>= \i -> StateS.put (i+1) $> i- -- go :: BriDoc -> Memo.MemoT BriDoc BriDocNumbered (StateS.State Int) BriDocNumbered- -- go = Memo.memo $ \bdX -> do- -- i <- lift $ incGet- -- fmap (\bd' -> (i,bd')) $ case bdX of- -- BDEmpty -> return $ BDFEmpty- -- BDLit t -> return $ BDFLit t- -- BDSeq list -> BDFSeq <$> go `mapM` list- -- BDCols sig list -> BDFCols sig <$> go `mapM` list- -- BDSeparator -> return $ BDFSeparator- -- BDAddBaseY ind bd -> BDFAddBaseY ind <$> go bd- -- BDSetBaseY bd -> BDFSetBaseY <$> go bd- -- BDSetIndentLevel bd -> BDFSetIndentLevel <$> go bd- -- BDPar ind line indented -> [ BDFPar ind line' indented'- -- | line' <- go line- -- , indented' <- go indented- -- ]- -- BDAlt alts -> BDFAlt <$> go `mapM` alts -- not that this will happen- -- BDForceMultiline bd -> BDFForceMultiline <$> go bd- -- BDForceSingleline bd -> BDFForceSingleline <$> go bd- -- BDForwardLineMode bd -> BDFForwardLineMode <$> go bd- -- BDExternal k ks c t -> return $ BDFExternal k ks c t- -- BDAnnotationPrior annKey bd -> BDFAnnotationPrior annKey <$> go bd- -- BDAnnotationPost annKey bd -> BDFAnnotationRest annKey <$> go bd- -- BDLines lines -> BDFLines <$> go `mapM` lines- -- BDEnsureIndent ind bd -> BDFEnsureIndent ind <$> go bd- -- BDProhibitMTEL bd -> BDFProhibitMTEL <$> go bd---- rec :: BriDocNumbered -> Memo.MemoT Int [VerticalSpacing] (MultiRWSS.MultiRWS r w (AltCurPos ': s)) BriDocNumbered- rec bdX@(brDcId, brDc) = do- let reWrap = (,) brDcId- -- debugAcp :: AltCurPos <- mGet- case brDc of- -- BDWrapAnnKey annKey bd -> do- -- acp <- mGet- -- mSet $ acp { _acp_forceMLFlag = altLineModeDecay $ _acp_forceMLFlag acp }- -- BDWrapAnnKey annKey <$> rec bd- BDFEmpty{} -> processSpacingSimple bdX $> bdX- BDFLit{} -> processSpacingSimple bdX $> bdX- BDFSeq list ->- reWrap . BDFSeq <$> list `forM` rec- BDFCols sig list ->- reWrap . BDFCols sig <$> list `forM` rec- BDFSeparator -> processSpacingSimple bdX $> bdX- BDFAddBaseY indent bd -> do- acp <- mGet- indAdd <- fixIndentationForMultiple acp indent- mSet $ acp { _acp_indentPrep = max (_acp_indentPrep acp) indAdd }- r <- rec bd- acp' <- mGet- mSet $ acp' { _acp_indent = _acp_indent acp }- return $ case indent of- BrIndentNone -> r- BrIndentRegular -> reWrap $ BDFAddBaseY (BrIndentSpecial indAdd) r- BrIndentSpecial i -> reWrap $ BDFAddBaseY (BrIndentSpecial i) r- BDFBaseYPushCur bd -> do- acp <- mGet- mSet $ acp { _acp_indent = _acp_line acp }- r <- rec bd- return $ reWrap $ BDFBaseYPushCur r- BDFBaseYPop bd -> do- acp <- mGet- r <- rec bd- acp' <- mGet- mSet $ acp' { _acp_indent = _acp_indentPrep acp }- return $ reWrap $ BDFBaseYPop r- BDFIndentLevelPushCur bd -> do- reWrap . BDFIndentLevelPushCur <$> rec bd- BDFIndentLevelPop bd -> do- reWrap . BDFIndentLevelPop <$> rec bd- BDFPar indent sameLine indented -> do- indAmount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack- let indAdd = case indent of- BrIndentNone -> 0- BrIndentRegular -> indAmount- BrIndentSpecial i -> i- acp <- mGet- let ind = _acp_indent acp + _acp_indentPrep acp + indAdd- mSet $ acp- { _acp_indent = ind- , _acp_indentPrep = 0- }- sameLine' <- rec sameLine- mModify $ \acp' -> acp'- { _acp_line = ind- , _acp_indent = ind- }- indented' <- rec indented- return $ reWrap $ BDFPar indent sameLine' indented'- BDFAlt [] -> error "empty BDAlt" -- returning BDEmpty instead is a- -- possibility, but i will prefer a- -- fail-early approach; BDEmpty does not- -- make sense semantically for Alt[].- BDFAlt alts -> do- altChooser <- mAsk <&> _conf_layout .> _lconfig_altChooser .> confUnpack- case altChooser of- AltChooserSimpleQuick -> do- rec $ head alts- AltChooserShallowBest -> do- spacings <- alts `forM` getSpacing- acp <- mGet- let lineCheck LineModeInvalid = False- lineCheck (LineModeValid (VerticalSpacing _ p _)) =- case _acp_forceMLFlag acp of- AltLineModeStateNone -> True- AltLineModeStateForceSL{} -> p == VerticalSpacingParNone- AltLineModeStateForceML{} -> p /= VerticalSpacingParNone- AltLineModeStateContradiction -> False- -- TODO: use COMPLETE pragma instead?- lineCheck _ = error "ghc exhaustive check is insufficient"- lconf <- _conf_layout <$> mAsk- let options = -- trace ("considering options:" ++ show (length alts, acp)) $- (zip spacings alts- <&> \(vs, bd) -> -- trace ("spacing=" ++ show vs ++ ",hasSpace=" ++ show (hasSpace lconf acp vs) ++ ",lineCheck=" ++ show (lineCheck vs))- ( hasSpace1 lconf acp vs && lineCheck vs, bd))- rec- $ fromMaybe (-- trace ("choosing last") $- List.last alts)- $ Data.List.Extra.firstJust (\(_i::Int, (b,x)) ->- [ -- traceShow ("choosing option " ++ show i) $- x- | b- ])- $ zip [1..] options- AltChooserBoundedSearch limit -> do- spacings <- alts `forM` getSpacings limit- acp <- mGet- let lineCheck (VerticalSpacing _ p _) =- case _acp_forceMLFlag acp of- AltLineModeStateNone -> True- AltLineModeStateForceSL{} -> p == VerticalSpacingParNone- AltLineModeStateForceML{} -> p /= VerticalSpacingParNone- AltLineModeStateContradiction -> False- lconf <- _conf_layout <$> mAsk- let options = -- trace ("considering options:" ++ show (length alts, acp)) $- (zip spacings alts- <&> \(vs, bd) -> -- trace ("spacing=" ++ show vs ++ ",hasSpace=" ++ show (hasSpace lconf acp vs) ++ ",lineCheck=" ++ show (lineCheck vs))- ( any (hasSpace2 lconf acp) vs- && any lineCheck vs, bd))- let checkedOptions :: [Maybe (Int, BriDocNumbered)] =- zip [1..] options <&> (\(i, (b,x)) -> [ (i, x) | b ])- rec- $ fromMaybe (-- trace ("choosing last") $- List.last alts)- $ Data.List.Extra.firstJust (fmap snd) checkedOptions- BDFForceMultiline bd -> do- acp <- mGet- x <- do- mSet $ mergeLineMode acp (AltLineModeStateForceML False)- rec bd- acp' <- mGet- mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }- return $ x- BDFForceSingleline bd -> do- acp <- mGet- x <- do- mSet $ mergeLineMode acp AltLineModeStateForceSL- rec bd- acp' <- mGet- mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }- return $ x- BDFForwardLineMode bd -> do- acp <- mGet- x <- do- mSet $ acp { _acp_forceMLFlag = altLineModeRefresh $ _acp_forceMLFlag acp }- rec bd- acp' <- mGet- mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }- return $ x- BDFExternal{} -> processSpacingSimple bdX $> bdX- BDFPlain{} -> processSpacingSimple bdX $> bdX- BDFAnnotationPrior annKey bd -> do- acp <- mGet- mSet $ acp { _acp_forceMLFlag = altLineModeDecay $ _acp_forceMLFlag acp }- bd' <- rec bd- return $ reWrap $ BDFAnnotationPrior annKey bd'- BDFAnnotationRest annKey bd ->- reWrap . BDFAnnotationRest annKey <$> rec bd- BDFAnnotationKW annKey kw bd ->- reWrap . BDFAnnotationKW annKey kw <$> rec bd- BDFMoveToKWDP annKey kw b bd ->- reWrap . BDFMoveToKWDP annKey kw b <$> rec bd- BDFLines [] -> return $ reWrap BDFEmpty -- evil transformation. or harmless.- BDFLines (l:lr) -> do- ind <- _acp_indent <$> mGet- l' <- rec l- lr' <- lr `forM` \x -> do- mModify $ \acp -> acp- { _acp_line = ind- , _acp_indent = ind- }- rec x- return $ reWrap $ BDFLines (l':lr')- BDFEnsureIndent indent bd -> do- acp <- mGet- indAdd <- fixIndentationForMultiple acp indent- mSet $ acp- { _acp_indentPrep = 0- -- TODO: i am not sure this is valid, in general.- , _acp_indent = _acp_indent acp + indAdd- , _acp_line = max (_acp_line acp) (_acp_indent acp + indAdd)- -- we cannot use just _acp_line acp + indAdd because of the case- -- where there are multiple BDFEnsureIndents in the same line.- -- Then, the actual indentation is relative to the current- -- indentation, not the current cursor position.- }- r <- rec bd- acp' <- mGet- mSet $ acp' { _acp_indent = _acp_indent acp }- return $ case indent of- BrIndentNone -> r- BrIndentRegular -> reWrap $ BDFEnsureIndent (BrIndentSpecial indAdd) r- BrIndentSpecial i -> reWrap $ BDFEnsureIndent (BrIndentSpecial i) r- BDFNonBottomSpacing _ bd -> rec bd- BDFSetParSpacing bd -> rec bd- BDFForceParSpacing bd -> rec bd- BDFDebug s bd -> do- acp :: AltCurPos <- mGet- tellDebugMess $ "transformAlts: BDFDEBUG " ++ s ++ " (node-id=" ++ show brDcId ++ "): acp=" ++ show acp- reWrap . BDFDebug s <$> rec bd- processSpacingSimple- :: ( MonadMultiReader Config m- , MonadMultiState AltCurPos m- , MonadMultiWriter (Seq String) m- )- => BriDocNumbered- -> m ()- processSpacingSimple bd = getSpacing bd >>= \case- LineModeInvalid -> error "processSpacingSimple inv"- LineModeValid (VerticalSpacing i VerticalSpacingParNone _) -> do- acp <- mGet- mSet $ acp { _acp_line = _acp_line acp + i }- LineModeValid VerticalSpacing{} -> error "processSpacingSimple par"- _ -> error "ghc exhaustive check is insufficient"- hasSpace1 :: LayoutConfig -> AltCurPos -> LineModeValidity VerticalSpacing -> Bool- hasSpace1 _ _ LineModeInvalid = False- hasSpace1 lconf acp (LineModeValid vs) = hasSpace2 lconf acp vs- hasSpace1 _ _ _ = error "ghc exhaustive check is insufficient"- hasSpace2 :: LayoutConfig -> AltCurPos -> VerticalSpacing -> Bool- hasSpace2 lconf (AltCurPos line _indent _ _) (VerticalSpacing sameLine VerticalSpacingParNone _)- = line + sameLine <= confUnpack (_lconfig_cols lconf)- hasSpace2 lconf (AltCurPos line indent indentPrep _) (VerticalSpacing sameLine (VerticalSpacingParSome par) _)- = line + sameLine <= confUnpack (_lconfig_cols lconf)- && indent + indentPrep + par <= confUnpack (_lconfig_cols lconf)- hasSpace2 lconf (AltCurPos line _indent _ _) (VerticalSpacing sameLine VerticalSpacingParAlways{} _)- = line + sameLine <= confUnpack (_lconfig_cols lconf)--getSpacing- :: forall m- . (MonadMultiReader Config m, MonadMultiWriter (Seq String) m)- => BriDocNumbered- -> m (LineModeValidity VerticalSpacing)-getSpacing !bridoc = rec bridoc- where- rec :: BriDocNumbered -> m (LineModeValidity VerticalSpacing)- rec (brDcId, brDc) = do- config <- mAsk- let colMax = config & _conf_layout & _lconfig_cols & confUnpack- result <- case brDc of- -- BDWrapAnnKey _annKey bd -> rec bd- BDFEmpty ->- return $ LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False- BDFLit t ->- return $ LineModeValid $ VerticalSpacing (Text.length t) VerticalSpacingParNone False- BDFSeq list ->- sumVs <$> rec `mapM` list- BDFCols _sig list -> sumVs <$> rec `mapM` list- BDFSeparator ->- return $ LineModeValid $ VerticalSpacing 1 VerticalSpacingParNone False- BDFAddBaseY indent bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs- { _vs_paragraph = case _vs_paragraph vs of- VerticalSpacingParNone -> VerticalSpacingParNone- VerticalSpacingParAlways i -> VerticalSpacingParAlways $ case indent of- BrIndentNone -> i- BrIndentRegular -> i + ( confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- )- BrIndentSpecial j -> i + j- VerticalSpacingParSome i -> VerticalSpacingParSome $ case indent of- BrIndentNone -> i- BrIndentRegular -> i + ( confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- )- BrIndentSpecial j -> i + j- }- BDFBaseYPushCur bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs- -- We leave par as-is, even though it technically is not- -- accurate (in general).- -- the reason is that we really want to _keep_ it Just if it is- -- just so we properly communicate the is-multiline fact.- -- An alternative would be setting to (Just 0).- { _vs_sameLine = max (_vs_sameLine vs)- (case _vs_paragraph vs of- VerticalSpacingParNone -> 0- VerticalSpacingParSome i -> i- VerticalSpacingParAlways i -> min colMax i)- , _vs_paragraph = VerticalSpacingParSome 0- }- BDFBaseYPop bd -> rec bd- BDFIndentLevelPushCur bd -> rec bd- BDFIndentLevelPop bd -> rec bd- BDFPar BrIndentNone sameLine indented -> do- mVs <- rec sameLine- mIndSp <- rec indented- return- $ [ VerticalSpacing lsp pspResult parFlagResult- | VerticalSpacing lsp mPsp _ <- mVs- , indSp <- mIndSp- , lineMax <- getMaxVS $ mIndSp- , let pspResult = case mPsp of- VerticalSpacingParSome psp -> VerticalSpacingParSome $ max psp lineMax- VerticalSpacingParNone -> VerticalSpacingParSome $ lineMax- VerticalSpacingParAlways psp -> VerticalSpacingParAlways $ max psp lineMax- , let parFlagResult = mPsp == VerticalSpacingParNone- && _vs_paragraph indSp == VerticalSpacingParNone- && _vs_parFlag indSp- ]- BDFPar{} -> error "BDPar with indent in getSpacing"- BDFAlt [] -> error "empty BDAlt"- BDFAlt (alt:_) -> rec alt- BDFForceMultiline bd -> do- mVs <- rec bd- return $ mVs >>= _vs_paragraph .> \case- VerticalSpacingParNone -> LineModeInvalid- _ -> mVs- BDFForceSingleline bd -> do- mVs <- rec bd- return $ mVs >>= _vs_paragraph .> \case- VerticalSpacingParNone -> mVs- _ -> LineModeInvalid- BDFForwardLineMode bd -> rec bd- BDFExternal _ _ _ txt -> return $ LineModeValid $ case Text.lines txt of- [t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False- _ -> VerticalSpacing 999 VerticalSpacingParNone False- BDFPlain txt -> return $ LineModeValid $ case Text.lines txt of- [t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False- _ -> VerticalSpacing 999 VerticalSpacingParNone False- BDFAnnotationPrior _annKey bd -> rec bd- BDFAnnotationKW _annKey _kw bd -> rec bd- BDFAnnotationRest _annKey bd -> rec bd- BDFMoveToKWDP _annKey _kw _b bd -> rec bd- BDFLines [] -> return- $ LineModeValid- $ VerticalSpacing 0 VerticalSpacingParNone False- BDFLines ls@(_:_) -> do- lSps <- rec `mapM` ls- let (mVs:_) = lSps -- separated into let to avoid MonadFail- return $ [ VerticalSpacing lsp (VerticalSpacingParSome $ lineMax) False- | VerticalSpacing lsp _ _ <- mVs- , lineMax <- getMaxVS $ maxVs $ lSps- ]- BDFEnsureIndent indent bd -> do- mVs <- rec bd- let addInd = case indent of- BrIndentNone -> 0- BrIndentRegular -> confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- BrIndentSpecial i -> i- return $ mVs <&> \(VerticalSpacing lsp psp pf) ->- VerticalSpacing (lsp + addInd) psp pf- BDFNonBottomSpacing b bd -> do- mVs <- rec bd- return- $ mVs- <|> LineModeValid- (VerticalSpacing- 0- (if b then VerticalSpacingParSome 0- else VerticalSpacingParAlways colMax- )- False- )- BDFSetParSpacing bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs { _vs_parFlag = True }- BDFForceParSpacing bd -> do- mVs <- rec bd- return $ [ vs | vs <- mVs, _vs_parFlag vs || _vs_paragraph vs == VerticalSpacingParNone ]- BDFDebug s bd -> do- r <- rec bd- tellDebugMess $ "getSpacing: BDFDebug " ++ show s ++ " (node-id=" ++ show brDcId ++ "): mVs=" ++ show r- return r- return result- maxVs :: [LineModeValidity VerticalSpacing] -> LineModeValidity VerticalSpacing- maxVs = foldl'- (liftM2 (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) ->- VerticalSpacing (max x1 y1) (case (x2, y2) of- (x, VerticalSpacingParNone) -> x- (VerticalSpacingParNone, x) -> x- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParSome j, VerticalSpacingParAlways i) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParSome x, VerticalSpacingParSome y) ->- VerticalSpacingParSome $ max x y) False))- (LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False)- sumVs :: [LineModeValidity VerticalSpacing] -> LineModeValidity VerticalSpacing- sumVs sps = foldl' (liftM2 go) initial sps- where- go (VerticalSpacing x1 x2 x3) (VerticalSpacing y1 y2 _) = VerticalSpacing- (x1 + y1)- (case (x2, y2) of- (x, VerticalSpacingParNone) -> x- (VerticalSpacingParNone, x) -> x- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParSome x, VerticalSpacingParSome y) ->- VerticalSpacingParSome $ x + y)- x3- singleline (LineModeValid x) = _vs_paragraph x == VerticalSpacingParNone- singleline _ = False- isPar (LineModeValid x) = _vs_parFlag x- isPar _ = False- parFlag = case sps of- [] -> True- _ -> all singleline (List.init sps) && isPar (List.last sps)- initial = LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone parFlag- getMaxVS :: LineModeValidity VerticalSpacing -> LineModeValidity Int- getMaxVS = fmap $ \(VerticalSpacing x1 x2 _) -> x1 `max` case x2 of- VerticalSpacingParSome i -> i- VerticalSpacingParNone -> 0- VerticalSpacingParAlways i -> i--data SpecialCompare = Unequal | Smaller | Bigger--getSpacings- :: forall m- . (MonadMultiReader Config m, MonadMultiWriter (Seq String) m)- => Int- -> BriDocNumbered- -> Memo.MemoT Int [VerticalSpacing] m [VerticalSpacing]-getSpacings limit bridoc = preFilterLimit <$> rec bridoc- where- -- when we do `take K . filter someCondition` on a list of spacings, we- -- need to first (also) limit the size of the input list, otherwise a- -- _large_ input with a similarly _large_ prefix not passing our filtering- -- process could lead to exponential runtime behaviour.- -- TODO: 3 is arbitrary.- preFilterLimit :: [VerticalSpacing] -> [VerticalSpacing]- preFilterLimit = take (3*limit)- memoWithKey :: Memo.MonadMemo k v m1 => k -> m1 v -> m1 v- memoWithKey k v = Memo.memo (const v) k- rec :: BriDocNumbered -> Memo.MemoT Int [VerticalSpacing] m [VerticalSpacing]- rec (brDcId, brdc) = memoWithKey brDcId $ do- config <- mAsk- let colMax = config & _conf_layout & _lconfig_cols & confUnpack- let hasOkColCount (VerticalSpacing lsp psp _) =- lsp <= colMax && case psp of- VerticalSpacingParNone -> True- VerticalSpacingParSome i -> i <= colMax- VerticalSpacingParAlways{} -> True- let specialCompare vs1 vs2 =- if ( (_vs_sameLine vs1 == _vs_sameLine vs2)- && (_vs_parFlag vs1 == _vs_parFlag vs2)- )- then case (_vs_paragraph vs1, _vs_paragraph vs2) of- (VerticalSpacingParAlways i1, VerticalSpacingParAlways i2) ->- if i1 < i2 then Smaller else Bigger- (p1, p2) -> if p1 == p2 then Smaller else Unequal- else Unequal- let allowHangingQuasiQuotes =- config- & _conf_layout- & _lconfig_allowHangingQuasiQuotes- & confUnpack- let -- this is like List.nub, with one difference: if two elements- -- are unequal only in _vs_paragraph, with both ParAlways, we- -- treat them like equals and replace the first occurence with the- -- smallest member of this "equal group".- specialNub :: [VerticalSpacing] -> [VerticalSpacing]- specialNub [] = []- specialNub (x1 : xr) = case go x1 xr of- (r, xs') -> r : specialNub xs'- where- go y1 [] = (y1, [])- go y1 (y2 : yr) = case specialCompare y1 y2 of- Unequal -> let (r, yr') = go y1 yr in (r, y2 : yr')- Smaller -> go y1 yr- Bigger -> go y2 yr- let -- the standard function used to enforce a constant upper bound- -- on the number of elements returned for each node. Should be- -- applied whenever in a parent the combination of spacings from- -- its children might cause excess of the upper bound.- filterAndLimit :: [VerticalSpacing] -> [VerticalSpacing]- filterAndLimit = take limit- -- prune so we always consider a constant- -- amount of spacings per node of the BriDoc.- . specialNub- -- In the end we want to know if there is at least- -- one valid spacing for any alternative.- -- If there are duplicates in the list, then these- -- will either all be valid (so having more than the- -- first is pointless) or all invalid (in which- -- case having any of them is pointless).- -- Nonetheless I think the order of spacings should- -- be preserved as it provides a deterministic- -- choice for which spacings to prune (which is- -- an argument against simply using a Set).- -- I have also considered `fmap head . group` which- -- seems to work similarly well for common cases- -- and which might behave even better when it comes- -- to determinism of the algorithm. But determinism- -- should not be overrated here either - in the end- -- this is about deterministic behaviour of the- -- pruning we do that potentially results in- -- non-optimal layouts, and we'd rather take optimal- -- layouts when we can than take non-optimal layouts- -- just to be consistent with other cases where- -- we'd choose non-optimal layouts.- . filter hasOkColCount- -- throw out any spacings (i.e. children) that- -- already use more columns than available in- -- total.- . preFilterLimit- result <- case brdc of- -- BDWrapAnnKey _annKey bd -> rec bd- BDFEmpty ->- return $ [VerticalSpacing 0 VerticalSpacingParNone False]- BDFLit t ->- return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]- BDFSeq list ->- fmap sumVs . mapM filterAndLimit <$> rec `mapM` list- BDFCols _sig list ->- fmap sumVs . mapM filterAndLimit <$> rec `mapM` list- BDFSeparator ->- return $ [VerticalSpacing 1 VerticalSpacingParNone False]- BDFAddBaseY indent bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs- { _vs_paragraph = case _vs_paragraph vs of- VerticalSpacingParNone -> VerticalSpacingParNone- VerticalSpacingParAlways i -> VerticalSpacingParAlways $ case indent of- BrIndentNone -> i- BrIndentRegular -> i + ( confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- )- BrIndentSpecial j -> i + j- VerticalSpacingParSome i -> VerticalSpacingParSome $ case indent of- BrIndentNone -> i- BrIndentRegular -> i + ( confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- )- BrIndentSpecial j -> i + j- }- BDFBaseYPushCur bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs- -- We leave par as-is, even though it technically is not- -- accurate (in general).- -- the reason is that we really want to _keep_ it Just if it is- -- just so we properly communicate the is-multiline fact.- -- An alternative would be setting to (Just 0).- { _vs_sameLine = max (_vs_sameLine vs)- (case _vs_paragraph vs of- VerticalSpacingParNone -> 0- VerticalSpacingParSome i -> i- VerticalSpacingParAlways i -> min colMax i)- , _vs_paragraph = case _vs_paragraph vs of- VerticalSpacingParNone -> VerticalSpacingParNone- VerticalSpacingParSome i -> VerticalSpacingParSome i- VerticalSpacingParAlways i -> VerticalSpacingParAlways i- }- BDFBaseYPop bd -> rec bd- BDFIndentLevelPushCur bd -> rec bd- BDFIndentLevelPop bd -> rec bd- BDFPar BrIndentNone sameLine indented -> do- mVss <- filterAndLimit <$> rec sameLine- indSps <- filterAndLimit <$> rec indented- let mVsIndSp = take limit- $ [ (x,y)- | x<-mVss- , y<-indSps- ]- return $ mVsIndSp <&>- \(VerticalSpacing lsp mPsp _, indSp) ->- VerticalSpacing- lsp- (case mPsp of- VerticalSpacingParSome psp ->- VerticalSpacingParSome $ max psp $ getMaxVS indSp -- TODO- VerticalSpacingParNone -> spMakePar indSp- VerticalSpacingParAlways psp ->- VerticalSpacingParAlways $ max psp $ getMaxVS indSp)- ( mPsp == VerticalSpacingParNone- && _vs_paragraph indSp == VerticalSpacingParNone- && _vs_parFlag indSp- )-- BDFPar{} -> error "BDPar with indent in getSpacing"- BDFAlt [] -> error "empty BDAlt"- -- BDAlt (alt:_) -> rec alt- BDFAlt alts -> do- r <- rec `mapM` alts- return $ filterAndLimit =<< r- BDFForceMultiline bd -> do- mVs <- filterAndLimit <$> rec bd- return $ filter ((/=VerticalSpacingParNone) . _vs_paragraph) mVs- BDFForceSingleline bd -> do- mVs <- filterAndLimit <$> rec bd- return $ filter ((==VerticalSpacingParNone) . _vs_paragraph) mVs- BDFForwardLineMode bd -> rec bd- BDFExternal _ _ _ txt | [t] <- Text.lines txt ->- return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]- BDFExternal{} ->- return $ [] -- yes, we just assume that we cannot properly layout- -- this.- BDFPlain t -> return- [ case Text.lines t of- [] -> VerticalSpacing 0 VerticalSpacingParNone False- [t1 ] -> VerticalSpacing- (Text.length t1)- VerticalSpacingParNone- False- (t1 : _) -> VerticalSpacing- (Text.length t1)- (VerticalSpacingParAlways 0)- True- | allowHangingQuasiQuotes- ]- BDFAnnotationPrior _annKey bd -> rec bd- BDFAnnotationKW _annKey _kw bd -> rec bd- BDFAnnotationRest _annKey bd -> rec bd- BDFMoveToKWDP _annKey _kw _b bd -> rec bd- BDFLines [] -> return $ [VerticalSpacing 0 VerticalSpacingParNone False]- BDFLines ls@(_:_) -> do- -- we simply assume that lines is only used "properly", i.e. in- -- such a way that the first line can be treated "as a part of the- -- paragraph". That most importantly means that Lines should never- -- be inserted anywhere but at the start of the line. A- -- counterexample would be anything like Seq[Lit "foo", Lines].- lSpss <- map filterAndLimit <$> rec `mapM` ls- let worbled = fmap reverse- $ sequence- $ reverse- $ lSpss- sumF lSps@(lSp1:_) = VerticalSpacing (_vs_sameLine lSp1)- (spMakePar $ maxVs lSps)- False- sumF [] = error $ "should not happen. if my logic does not fail"- ++ "me, this follows from not (null ls)."- return $ sumF <$> worbled- -- lSpss@(mVs:_) <- rec `mapM` ls- -- return $ case Control.Lens.transposeOf traverse lSpss of -- TODO: we currently only- -- -- consider the first alternative for the- -- -- line's spacings.- -- -- also i am not sure if always including- -- -- the first line length in the paragraph- -- -- length gives the desired results.- -- -- it is the safe path though, for now.- -- [] -> []- -- (lSps:_) -> mVs <&> \(VerticalSpacing lsp _) ->- -- VerticalSpacing lsp $ VerticalSpacingParSome $ getMaxVS $ maxVs lSps- BDFEnsureIndent indent bd -> do- mVs <- rec bd- let addInd = case indent of- BrIndentNone -> 0- BrIndentRegular -> confUnpack- $ _lconfig_indentAmount- $ _conf_layout- $ config- BrIndentSpecial i -> i- return $ mVs <&> \(VerticalSpacing lsp psp parFlag) ->- VerticalSpacing (lsp + addInd) psp parFlag- BDFNonBottomSpacing b bd -> do- -- TODO: the `b` flag is an ugly hack, but I was not able to make- -- all tests work without it. It should be possible to have- -- `spMakePar` map VSPAlways{} to VSPSome x1, which fixes this- -- problem but breaks certain other cases.- mVs <- rec bd- return $ if null mVs- then [VerticalSpacing- 0- (if b then VerticalSpacingParSome 0- else VerticalSpacingParAlways colMax- )- False- ]- else mVs <&> \vs -> vs- { _vs_sameLine = min colMax (_vs_sameLine vs)- , _vs_paragraph = case _vs_paragraph vs of- VerticalSpacingParNone -> VerticalSpacingParNone- VerticalSpacingParAlways i- | b -> VerticalSpacingParSome 0- | otherwise -> VerticalSpacingParAlways i- VerticalSpacingParSome i- | b -> VerticalSpacingParSome 0- | otherwise -> VerticalSpacingParAlways i- }- -- the version below is an alternative idea: fold the input- -- spacings into a single spacing. This was hoped to improve in- -- certain cases where non-bottom alternatives took up "too much- -- explored search space"; the downside is that it also cuts- -- the search-space short in other cases where it is not necessary,- -- leading to unnecessary new-lines. Disabled for now. A better- -- solution would require conditionally folding the search-space- -- only in appropriate locations (i.e. a new BriDoc node type- -- for this purpose, perhaps "BDFNonBottomSpacing1").- -- else- -- [ Foldable.foldl1- -- (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) ->- -- VerticalSpacing- -- (min x1 y1)- -- (case (x2, y2) of- -- (x, VerticalSpacingParNone) -> x- -- (VerticalSpacingParNone, x) -> x- -- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->- -- VerticalSpacingParAlways $ min i j- -- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->- -- VerticalSpacingParAlways $ min i j- -- (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->- -- VerticalSpacingParAlways $ min i j- -- (VerticalSpacingParSome x, VerticalSpacingParSome y) ->- -- VerticalSpacingParSome $ min x y)- -- False)- -- mVs- -- ]- BDFSetParSpacing bd -> do- mVs <- rec bd- return $ mVs <&> \vs -> vs { _vs_parFlag = True }- BDFForceParSpacing bd -> do- mVs <- preFilterLimit <$> rec bd- return $ [ vs | vs <- mVs, _vs_parFlag vs || _vs_paragraph vs == VerticalSpacingParNone ]- BDFDebug s bd -> do- r <- rec bd- tellDebugMess $ "getSpacings: BDFDebug " ++ show s ++ " (node-id=" ++ show brDcId ++ "): vs=" ++ show (take 9 r)- return r- return result- maxVs :: [VerticalSpacing] -> VerticalSpacing- maxVs = foldl'- (\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) ->- VerticalSpacing- (max x1 y1)- (case (x2, y2) of- (x, VerticalSpacingParNone) -> x- (VerticalSpacingParNone, x) -> x- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ max i j- (VerticalSpacingParSome x, VerticalSpacingParSome y) ->- VerticalSpacingParSome $ max x y)- False)- (VerticalSpacing 0 VerticalSpacingParNone False)- sumVs :: [VerticalSpacing] -> VerticalSpacing- sumVs sps = foldl' go initial sps- where- go (VerticalSpacing x1 x2 x3) (VerticalSpacing y1 y2 _) = VerticalSpacing- (x1 + y1)- (case (x2, y2) of- (x, VerticalSpacingParNone) -> x- (VerticalSpacingParNone, x) -> x- (VerticalSpacingParAlways i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParAlways i, VerticalSpacingParSome j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParSome i, VerticalSpacingParAlways j) ->- VerticalSpacingParAlways $ i+j- (VerticalSpacingParSome x, VerticalSpacingParSome y) -> VerticalSpacingParSome $ x + y)- x3- singleline x = _vs_paragraph x == VerticalSpacingParNone- isPar x = _vs_parFlag x- parFlag = case sps of- [] -> True- _ -> all singleline (List.init sps) && isPar (List.last sps)- initial = VerticalSpacing 0 VerticalSpacingParNone parFlag- getMaxVS :: VerticalSpacing -> Int- getMaxVS (VerticalSpacing x1 x2 _) = x1 `max` case x2 of- VerticalSpacingParSome i -> i- VerticalSpacingParNone -> 0- VerticalSpacingParAlways i -> i- spMakePar :: VerticalSpacing -> VerticalSpacingPar- spMakePar (VerticalSpacing x1 x2 _) = case x2 of- VerticalSpacingParSome i -> VerticalSpacingParSome $ x1 `max` i- VerticalSpacingParNone -> VerticalSpacingParSome $ x1- VerticalSpacingParAlways i -> VerticalSpacingParAlways $ x1 `max` i--fixIndentationForMultiple- :: (MonadMultiReader (CConfig Identity) m) => AltCurPos -> BrIndent -> m Int-fixIndentationForMultiple acp indent = do- indAmount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack- let indAddRaw = case indent of- BrIndentNone -> 0- BrIndentRegular -> indAmount- BrIndentSpecial i -> i- -- for IndentPolicyMultiple, we restrict the amount of added- -- indentation in such a manner that we end up on a multiple of the- -- base indentation.- indPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack- pure $ if indPolicy == IndentPolicyMultiple- then- let indAddMultiple1 =- indAddRaw - ((_acp_indent acp + indAddRaw) `mod` indAmount)- indAddMultiple2 = if indAddMultiple1 <= 0- then indAddMultiple1 + indAmount- else indAddMultiple1- in indAddMultiple2- else indAddRaw
− source/library/Language/Haskell/Brittany/Internal/Transformations/Columns.hs
@@ -1,133 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Transformations.Columns where--import qualified Data.Generics.Uniplate.Direct as Uniplate-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.Types----transformSimplifyColumns :: BriDoc -> BriDoc-transformSimplifyColumns = Uniplate.rewrite $ \case- -- BDWrapAnnKey annKey bd ->- -- BDWrapAnnKey annKey $ transformSimplify bd- BDEmpty -> Nothing- BDLit{} -> Nothing- BDSeq list | any (\case BDSeq{} -> True- BDEmpty{} -> True- _ -> False) list -> Just $ BDSeq $ list >>= \case- BDEmpty -> []- BDSeq l -> l- x -> [x]- BDSeq (BDCols sig1 cols1@(_:_):rest)- | all (\case BDSeparator -> True; _ -> False) rest ->- Just $ BDCols sig1 (List.init cols1 ++ [BDSeq (List.last cols1:rest)])- BDLines lines | any (\case BDLines{} -> True- BDEmpty{} -> True- _ -> False) lines ->- Just $ BDLines $ filter isNotEmpty $ lines >>= \case- BDLines l -> l- x -> [x]- -- prior floating in- BDAnnotationPrior annKey1 (BDSeq (l:lr)) ->- Just $ BDSeq (BDAnnotationPrior annKey1 l:lr)- BDAnnotationPrior annKey1 (BDLines (l:lr)) ->- Just $ BDLines (BDAnnotationPrior annKey1 l:lr)- BDAnnotationPrior annKey1 (BDCols sig (l:lr)) ->- Just $ BDCols sig (BDAnnotationPrior annKey1 l:lr)- -- post floating in- BDAnnotationRest annKey1 (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDLines list) ->- Just $ BDLines $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAnnotationRest annKey1 $ List.last cols]- BDAnnotationKW annKey1 kw (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAnnotationKW annKey1 kw $ List.last list]- BDAnnotationKW annKey1 kw (BDLines list) ->- Just $ BDLines $ List.init list ++ [BDAnnotationKW annKey1 kw $ List.last list]- BDAnnotationKW annKey1 kw (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAnnotationKW annKey1 kw $ List.last cols]- -- ensureIndent float-in- -- not sure if the following rule is necessary; tests currently are- -- unaffected.- -- BDEnsureIndent indent (BDLines lines) ->- -- Just $ BDLines $ BDEnsureIndent indent <$> lines- -- matching col special transformation- BDCols sig1 cols1@(_:_)- | BDLines lines@(_:_:_) <- List.last cols1- , BDCols sig2 cols2 <- List.last lines- , sig1==sig2 ->- Just $ BDLines- [ BDCols sig1 $ List.init cols1 ++ [BDLines $ List.init lines]- , BDCols sig2 cols2- ]- BDCols sig1 cols1@(_:_)- | BDLines lines@(_:_:_) <- List.last cols1- , BDEnsureIndent _ (BDCols sig2 cols2) <- List.last lines- , sig1==sig2 ->- Just $ BDLines- [ BDCols sig1 $ List.init cols1 ++ [BDLines $ List.init lines]- , BDCols sig2 cols2- ]- BDPar ind col1@(BDCols sig1 _) col2@(BDCols sig2 _) | sig1==sig2 ->- Just $ BDAddBaseY ind (BDLines [col1, col2])- BDPar ind col1@(BDCols sig1 _) (BDLines (col2@(BDCols sig2 _):rest))- | sig1==sig2 ->- Just $ BDPar ind (BDLines [col1, col2]) (BDLines rest)- BDPar ind (BDLines lines1) col2@(BDCols sig2 _)- | BDCols sig1 _ <- List.last lines1- , sig1==sig2 ->- Just $ BDAddBaseY ind (BDLines $ lines1 ++ [col2])- BDPar ind (BDLines lines1) (BDLines (col2@(BDCols sig2 _):rest))- | BDCols sig1 _ <- List.last lines1- , sig1==sig2 ->- Just $ BDPar ind (BDLines $ lines1 ++ [col2]) (BDLines rest)- -- BDPar ind1 (BDCols sig1 cols1) (BDPar ind2 line (BDCols sig2 cols2))- -- | sig1==sig2 ->- -- Just $ BDPar- -- ind1- -- (BDLines [BDCols sig1 cols1, BDCols sig])- BDCols sig1 cols | BDPar _ind line (BDCols sig2 cols2) <- List.last cols- , sig1==sig2 ->- Just $ BDLines- [ BDCols sig1 (List.init cols ++ [line])- , BDCols sig2 cols2- ]- BDCols sig1 cols | BDPar ind line (BDLines lines) <- List.last cols- , BDCols sig2 cols2 <- List.last lines- , sig1==sig2 ->- Just $ BDLines- [ BDCols sig1 $ List.init cols ++ [BDPar ind line (BDLines $ List.init lines)]- , BDCols sig2 cols2- ]- BDLines [x] -> Just $ x- BDLines [] -> Just $ BDEmpty- BDSeq{} -> Nothing- BDCols{} -> Nothing- BDSeparator -> Nothing- BDAddBaseY{} -> Nothing- BDBaseYPushCur{} -> Nothing- BDBaseYPop{} -> Nothing- BDIndentLevelPushCur{} -> Nothing- BDIndentLevelPop{} -> Nothing- BDPar{} -> Nothing- BDAlt{} -> Nothing- BDForceMultiline{} -> Nothing- BDForceSingleline{} -> Nothing- BDForwardLineMode{} -> Nothing- BDExternal{} -> Nothing- BDPlain{} -> Nothing- BDLines{} -> Nothing- BDAnnotationPrior{} -> Nothing- BDAnnotationKW{} -> Nothing- BDAnnotationRest{} -> Nothing- BDMoveToKWDP{} -> Nothing- BDEnsureIndent{} -> Nothing- BDSetParSpacing{} -> Nothing- BDForceParSpacing{} -> Nothing- BDDebug{} -> Nothing- BDNonBottomSpacing _ x -> Just x
− source/library/Language/Haskell/Brittany/Internal/Transformations/Floating.hs
@@ -1,195 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Transformations.Floating where--import qualified Data.Generics.Uniplate.Direct as Uniplate-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils------ note that this is not total, and cannot be with that exact signature.-mergeIndents :: BrIndent -> BrIndent -> BrIndent-mergeIndents BrIndentNone x = x-mergeIndents x BrIndentNone = x-mergeIndents (BrIndentSpecial i) (BrIndentSpecial j) = BrIndentSpecial (max i j)-mergeIndents _ _ = error "mergeIndents"---transformSimplifyFloating :: BriDoc -> BriDoc-transformSimplifyFloating = stepBO .> stepFull- -- note that semantically, stepFull is completely sufficient.- -- but the bottom-up switch-to-top-down-on-match transformation has much- -- better complexity.- -- UPDATE: by now, stepBO does more than stepFull; for semantic equivalence- -- the push/pop cases would need to be copied over- where- descendPrior = transformDownMay $ \case- -- prior floating in- BDAnnotationPrior annKey1 (BDPar ind line indented) ->- Just $ BDPar ind (BDAnnotationPrior annKey1 line) indented- BDAnnotationPrior annKey1 (BDSeq (l:lr)) ->- Just $ BDSeq (BDAnnotationPrior annKey1 l:lr)- BDAnnotationPrior annKey1 (BDLines (l:lr)) ->- Just $ BDLines (BDAnnotationPrior annKey1 l:lr)- BDAnnotationPrior annKey1 (BDCols sig (l:lr)) ->- Just $ BDCols sig (BDAnnotationPrior annKey1 l:lr)- BDAnnotationPrior annKey1 (BDAddBaseY indent x) ->- Just $ BDAddBaseY indent $ BDAnnotationPrior annKey1 x- BDAnnotationPrior annKey1 (BDDebug s x) ->- Just $ BDDebug s $ BDAnnotationPrior annKey1 x- _ -> Nothing- descendRest = transformDownMay $ \case- -- post floating in- BDAnnotationRest annKey1 (BDPar ind line indented) ->- Just $ BDPar ind line $ BDAnnotationRest annKey1 indented- BDAnnotationRest annKey1 (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDLines list) ->- Just $ BDLines $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAnnotationRest annKey1 $ List.last cols]- BDAnnotationRest annKey1 (BDAddBaseY indent x) ->- Just $ BDAddBaseY indent $ BDAnnotationRest annKey1 x- BDAnnotationRest annKey1 (BDDebug s x) ->- Just $ BDDebug s $ BDAnnotationRest annKey1 x- _ -> Nothing- descendKW = transformDownMay $ \case- -- post floating in- BDAnnotationKW annKey1 kw (BDPar ind line indented) ->- Just $ BDPar ind line $ BDAnnotationKW annKey1 kw indented- BDAnnotationKW annKey1 kw (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAnnotationKW annKey1 kw $ List.last list]- BDAnnotationKW annKey1 kw (BDLines list) ->- Just $ BDLines $ List.init list ++ [BDAnnotationKW annKey1 kw $ List.last list]- BDAnnotationKW annKey1 kw (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAnnotationKW annKey1 kw $ List.last cols]- BDAnnotationKW annKey1 kw (BDAddBaseY indent x) ->- Just $ BDAddBaseY indent $ BDAnnotationKW annKey1 kw x- BDAnnotationKW annKey1 kw (BDDebug s x) ->- Just $ BDDebug s $ BDAnnotationKW annKey1 kw x- _ -> Nothing- descendBYPush = transformDownMay $ \case- BDBaseYPushCur (BDCols sig cols@(_:_)) ->- Just $ BDCols sig (BDBaseYPushCur (List.head cols) : List.tail cols)- BDBaseYPushCur (BDDebug s x) ->- Just $ BDDebug s (BDBaseYPushCur x)- _ -> Nothing- descendBYPop = transformDownMay $ \case- BDBaseYPop (BDCols sig cols@(_:_)) ->- Just $ BDCols sig (List.init cols ++ [BDBaseYPop (List.last cols)])- BDBaseYPop (BDDebug s x) ->- Just $ BDDebug s (BDBaseYPop x)- _ -> Nothing- descendILPush = transformDownMay $ \case- BDIndentLevelPushCur (BDCols sig cols@(_:_)) ->- Just $ BDCols sig (BDIndentLevelPushCur (List.head cols) : List.tail cols)- BDIndentLevelPushCur (BDDebug s x) ->- Just $ BDDebug s (BDIndentLevelPushCur x)- _ -> Nothing- descendILPop = transformDownMay $ \case- BDIndentLevelPop (BDCols sig cols@(_:_)) ->- Just $ BDCols sig (List.init cols ++ [BDIndentLevelPop (List.last cols)])- BDIndentLevelPop (BDDebug s x) ->- Just $ BDDebug s (BDIndentLevelPop x)- _ -> Nothing- descendAddB = transformDownMay $ \case- BDAddBaseY BrIndentNone x ->- Just x- -- AddIndent floats into Lines.- BDAddBaseY indent (BDLines lines) ->- Just $ BDLines $ BDAddBaseY indent <$> lines- -- AddIndent floats into last column- BDAddBaseY indent (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]- -- merge AddIndent and Par- BDAddBaseY ind1 (BDPar ind2 line indented) ->- Just $ BDPar (mergeIndents ind1 ind2) line indented- BDAddBaseY ind (BDAnnotationPrior annKey1 x) ->- Just $ BDAnnotationPrior annKey1 (BDAddBaseY ind x)- BDAddBaseY ind (BDAnnotationRest annKey1 x) ->- Just $ BDAnnotationRest annKey1 (BDAddBaseY ind x)- BDAddBaseY ind (BDAnnotationKW annKey1 kw x) ->- Just $ BDAnnotationKW annKey1 kw (BDAddBaseY ind x)- BDAddBaseY ind (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAddBaseY ind (List.last list)]- BDAddBaseY _ lit@BDLit{} ->- Just $ lit- BDAddBaseY ind (BDBaseYPushCur x) ->- Just $ BDBaseYPushCur (BDAddBaseY ind x)- BDAddBaseY ind (BDBaseYPop x) ->- Just $ BDBaseYPop (BDAddBaseY ind x)- BDAddBaseY ind (BDDebug s x) ->- Just $ BDDebug s (BDAddBaseY ind x)- BDAddBaseY ind (BDIndentLevelPop x) ->- Just $ BDIndentLevelPop (BDAddBaseY ind x)- BDAddBaseY ind (BDIndentLevelPushCur x) ->- Just $ BDIndentLevelPushCur (BDAddBaseY ind x)- BDAddBaseY ind (BDEnsureIndent ind2 x) ->- Just $ BDEnsureIndent (mergeIndents ind ind2) x- _ -> Nothing- stepBO :: BriDoc -> BriDoc- stepBO = -- traceFunctionWith "stepBO" (show . briDocToDocWithAnns) (show . briDocToDocWithAnns) $- transformUp f- where- f = \case- x@BDAnnotationPrior{} -> descendPrior x- x@BDAnnotationKW{} -> descendKW x- x@BDAnnotationRest{} -> descendRest x- x@BDAddBaseY{} -> descendAddB x- x@BDBaseYPushCur{} -> descendBYPush x- x@BDBaseYPop{} -> descendBYPop x- x@BDIndentLevelPushCur{} -> descendILPush x- x@BDIndentLevelPop{} -> descendILPop x- x -> x- stepFull = -- traceFunctionWith "stepFull" (show . briDocToDocWithAnns) (show . briDocToDocWithAnns) $- Uniplate.rewrite $ \case- BDAddBaseY BrIndentNone x ->- Just $ x- -- AddIndent floats into Lines.- BDAddBaseY indent (BDLines lines) ->- Just $ BDLines $ BDAddBaseY indent <$> lines- -- AddIndent floats into last column- BDAddBaseY indent (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]- BDAddBaseY ind (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAddBaseY ind (List.last list)]- -- merge AddIndent and Par- BDAddBaseY ind1 (BDPar ind2 line indented) ->- Just $ BDPar (mergeIndents ind1 ind2) line indented- BDAddBaseY _ lit@BDLit{} ->- Just $ lit- BDAddBaseY ind (BDBaseYPushCur x) ->- Just $ BDBaseYPushCur (BDAddBaseY ind x)- BDAddBaseY ind (BDBaseYPop x) ->- Just $ BDBaseYPop (BDAddBaseY ind x)- -- prior floating in- BDAnnotationPrior annKey1 (BDPar ind line indented) ->- Just $ BDPar ind (BDAnnotationPrior annKey1 line) indented- BDAnnotationPrior annKey1 (BDSeq (l:lr)) ->- Just $ BDSeq ((BDAnnotationPrior annKey1 l):lr)- BDAnnotationPrior annKey1 (BDLines (l:lr)) ->- Just $ BDLines ((BDAnnotationPrior annKey1 l):lr)- BDAnnotationPrior annKey1 (BDCols sig (l:lr)) ->- Just $ BDCols sig ((BDAnnotationPrior annKey1 l):lr)- -- EnsureIndent float-in- -- BDEnsureIndent indent (BDCols sig (col:colr)) ->- -- Just $ BDCols sig (BDEnsureIndent indent col : (BDAddBaseY indent <$> colr))- -- not sure if the following rule is necessary; tests currently are- -- unaffected.- -- BDEnsureIndent indent (BDLines lines) ->- -- Just $ BDLines $ BDEnsureIndent indent <$> lines- -- post floating in- BDAnnotationRest annKey1 (BDPar ind line indented) ->- Just $ BDPar ind line $ BDAnnotationRest annKey1 indented- BDAnnotationRest annKey1 (BDSeq list) ->- Just $ BDSeq $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDLines list) ->- Just $ BDLines $ List.init list ++ [BDAnnotationRest annKey1 $ List.last list]- BDAnnotationRest annKey1 (BDCols sig cols) ->- Just $ BDCols sig $ List.init cols ++ [BDAnnotationRest annKey1 $ List.last cols]- _ -> Nothing
− source/library/Language/Haskell/Brittany/Internal/Transformations/Indent.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Transformations.Indent where--import qualified Data.Generics.Uniplate.Direct as Uniplate-import qualified GHC.OldList as List-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.Types------ prepare layouting by translating BDPar's, replacing them with Indents and--- floating those in. This gives a more clear picture of what exactly is--- affected by what amount of indentation.-transformSimplifyIndent :: BriDoc -> BriDoc-transformSimplifyIndent = Uniplate.rewrite $ \case- BDPar ind (BDLines lines) indented ->- Just $ BDEnsureIndent ind $ BDLines $ lines ++ [indented]- BDPar ind (BDCols sig cols) indented ->- Just $ BDCols sig (List.init cols ++ [BDPar ind (List.last cols) indented])- BDPar BrIndentNone _ _ -> Nothing- BDPar ind x indented ->- Just $ BDPar BrIndentNone (BDAddBaseY ind x) (BDEnsureIndent ind indented)- -- BDPar ind x indented ->- -- Just $ BDLines- -- [ BDAddBaseY ind x- -- , BDEnsureIndent ind indented- -- ]- BDLines lines | any ( \case- BDLines{} -> True- BDEmpty{} -> True- _ -> False- )- lines ->- Just $ BDLines $ filter isNotEmpty $ lines >>= \case- BDLines l -> l- x -> [x]- BDLines [l] -> Just l- BDAddBaseY i (BDAnnotationPrior k x) ->- Just $ BDAnnotationPrior k (BDAddBaseY i x)- BDAddBaseY i (BDAnnotationKW k kw x) ->- Just $ BDAnnotationKW k kw (BDAddBaseY i x)- BDAddBaseY i (BDAnnotationRest k x) ->- Just $ BDAnnotationRest k (BDAddBaseY i x)- BDAddBaseY i (BDSeq l) ->- Just $ BDSeq $ List.init l ++ [BDAddBaseY i $ List.last l]- BDAddBaseY i (BDCols sig l) ->- Just $ BDCols sig $ List.init l ++ [BDAddBaseY i $ List.last l]- BDAddBaseY _ lit@BDLit{} -> Just lit-- _ -> Nothing
− source/library/Language/Haskell/Brittany/Internal/Transformations/Par.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Internal.Transformations.Par where--import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils----transformSimplifyPar :: BriDoc -> BriDoc-transformSimplifyPar = transformUp $ \case- -- BDPar BrIndentNone line1 line2 -> Just $ BDLines [line1, line2]- -- BDPar line indented ->- -- Just $ BDLines [line, indented]- -- BDPar ind1 (BDPar ind2 line p1) p2 | ind1==ind2 ->- -- Just $ BDPar ind1 line (BDLines [p1, p2])- x@(BDPar _ (BDPar _ BDPar{} _) _) -> x- BDPar ind1 (BDPar ind2 line p1) (BDLines indenteds) ->- BDPar ind1 line (BDLines (BDEnsureIndent ind2 p1 : indenteds))- BDPar ind1 (BDPar ind2 line p1) p2 ->- BDPar ind1 line (BDLines [BDEnsureIndent ind2 p1, p2])- BDLines lines | any ( \case- BDLines{} -> True- BDEmpty{} -> True- _ -> False- )- lines -> case go lines of- [] -> BDEmpty- [x] -> x- xs -> BDLines xs- where- go = (=<<) $ \case- BDLines l -> go l- BDEmpty -> []- x -> [x]- BDLines [] -> BDEmpty- BDLines [x] -> x- -- BDCols sig cols | BDPar ind line indented <- List.last cols ->- -- Just $ BDPar ind (BDCols sig (List.init cols ++ [line])) indented- -- BDPar BrIndentNone line indented ->- -- Just $ BDLines [line, indented]- BDEnsureIndent BrIndentNone x -> x- x -> x
− source/library/Language/Haskell/Brittany/Internal/Types.hs
@@ -1,463 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE PatternSynonyms #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}--module Language.Haskell.Brittany.Internal.Types where--import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS-import qualified Data.Data-import Data.Generics.Uniplate.Direct as Uniplate-import qualified Data.Kind as Kind-import qualified Data.Strict.Maybe as Strict-import qualified Data.Text.Lazy.Builder as Text.Builder-import GHC (AnnKeywordId, GenLocated, Located, SrcSpan)-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Prelude-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import Language.Haskell.GHC.ExactPrint (AnnKey)-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types-import Language.Haskell.GHC.ExactPrint.Types (Anns)-import qualified Safe---data PerItemConfig = PerItemConfig- { _icd_perBinding :: Map String (CConfig Maybe)- , _icd_perKey :: Map ExactPrint.Types.AnnKey (CConfig Maybe)- }- deriving Data.Data.Data--type PPM = MultiRWSS.MultiRWS- '[Map ExactPrint.AnnKey ExactPrint.Anns, PerItemConfig, Config, ExactPrint.Anns]- '[Text.Builder.Builder, [BrittanyError], Seq String]- '[]--type PPMLocal = MultiRWSS.MultiRWS- '[Config, ExactPrint.Anns]- '[Text.Builder.Builder, [BrittanyError], Seq String]- '[]--newtype TopLevelDeclNameMap = TopLevelDeclNameMap (Map ExactPrint.AnnKey String)--data LayoutState = LayoutState- { _lstate_baseYs :: [Int]- -- ^ stack of number of current indentation columns- -- (not number of indentations).- , _lstate_curYOrAddNewline :: Either Int Int- -- ^ Either:- -- 1) number of chars in the current line.- -- 2) number of newlines to be inserted before inserting any- -- non-space elements.- , _lstate_indLevels :: [Int]- -- ^ stack of current indentation levels. set for- -- any layout-affected elements such as- -- let/do/case/where elements.- -- The main purpose of this member is to- -- properly align comments, as their- -- annotation positions are relative to the- -- current layout indentation level.- , _lstate_indLevelLinger :: Int -- like a "last" of indLevel. Used for- -- properly treating cases where comments- -- on the first indented element have an- -- annotation offset relative to the last- -- non-indented element, which is confusing.- , _lstate_comments :: Anns- , _lstate_commentCol :: Maybe Int -- this communicates two things:- -- firstly, that cursor is currently- -- at the end of a comment (so needs- -- newline before any actual content).- -- secondly, the column at which- -- insertion of comments started.- , _lstate_addSepSpace :: Maybe Int -- number of spaces to insert if anyone- -- writes (any non-spaces) in the- -- current line.- -- , _lstate_isNewline :: NewLineState- -- -- captures if the layouter currently is in a new line, i.e. if the- -- -- current line only contains (indentation) spaces.- -- this is mostly superseeded by curYOrAddNewline, iirc.- , _lstate_commentNewlines :: Int -- number of newlines inserted due to- -- move-to-DP at a start of a comment.- -- Necessary because some keyword DPs- -- are relative to the last non-comment- -- entity (for some reason).- -- This is not very strictly reset to 0,- -- so we might in some cases get "artifacts"- -- from previous document elements.- -- But the worst effect at the moment would- -- be that we introduce less newlines on- -- moveToKWDP, which seems harmless enough.- }--lstate_baseY :: LayoutState -> Int-lstate_baseY = Safe.headNote "lstate_baseY" . _lstate_baseYs--lstate_indLevel :: LayoutState -> Int-lstate_indLevel = Safe.headNote "lstate_baseY" . _lstate_indLevels---- evil, incomplete Show instance; only for debugging.-instance Show LayoutState where- show state =- "LayoutState"- ++ "{baseYs=" ++ show (_lstate_baseYs state)- ++ ",curYOrAddNewline=" ++ show (_lstate_curYOrAddNewline state)- ++ ",indLevels=" ++ show (_lstate_indLevels state)- ++ ",indLevelLinger=" ++ show (_lstate_indLevelLinger state)- ++ ",commentCol=" ++ show (_lstate_commentCol state)- ++ ",addSepSpace=" ++ show (_lstate_addSepSpace state)- ++ ",commentNewlines=" ++ show (_lstate_commentNewlines state)- ++ "}"---- data NewLineState = NewLineStateInit -- initial state. we do not know if in a--- -- newline, really. by special-casing--- -- this we can appropriately handle it--- -- differently at use-site.--- | NewLineStateYes--- | NewLineStateNo--- deriving Eq---- data LayoutSettings = LayoutSettings--- { _lsettings_cols :: Int -- the thing that has default 80.--- , _lsettings_indentPolicy :: IndentPolicy--- , _lsettings_indentAmount :: Int--- , _lsettings_indentWhereSpecial :: Bool -- indent where only 1 sometimes (TODO).--- , _lsettings_indentListSpecial :: Bool -- use some special indentation for ","--- -- when creating zero-indentation--- -- multi-line list literals.--- , _lsettings_importColumn :: Int--- , _lsettings_initialAnns :: ExactPrint.Anns--- }--data BrittanyError- = ErrorInput String- -- ^ parsing failed- | ErrorUnusedComment String- -- ^ internal error: some comment went missing- | ErrorMacroConfig String String- -- ^ in-source config string parsing error; first argument is the parser- -- output and second the corresponding, ill-formed input.- | LayoutWarning String- -- ^ some warning- | forall ast . Data.Data.Data ast => ErrorUnknownNode String (GenLocated SrcSpan ast)- -- ^ internal error: pretty-printing is not implemented for type of node- -- in the syntax-tree- | ErrorOutputCheck- -- ^ checking the output for syntactic validity failed--data BriSpacing = BriSpacing- { _bs_spacePastLineIndent :: Int -- space in the current,- -- potentially somewhat filled- -- line.- , _bs_spacePastIndent :: Int -- space required in properly- -- indented blocks below the- -- current line.- }--data ColSig- = ColTyOpPrefix- -- any prefixed operator/paren/"::"/..- -- expected to have exactly two colums.- -- e.g. ":: foo"- -- 111222- -- "-> bar asd asd"- -- 11122222222222- | ColPatternsFuncPrefix- -- pattern-part of the lhs, e.g. "func (foo a b) c _".- -- Has variable number of columns depending on the number of patterns.- | ColPatternsFuncInfix- -- pattern-part of the lhs, e.g. "Foo a <> Foo b".- -- Has variable number of columns depending on the number of patterns.- | ColPatterns- | ColCasePattern- | ColBindingLine (Maybe Text)- -- e.g. "func pat pat = expr"- -- 1111111111111222222- -- or "pat | stmt -> expr"- -- 111111111112222222- -- expected to have exactly two columns.- | ColGuard- -- e.g. "func pat pat | cond = ..."- -- 11111111111112222222- -- or "pat | cond1, cond2 -> ..."- -- 1111222222222222222- -- expected to have exactly two columns- | ColGuardedBody- -- e.g. | foofoo = 1- -- | bar = 2- -- 111111111222- -- expected to have exactly two columns- | ColBindStmt- | ColDoLet -- the non-indented variant- | ColRec- | ColRecUpdate -- used for both RecCon and RecUpd. TODO: refactor to reflect?- | ColRecDecl- | ColListComp- | ColList- | ColApp Text- | ColTuple- | ColTuples- | ColOpPrefix -- merge with ColList ? other stuff?- | ColImport-- -- TODO- deriving (Eq, Ord, Data.Data.Data, Show)--data BrIndent = BrIndentNone- | BrIndentRegular- | BrIndentSpecial Int- deriving (Eq, Ord, Data.Data.Data, Show)--type ToBriDocM = MultiRWSS.MultiRWS- '[Config, Anns] -- reader- '[[BrittanyError], Seq String] -- writer- '[NodeAllocIndex] -- state--type ToBriDoc (sym :: Kind.Type -> Kind.Type) = Located (sym GhcPs) -> ToBriDocM BriDocNumbered-type ToBriDoc' sym = Located sym -> ToBriDocM BriDocNumbered-type ToBriDocC sym c = Located sym -> ToBriDocM c--data DocMultiLine- = MultiLineNo- | MultiLinePossible- deriving (Eq)---- isomorphic to BriDocF Identity. Provided for ease of use, as we do a lot--- of transformations on `BriDocF Identity`s and it is really annoying to--- `Identity`/`runIdentity` everywhere.-data BriDoc- = -- BDWrapAnnKey AnnKey BriDoc- BDEmpty- | BDLit !Text- | BDSeq [BriDoc] -- elements other than the last should- -- not contains BDPars.- | BDCols ColSig [BriDoc] -- elements other than the last- -- should not contains BDPars- | BDSeparator -- semantically, space-unless-at-end-of-line.- | BDAddBaseY BrIndent BriDoc- | BDBaseYPushCur BriDoc- | BDBaseYPop BriDoc- | BDIndentLevelPushCur BriDoc- | BDIndentLevelPop BriDoc- | BDPar- { _bdpar_indent :: BrIndent- , _bdpar_restOfLine :: BriDoc -- should not contain other BDPars- , _bdpar_indented :: BriDoc- }- -- | BDAddIndent BrIndent (BriDocF f)- -- | BDNewline- | BDAlt [BriDoc]- | BDForwardLineMode BriDoc- | BDExternal AnnKey- (Set AnnKey) -- set of annkeys contained within the node- -- to be printed via exactprint- Bool -- should print extra comment ?- Text- | BDPlain !Text -- used for QuasiQuotes, content can be multi-line- -- (contrast to BDLit)- | BDAnnotationPrior AnnKey BriDoc- | BDAnnotationKW AnnKey (Maybe AnnKeywordId) BriDoc- | BDAnnotationRest AnnKey BriDoc- | BDMoveToKWDP AnnKey AnnKeywordId Bool BriDoc -- True if should respect x offset- | BDLines [BriDoc]- | BDEnsureIndent BrIndent BriDoc- -- the following constructors are only relevant for the alt transformation- -- and are removed afterwards. They should never occur in any BriDoc- -- after the alt transformation.- | BDForceMultiline BriDoc- | BDForceSingleline BriDoc- | BDNonBottomSpacing Bool BriDoc- | BDSetParSpacing BriDoc- | BDForceParSpacing BriDoc- -- pseudo-deprecated- | BDDebug String BriDoc- deriving (Data.Data.Data, Eq, Ord)--data BriDocF f- = -- BDWrapAnnKey AnnKey BriDoc- BDFEmpty- | BDFLit !Text- | BDFSeq [f (BriDocF f)] -- elements other than the last should- -- not contains BDPars.- | BDFCols ColSig [f (BriDocF f)] -- elements other than the last- -- should not contains BDPars- | BDFSeparator -- semantically, space-unless-at-end-of-line.- | BDFAddBaseY BrIndent (f (BriDocF f))- | BDFBaseYPushCur (f (BriDocF f))- | BDFBaseYPop (f (BriDocF f))- | BDFIndentLevelPushCur (f (BriDocF f))- | BDFIndentLevelPop (f (BriDocF f))- | BDFPar- { _bdfpar_indent :: BrIndent- , _bdfpar_restOfLine :: f (BriDocF f) -- should not contain other BDPars- , _bdfpar_indented :: f (BriDocF f)- }- -- | BDAddIndent BrIndent (BriDocF f)- -- | BDNewline- | BDFAlt [f (BriDocF f)]- | BDFForwardLineMode (f (BriDocF f))- | BDFExternal AnnKey- (Set AnnKey) -- set of annkeys contained within the node- -- to be printed via exactprint- Bool -- should print extra comment ?- Text- | BDFPlain !Text -- used for QuasiQuotes, content can be multi-line- -- (contrast to BDLit)- | BDFAnnotationPrior AnnKey (f (BriDocF f))- | BDFAnnotationKW AnnKey (Maybe AnnKeywordId) (f (BriDocF f))- | BDFAnnotationRest AnnKey (f (BriDocF f))- | BDFMoveToKWDP AnnKey AnnKeywordId Bool (f (BriDocF f)) -- True if should respect x offset- | BDFLines [(f (BriDocF f))]- | BDFEnsureIndent BrIndent (f (BriDocF f))- | BDFForceMultiline (f (BriDocF f))- | BDFForceSingleline (f (BriDocF f))- | BDFNonBottomSpacing Bool (f (BriDocF f))- | BDFSetParSpacing (f (BriDocF f))- | BDFForceParSpacing (f (BriDocF f))- | BDFDebug String (f (BriDocF f))---- deriving instance Data.Data.Data (BriDocF Identity)-deriving instance Data.Data.Data (BriDocF ((,) Int))--type BriDocFInt = BriDocF ((,) Int)-type BriDocNumbered = (Int, BriDocFInt)--instance Uniplate.Uniplate BriDoc where- uniplate x@BDEmpty{} = plate x- uniplate x@BDLit{} = plate x- uniplate (BDSeq list ) = plate BDSeq ||* list- uniplate (BDCols sig list) = plate BDCols |- sig ||* list- uniplate x@BDSeparator = plate x- uniplate (BDAddBaseY ind bd ) = plate BDAddBaseY |- ind |* bd- uniplate (BDBaseYPushCur bd) = plate BDBaseYPushCur |* bd- uniplate (BDBaseYPop bd) = plate BDBaseYPop |* bd- uniplate (BDIndentLevelPushCur bd) = plate BDIndentLevelPushCur |* bd- uniplate (BDIndentLevelPop bd) = plate BDIndentLevelPop |* bd- uniplate (BDPar ind line indented) = plate BDPar |- ind |* line |* indented- uniplate (BDAlt alts ) = plate BDAlt ||* alts- uniplate (BDForwardLineMode bd ) = plate BDForwardLineMode |* bd- uniplate x@BDExternal{} = plate x- uniplate x@BDPlain{} = plate x- uniplate (BDAnnotationPrior annKey bd) =- plate BDAnnotationPrior |- annKey |* bd- uniplate (BDAnnotationKW annKey kw bd) =- plate BDAnnotationKW |- annKey |- kw |* bd- uniplate (BDAnnotationRest annKey bd) =- plate BDAnnotationRest |- annKey |* bd- uniplate (BDMoveToKWDP annKey kw b bd) =- plate BDMoveToKWDP |- annKey |- kw |- b |* bd- uniplate (BDLines lines ) = plate BDLines ||* lines- uniplate (BDEnsureIndent ind bd ) = plate BDEnsureIndent |- ind |* bd- uniplate (BDForceMultiline bd ) = plate BDForceMultiline |* bd- uniplate (BDForceSingleline bd ) = plate BDForceSingleline |* bd- uniplate (BDNonBottomSpacing b bd) = plate BDNonBottomSpacing |- b |* bd- uniplate (BDSetParSpacing bd ) = plate BDSetParSpacing |* bd- uniplate (BDForceParSpacing bd ) = plate BDForceParSpacing |* bd- uniplate (BDDebug s bd ) = plate BDDebug |- s |* bd--newtype NodeAllocIndex = NodeAllocIndex Int---- TODO: rename to "dropLabels" ?-unwrapBriDocNumbered :: BriDocNumbered -> BriDoc-unwrapBriDocNumbered tpl = case snd tpl of- BDFEmpty -> BDEmpty- BDFLit t -> BDLit t- BDFSeq list -> BDSeq $ rec <$> list- BDFCols sig list -> BDCols sig $ rec <$> list- BDFSeparator -> BDSeparator- BDFAddBaseY ind bd -> BDAddBaseY ind $ rec bd- BDFBaseYPushCur bd -> BDBaseYPushCur $ rec bd- BDFBaseYPop bd -> BDBaseYPop $ rec bd- BDFIndentLevelPushCur bd -> BDIndentLevelPushCur $ rec bd- BDFIndentLevelPop bd -> BDIndentLevelPop $ rec bd- BDFPar ind line indented -> BDPar ind (rec line) (rec indented)- BDFAlt alts -> BDAlt $ rec <$> alts -- not that this will happen- BDFForwardLineMode bd -> BDForwardLineMode $ rec bd- BDFExternal k ks c t -> BDExternal k ks c t- BDFPlain t -> BDPlain t- BDFAnnotationPrior annKey bd -> BDAnnotationPrior annKey $ rec bd- BDFAnnotationKW annKey kw bd -> BDAnnotationKW annKey kw $ rec bd- BDFAnnotationRest annKey bd -> BDAnnotationRest annKey $ rec bd- BDFMoveToKWDP annKey kw b bd -> BDMoveToKWDP annKey kw b $ rec bd- BDFLines lines -> BDLines $ rec <$> lines- BDFEnsureIndent ind bd -> BDEnsureIndent ind $ rec bd- BDFForceMultiline bd -> BDForceMultiline $ rec bd- BDFForceSingleline bd -> BDForceSingleline $ rec bd- BDFNonBottomSpacing b bd -> BDNonBottomSpacing b $ rec bd- BDFSetParSpacing bd -> BDSetParSpacing $ rec bd- BDFForceParSpacing bd -> BDForceParSpacing $ rec bd- BDFDebug s bd -> BDDebug (s ++ "@" ++ show (fst tpl)) $ rec bd- where rec = unwrapBriDocNumbered--isNotEmpty :: BriDoc -> Bool-isNotEmpty BDEmpty = False-isNotEmpty _ = True---- this might not work. is not used anywhere either.-briDocSeqSpine :: BriDoc -> ()-briDocSeqSpine = \case- BDEmpty -> ()- BDLit _t -> ()- BDSeq list -> foldl' ((briDocSeqSpine .) . seq) () list- BDCols _sig list -> foldl' ((briDocSeqSpine .) . seq) () list- BDSeparator -> ()- BDAddBaseY _ind bd -> briDocSeqSpine bd- BDBaseYPushCur bd -> briDocSeqSpine bd- BDBaseYPop bd -> briDocSeqSpine bd- BDIndentLevelPushCur bd -> briDocSeqSpine bd- BDIndentLevelPop bd -> briDocSeqSpine bd- BDPar _ind line indented -> briDocSeqSpine line `seq` briDocSeqSpine indented- BDAlt alts -> foldl' (\() -> briDocSeqSpine) () alts- BDForwardLineMode bd -> briDocSeqSpine bd- BDExternal{} -> ()- BDPlain{} -> ()- BDAnnotationPrior _annKey bd -> briDocSeqSpine bd- BDAnnotationKW _annKey _kw bd -> briDocSeqSpine bd- BDAnnotationRest _annKey bd -> briDocSeqSpine bd- BDMoveToKWDP _annKey _kw _b bd -> briDocSeqSpine bd- BDLines lines -> foldl' (\() -> briDocSeqSpine) () lines- BDEnsureIndent _ind bd -> briDocSeqSpine bd- BDForceMultiline bd -> briDocSeqSpine bd- BDForceSingleline bd -> briDocSeqSpine bd- BDNonBottomSpacing _ bd -> briDocSeqSpine bd- BDSetParSpacing bd -> briDocSeqSpine bd- BDForceParSpacing bd -> briDocSeqSpine bd- BDDebug _s bd -> briDocSeqSpine bd--briDocForceSpine :: BriDoc -> BriDoc-briDocForceSpine bd = briDocSeqSpine bd `seq` bd---data VerticalSpacingPar- = VerticalSpacingParNone -- no indented lines- | VerticalSpacingParSome Int -- indented lines, requiring this much- -- vertical space at most- | VerticalSpacingParAlways Int -- indented lines, requiring this much- -- vertical space at most, but should- -- be considered as having space for- -- any spacing validity check.- -- TODO: it might be wrong not to extend "always" to the none case, i.e.- -- we might get better properties of spacing operators by having a- -- product like (Normal|Always, None|Some Int).- deriving (Eq, Show)--data VerticalSpacing- = VerticalSpacing- { _vs_sameLine :: !Int- , _vs_paragraph :: !VerticalSpacingPar- , _vs_parFlag :: !Bool- }- deriving (Eq, Show)--newtype LineModeValidity a = LineModeValidity (Strict.Maybe a)- deriving (Functor, Applicative, Monad, Show, Alternative)--pattern LineModeValid :: forall t. t -> LineModeValidity t-pattern LineModeValid x = LineModeValidity (Strict.Just x) :: LineModeValidity t-pattern LineModeInvalid :: forall t. LineModeValidity t-pattern LineModeInvalid = LineModeValidity Strict.Nothing :: LineModeValidity t
− source/library/Language/Haskell/Brittany/Internal/Utils.hs
@@ -1,270 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.Brittany.Internal.Utils where--import qualified Data.ByteString as B-import qualified Data.Coerce-import Data.Data-import Data.Generics.Aliases-import qualified Data.Generics.Uniplate.Direct as Uniplate-import qualified Data.Semigroup as Semigroup-import qualified Data.Sequence as Seq-import DataTreePrint-import qualified GHC.Data.FastString as GHC-import qualified GHC.Driver.Session as GHC-import qualified GHC.Hs.Extension as HsExtension-import qualified GHC.OldList as List-import GHC.Types.Name.Occurrence as OccName (occNameString)-import qualified GHC.Types.SrcLoc as GHC-import qualified GHC.Utils.Outputable as GHC-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types-import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils-import qualified Text.PrettyPrint as PP----parDoc :: String -> PP.Doc-parDoc = PP.fsep . fmap PP.text . List.words--parDocW :: [String] -> PP.Doc-parDocW = PP.fsep . fmap PP.text . List.words . List.unwords---showSDoc_ :: GHC.SDoc -> String-showSDoc_ = GHC.showSDoc GHC.unsafeGlobalDynFlags--showOutputable :: (GHC.Outputable a) => a -> String-showOutputable = GHC.showPpr GHC.unsafeGlobalDynFlags--fromMaybeIdentity :: Identity a -> Maybe a -> Identity a-fromMaybeIdentity x y = Data.Coerce.coerce $ fromMaybe (Data.Coerce.coerce x) y--fromOptionIdentity :: Identity a -> Maybe a -> Identity a-fromOptionIdentity x y =- Data.Coerce.coerce $ fromMaybe (Data.Coerce.coerce x) y---- maximum monoid over N+0--- or more than N, because Num is allowed.-newtype Max a = Max { getMax :: a }- deriving (Eq, Ord, Show, Bounded, Num)--instance (Num a, Ord a) => Semigroup (Max a) where- (<>) = Data.Coerce.coerce (max :: a -> a -> a)--instance (Num a, Ord a) => Monoid (Max a) where- mempty = Max 0- mappend = (<>)--newtype ShowIsId = ShowIsId String deriving Data--instance Show ShowIsId where show (ShowIsId x) = x--data A x = A ShowIsId x deriving Data--customLayouterF :: ExactPrint.Types.Anns -> LayouterF-customLayouterF anns layoutF =- DataToLayouter- $ f- `extQ` showIsId- `extQ` fastString- `extQ` bytestring- `extQ` occName- `extQ` srcSpan- `ext2Q` located- where- DataToLayouter f = defaultLayouterF layoutF- simpleLayouter :: String -> NodeLayouter- simpleLayouter s = NodeLayouter (length s) False (const $ PP.text s)- showIsId :: ShowIsId -> NodeLayouter- showIsId (ShowIsId s) = NodeLayouter (length s + 2) True $ \case- Left True -> PP.parens $ PP.text s- Left False -> PP.text s- Right _ -> PP.text s- fastString =- simpleLayouter . ("{FastString: "++) . (++"}") . show :: GHC.FastString- -> NodeLayouter- bytestring = simpleLayouter . show :: B.ByteString -> NodeLayouter- occName = simpleLayouter . ("{OccName: "++) . (++"}") . OccName.occNameString- srcSpan :: GHC.SrcSpan -> NodeLayouter- srcSpan ss = simpleLayouter- -- - $ "{"++ showSDoc_ (GHC.ppr ss)++"}"- $ "{" ++ showOutputable ss ++ "}"- located :: (Data b, Data loc) => GHC.GenLocated loc b -> NodeLayouter- located (GHC.L ss a) = runDataToLayouter layoutF $ A annStr a- where- annStr = case cast ss of- Just (s :: GHC.SrcSpan) ->- ShowIsId $ show (ExactPrint.Utils.getAnnotationEP (GHC.L s a) anns)- Nothing -> ShowIsId "nnnnnnnn"--customLayouterNoAnnsF :: LayouterF-customLayouterNoAnnsF layoutF =- DataToLayouter- $ f- `extQ` showIsId- `extQ` fastString- `extQ` bytestring- `extQ` occName- `extQ` srcSpan- `ext2Q` located- where- DataToLayouter f = defaultLayouterF layoutF- simpleLayouter :: String -> NodeLayouter- simpleLayouter s = NodeLayouter (length s) False (const $ PP.text s)- showIsId :: ShowIsId -> NodeLayouter- showIsId (ShowIsId s) = NodeLayouter (length s + 2) True $ \case- Left True -> PP.parens $ PP.text s- Left False -> PP.text s- Right _ -> PP.text s- fastString =- simpleLayouter . ("{FastString: "++) . (++"}") . show :: GHC.FastString- -> NodeLayouter- bytestring = simpleLayouter . show :: B.ByteString -> NodeLayouter- occName = simpleLayouter . ("{OccName: "++) . (++"}") . OccName.occNameString- srcSpan :: GHC.SrcSpan -> NodeLayouter- srcSpan ss = simpleLayouter $ "{" ++ showSDoc_ (GHC.ppr ss) ++ "}"- located :: (Data b) => GHC.GenLocated loc b -> NodeLayouter- located (GHC.L _ss a) = runDataToLayouter layoutF a---- displayBriDocTree :: BriDoc -> PP.Doc--- displayBriDocTree = \case--- BDWrapAnnKey annKey doc -> def "BDWrapAnnKey"--- $ PP.text (show annKey)--- $+$ displayBriDocTree doc--- BDEmpty -> PP.text "BDEmpty"--- BDLit t -> def "BDLit" $ PP.text (show t)--- BDSeq list -> def "BDSeq" $ displayList list--- BDCols sig list -> def "BDCols" $ PP.text (show sig)--- $+$ displayList list--- BDSeparator -> PP.text "BDSeparator"--- BDPar rol indent lines -> def "BDPar" $ displayBriDocTree rol--- $+$ PP.text (show indent)--- $+$ displayList lines--- BDAlt alts -> def "BDAlt" $ displayList alts--- BDExternal ast _t -> def "BDExternal" (astToDoc ast)--- BDSpecialPostCommentLoc _ -> PP.text "BDSpecialPostCommentLoc"--- where--- def x r = PP.text x $+$ PP.nest 2 r--- displayList :: [BriDoc] -> PP.Doc--- displayList [] = PP.text "[]"--- displayList (x:xr) = PP.cat $ PP.text "[" <+> displayBriDocTree x--- : [PP.text "," <+> displayBriDocTree t | t<-xr]--- ++ [PP.text "]"]---- displayBriDocSimpleTree :: BriDocSimple -> PP.Doc--- displayBriDocSimpleTree = \case--- BDSWrapAnnKey annKey doc -> def "BDSWrapAnnKey"--- $ PP.text (show annKey)--- $+$ displayBriDocSimpleTree doc--- BDSLit t -> def "BDSLit" $ PP.text (show t)--- BDSSeq list -> def "BDSSeq" $ displayList list--- BDSCols sig list -> def "BDSCols" $ PP.text (show sig)--- $+$ displayList list--- BDSSeparator -> PP.text "BDSSeparator"--- BDSPar rol indent lines -> def "BDSPar" $ displayBriDocSimpleTree rol--- $+$ PP.text (show indent)--- $+$ displayList lines--- BDSExternal annKey _subKeys _t -> def "BDSExternal" (PP.text $ show annKey)--- BDSSpecialPostCommentLoc _ -> PP.text "BDSSpecialPostCommentLoc"--- where--- def x r = PP.text x $+$ PP.nest 2 r--- displayList :: [BriDocSimple] -> PP.Doc--- displayList [] = PP.text "[]"--- displayList (x:xr) = PP.cat $ PP.text "[" <+> displayBriDocSimpleTree x--- : [PP.text "," <+> displayBriDocSimpleTree t | t<-xr]--- ++ [PP.text "]"]--traceIfDumpConf- :: (MonadMultiReader Config m, Show a)- => String- -> (DebugConfig -> Identity (Semigroup.Last Bool))- -> a- -> m ()-traceIfDumpConf s accessor val = do- whenM (mAsk <&> _conf_debug .> accessor .> confUnpack) $ do- trace ("---- " ++ s ++ " ----\n" ++ show val) $ return ()--tellDebugMess :: MonadMultiWriter- (Seq String) m => String -> m ()-tellDebugMess s = mTell $ Seq.singleton s--tellDebugMessShow :: forall a m . (MonadMultiWriter- (Seq String) m, Show a) => a -> m ()-tellDebugMessShow = tellDebugMess . show---- i should really put that into multistate..-mModify :: MonadMultiState s m => (s -> s) -> m ()-mModify f = mGet >>= mSet . f--astToDoc :: Data ast => ast -> PP.Doc-astToDoc ast = printTreeWithCustom 160 customLayouterNoAnnsF ast--briDocToDoc :: BriDoc -> PP.Doc-briDocToDoc = astToDoc . removeAnnotations- where- removeAnnotations = Uniplate.transform $ \case- BDAnnotationPrior _ x -> x- BDAnnotationKW _ _ x -> x- BDAnnotationRest _ x -> x- x -> x--briDocToDocWithAnns :: BriDoc -> PP.Doc-briDocToDocWithAnns = astToDoc--annsDoc :: ExactPrint.Types.Anns -> PP.Doc-annsDoc = printTreeWithCustom 100 customLayouterNoAnnsF . fmap (ShowIsId . show)--breakEither :: (a -> Either b c) -> [a] -> ([b], [c])-breakEither _ [] = ([], [])-breakEither fn (a1:aR) = case fn a1 of- Left b -> (b : bs, cs)- Right c -> (bs, c : cs)- where- (bs, cs) = breakEither fn aR--spanMaybe :: (a -> Maybe b) -> [a] -> ([b], [a])-spanMaybe f (x1:xR) | Just y <- f x1 = (y : ys, xs)- where- (ys, xs) = spanMaybe f xR-spanMaybe _ xs = ([], xs)--data FirstLastView a- = FirstLastEmpty- | FirstLastSingleton a- | FirstLast a [a] a--splitFirstLast :: [a] -> FirstLastView a-splitFirstLast [] = FirstLastEmpty-splitFirstLast [x] = FirstLastSingleton x-splitFirstLast (x1:xr) = FirstLast x1 (List.init xr) (List.last xr)---- TODO: move to uniplate upstream?--- aka `transform`-transformUp :: Uniplate.Uniplate on => (on -> on) -> (on -> on)-transformUp f = g where g = f . Uniplate.descend g-_transformDown :: Uniplate.Uniplate on => (on -> on) -> (on -> on)-_transformDown f = g where g = Uniplate.descend g . f-transformDownMay :: Uniplate.Uniplate on => (on -> Maybe on) -> (on -> on)-transformDownMay f = g where g x = maybe x (Uniplate.descend g) $ f x-_transformDownRec :: Uniplate.Uniplate on => (on -> Maybe on) -> (on -> on)-_transformDownRec f = g where g x = maybe (Uniplate.descend g x) g $ f x---- | similar to List.lines, but treating the case of final newline character--- in such a manner that this function is the inverse of @intercalate "\n"@.-lines' :: String -> [String]-lines' s = case break (== '\n') s of- (s1, []) -> [s1]- (s1, [_]) -> [s1, ""]- (s1, (_:r)) -> s1 : lines' r--absurdExt :: HsExtension.NoExtCon -> a-absurdExt = HsExtension.noExtCon
− source/library/Language/Haskell/Brittany/Main.hs
@@ -1,483 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Language.Haskell.Brittany.Main where--import Control.Monad (zipWithM)-import qualified Control.Monad.Trans.Except as ExceptT-import Data.CZipWith-import qualified Data.Either-import qualified Data.List.Extra-import qualified Data.Monoid-import qualified Data.Semigroup as Semigroup-import qualified Data.Text as Text-import qualified Data.Text.IO as Text.IO-import qualified Data.Text.Lazy as TextL-import DataTreePrint-import GHC (GenLocated(L))-import qualified GHC.Driver.Session as GHC-import qualified GHC.LanguageExtensions.Type as GHC-import qualified GHC.OldList as List-import GHC.Utils.Outputable (Outputable(..), showSDocUnsafe)-import Language.Haskell.Brittany.Internal-import Language.Haskell.Brittany.Internal.Config-import Language.Haskell.Brittany.Internal.Config.Types-import Language.Haskell.Brittany.Internal.Obfuscation-import Language.Haskell.Brittany.Internal.Prelude-import Language.Haskell.Brittany.Internal.PreludeUtils-import Language.Haskell.Brittany.Internal.Types-import Language.Haskell.Brittany.Internal.Utils-import qualified Language.Haskell.GHC.ExactPrint as ExactPrint-import Paths_brittany-import qualified System.Directory as Directory-import qualified System.Environment as Environment-import qualified System.Exit-import qualified System.FilePath.Posix as FilePath-import qualified System.IO-import qualified Text.ParserCombinators.ReadP as ReadP-import qualified Text.ParserCombinators.ReadPrec as ReadPrec-import qualified Text.PrettyPrint as PP-import Text.Read (Read(..))-import UI.Butcher.Monadic----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 = do- progName <- Environment.getProgName- args <- Environment.getArgs- mainWith progName args--mainWith :: String -> [String] -> IO ()-mainWith progName args =- Environment.withProgName progName- . Environment.withArgs args- $ mainFromCmdParserWithHelpDesc mainCmdParser--helpDoc :: PP.Doc-helpDoc = PP.vcat $ List.intersperse- (PP.text "")- [ parDocW- [ "Reformats one or more haskell modules."- , "Currently affects only the module head (imports/exports), type"- , "signatures and function bindings;"- , "everything else is left unmodified."- , "Based on ghc-exactprint, thus (theoretically) supporting all"- , "that ghc does."- ]- , parDoc $ "Example invocations:"- , PP.hang (PP.text "") 2 $ PP.vcat- [ PP.text "brittany"- , PP.nest 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 output is syntactically valid and that no comments are removed."- , "Nonetheless, this is a young project, and there will always be bugs,"- , "and ensuring that the transformation never changes semantics of the"- , "transformed source is currently not possible."- , "Please do check the output and do not let brittany override your large"- , "codebase without having backups."- ]- , parDoc $ "There is NO WARRANTY, to the extent permitted by law."- , parDocW- [ "This program is free software released under the AGPLv3."- , "For details use the --license flag."- ]- , parDoc $ "See https://github.com/lspitzner/brittany"- , parDoc- $ "Please report bugs at"- ++ " https://github.com/lspitzner/brittany/issues"- ]--licenseDoc :: PP.Doc-licenseDoc = PP.vcat $ List.intersperse- (PP.text "")- [ parDoc $ "Copyright (C) 2016-2019 Lennart Spitzner"- , parDoc $ "Copyright (C) 2019 PRODA LTD"- , parDocW- [ "This program is free software: you can redistribute it and/or modify"- , "it under the terms of the GNU Affero General Public License,"- , "version 3, as published by the Free Software Foundation."- ]- , parDocW- [ "This program is distributed in the hope that it will be useful,"- , "but WITHOUT ANY WARRANTY; without even the implied warranty of"- , "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"- , "GNU Affero General Public License for more details."- ]- , parDocW- [ "You should have received a copy of the GNU Affero General Public"- , "License along with this program. If not, see"- , "<http://www.gnu.org/licenses/>."- ]- ]---mainCmdParser :: CommandDesc () -> CmdParser Identity (IO ()) ()-mainCmdParser helpDesc = do- addCmdSynopsis "haskell source pretty printer"- addCmdHelp $ helpDoc- -- addCmd "debugArgs" $ do- addHelpCommand helpDesc- addCmd "license" $ addCmdImpl $ print $ licenseDoc- -- addButcherDebugCommand- reorderStart- printHelp <- addSimpleBoolFlag "h" ["help"] mempty- printVersion <- addSimpleBoolFlag "" ["version"] mempty- printLicense <- addSimpleBoolFlag "" ["license"] mempty- noUserConfig <- addSimpleBoolFlag "" ["no-user-config"] mempty- configPaths <- addFlagStringParams ""- ["config-file"]- "PATH"- (flagHelpStr "path to config file") -- TODO: allow default on addFlagStringParam ?- cmdlineConfig <- cmdlineConfigParser- suppressOutput <- addSimpleBoolFlag- ""- ["suppress-output"]- (flagHelp $ parDoc- "suppress the regular output, i.e. the transformed haskell source"- )- _verbosity <- addSimpleCountFlag- "v"- ["verbose"]- (flagHelp $ parDoc "[currently without effect; TODO]")- checkMode <- addSimpleBoolFlag- "c"- ["check-mode"]- (flagHelp- (PP.vcat- [ PP.text "check for changes but do not write them out"- , PP.text "exits with code 0 if no changes necessary, 1 otherwise"- , PP.text "and print file path(s) of files that have changes to stdout"- ]- )- )- writeMode <- addFlagReadParam- ""- ["write-mode"]- "(display|inplace)"- ( flagHelp- (PP.vcat- [ PP.text "display: output for any input(s) goes to stdout"- , PP.text "inplace: override respective input file (without backup!)"- ]- )- Data.Monoid.<> flagDefault Display- )- inputParams <- addParamNoFlagStrings- "PATH"- (paramHelpStr "paths to input/inout haskell source files")- reorderStop- addCmdImpl $ void $ do- when printLicense $ do- print licenseDoc- System.Exit.exitSuccess- when printVersion $ do- do- putStrLn $ "brittany version " ++ showVersion version- putStrLn $ "Copyright (C) 2016-2019 Lennart Spitzner"- putStrLn $ "Copyright (C) 2019 PRODA LTD"- putStrLn $ "There is NO WARRANTY, to the extent permitted by law."- System.Exit.exitSuccess- when printHelp $ do- liftIO- $ putStrLn- $ PP.renderStyle PP.style { PP.ribbonsPerLine = 1.0 }- $ ppHelpShallow helpDesc- System.Exit.exitSuccess-- let inputPaths =- if null inputParams then [Nothing] else map Just inputParams- let outputPaths = case writeMode of- Display -> repeat Nothing- Inplace -> inputPaths-- configsToLoad <- liftIO $ if null configPaths- then- maybeToList <$> (Directory.getCurrentDirectory >>= findLocalConfigPath)- else pure configPaths-- config <-- runMaybeT- (if noUserConfig- then readConfigs cmdlineConfig configsToLoad- else readConfigsWithUserConfig cmdlineConfig configsToLoad- )- >>= \case- Nothing -> System.Exit.exitWith (System.Exit.ExitFailure 53)- Just x -> return x- when (config & _conf_debug & _dconf_dump_config & confUnpack)- $ trace (showConfigYaml config)- $ return ()-- results <- zipWithM (coreIO putStrErrLn config suppressOutput checkMode)- inputPaths- outputPaths-- if checkMode- then when (Changes `elem` (Data.Either.rights results))- $ System.Exit.exitWith (System.Exit.ExitFailure 1)- else case results of- xs | all Data.Either.isRight xs -> pure ()- [Left x] -> System.Exit.exitWith (System.Exit.ExitFailure x)- _ -> System.Exit.exitWith (System.Exit.ExitFailure 1)---data ChangeStatus = Changes | NoChanges- deriving (Eq)---- | The main IO parts for the default mode of operation, and after commandline--- and config stuff is processed.-coreIO- :: (String -> IO ()) -- ^ error output function. In parallel operation, you- -- may want serialize the different outputs and- -- consequently not directly print to stderr.- -> Config -- ^ global program config.- -> Bool -- ^ whether to supress output (to stdout). Purely IO flag, so- -- currently not part of program config.- -> Bool -- ^ whether we are (just) in check mode.- -> Maybe FilePath.FilePath -- ^ input filepath; stdin if Nothing.- -> Maybe FilePath.FilePath -- ^ output filepath; stdout if Nothing.- -> IO (Either Int ChangeStatus) -- ^ Either an errorNo, or the change status.-coreIO putErrorLnIO config suppressOutput checkMode 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- -- amount of slight differences: This module is a bit more verbose, and- -- it tries to use the full-blown `parseModule` function which supports- -- CPP (but requires the input to be a file..).- let cppMode = config & _conf_preprocessor & _ppconf_CPPMode & confUnpack- -- the flag will do the following: insert a marker string- -- ("-- BRITANY_INCLUDE_HACK ") right before any lines starting with- -- "#include" before processing (parsing) input; and remove that marker- -- string from the transformation output.- -- The flag is intentionally misspelled to prevent clashing with- -- inline-config stuff.- let hackAroundIncludes =- config & _conf_preprocessor & _ppconf_hackAroundIncludes & confUnpack- let exactprintOnly = viaGlobal || viaDebug- where- viaGlobal = config & _conf_roundtrip_exactprint_only & confUnpack- viaDebug =- config & _conf_debug & _dconf_roundtrip_exactprint_only & confUnpack-- let cppCheckFunc dynFlags = if GHC.xopt GHC.Cpp dynFlags- then case cppMode of- CPPModeAbort -> do- return $ Left "Encountered -XCPP. Aborting."- CPPModeWarn -> do- putErrorLnIO- $ "Warning: Encountered -XCPP."- ++ " Be warned that -XCPP is not supported and that"- ++ " brittany cannot check that its output is syntactically"- ++ " valid in its presence."- return $ Right True- CPPModeNowarn -> return $ Right True- else return $ Right False- (parseResult, originalContents) <- case inputPathM of- Nothing -> do- -- TODO: refactor this hack to not be mixed into parsing logic- let hackF s = if "#include" `isPrefixOf` s- then "-- BRITANY_INCLUDE_HACK " ++ s- else s- let hackTransform = if hackAroundIncludes && not exactprintOnly- then List.intercalate "\n" . fmap hackF . lines'- else id- inputString <- liftIO System.IO.getContents- parseRes <- liftIO $ parseModuleFromString ghcOptions- "stdin"- cppCheckFunc- (hackTransform inputString)- return (parseRes, Text.pack inputString)- Just p -> liftIO $ do- parseRes <- parseModule ghcOptions p cppCheckFunc- inputText <- Text.IO.readFile p- -- The above means we read the file twice, but the- -- GHC API does not really expose the source it- -- read. Should be in cache still anyways.- --- -- We do not use TextL.IO.readFile because lazy IO is evil.- -- (not identical -> read is not finished ->- -- handle still open -> write below crashes - evil.)- return (parseRes, inputText)- case parseResult of- Left left -> do- putErrorLn "parse error:"- putErrorLn left- ExceptT.throwE 60- Right (anns, parsedSource, hasCPP) -> do- (inlineConf, perItemConf) <-- case- extractCommentConfigs anns (getTopLevelDeclNameMap parsedSource)- of- Left (err, input) -> do- putErrorLn $ "Error: parse error in inline configuration:"- putErrorLn err- putErrorLn $ " in the string \"" ++ input ++ "\"."- ExceptT.throwE 61- Right c -> -- trace (showTree c) $- pure c- let moduleConf = cZipWith fromOptionIdentity config inlineConf- when (config & _conf_debug & _dconf_dump_ast_full & confUnpack) $ do- let val = printTreeWithCustom 100 (customLayouterF anns) parsedSource- trace ("---- ast ----\n" ++ show val) $ return ()- let disableFormatting =- moduleConf & _conf_disable_formatting & confUnpack- (errsWarns, outSText, hasChanges) <- do- if- | disableFormatting -> do- pure ([], originalContents, False)- | exactprintOnly -> do- let r = Text.pack $ ExactPrint.exactPrint parsedSource anns- pure ([], r, r /= originalContents)- | otherwise -> do- let omitCheck =- moduleConf- & _conf_errorHandling- .> _econf_omit_output_valid_check- .> confUnpack- (ews, outRaw) <- if hasCPP || omitCheck- then return- $ pPrintModule moduleConf perItemConf anns parsedSource- else liftIO $ pPrintModuleAndCheck moduleConf- perItemConf- anns- parsedSource- let hackF s = fromMaybe s $ TextL.stripPrefix- (TextL.pack "-- BRITANY_INCLUDE_HACK ")- s- let out = TextL.toStrict $ if hackAroundIncludes- then- TextL.intercalate (TextL.pack "\n")- $ hackF- <$> TextL.splitOn (TextL.pack "\n") outRaw- else outRaw- out' <- if moduleConf & _conf_obfuscate & confUnpack- then lift $ obfuscate out- else pure out- pure $ (ews, out', out' /= originalContents)- let customErrOrder ErrorInput{} = 4- customErrOrder LayoutWarning{} = -1 :: Int- customErrOrder ErrorOutputCheck{} = 1- customErrOrder ErrorUnusedComment{} = 2- customErrOrder ErrorUnknownNode{} = -2 :: Int- customErrOrder ErrorMacroConfig{} = 5- unless (null errsWarns) $ do- let groupedErrsWarns =- Data.List.Extra.groupOn customErrOrder- $ List.sortOn customErrOrder- $ errsWarns- groupedErrsWarns `forM_` \case- (ErrorOutputCheck{} : _) -> do- putErrorLn- $ "ERROR: brittany pretty printer"- ++ " returned syntactically invalid result."- (ErrorInput str : _) -> do- putErrorLn $ "ERROR: parse error: " ++ str- uns@(ErrorUnknownNode{} : _) -> do- putErrorLn- $ "WARNING: encountered unknown syntactical constructs:"- uns `forM_` \case- ErrorUnknownNode str ast@(L loc _) -> do- putErrorLn $ " " <> str <> " at " <> showSDocUnsafe (ppr loc)- when- ( config- & _conf_debug- & _dconf_dump_ast_unknown- & confUnpack- )- $ do- putErrorLn $ " " ++ show (astToDoc ast)- _ -> error "cannot happen (TM)"- putErrorLn- " -> falling back on exactprint for this element of the module"- warns@(LayoutWarning{} : _) -> do- putErrorLn $ "WARNINGS:"- warns `forM_` \case- LayoutWarning str -> putErrorLn str- _ -> error "cannot happen (TM)"- unused@(ErrorUnusedComment{} : _) -> do- putErrorLn- $ "Error: detected unprocessed comments."- ++ " The transformation output will most likely"- ++ " not contain some of the comments"- ++ " present in the input haskell source file."- putErrorLn $ "Affected are the following comments:"- unused `forM_` \case- ErrorUnusedComment str -> putErrorLn str- _ -> error "cannot happen (TM)"- (ErrorMacroConfig err input : _) -> do- putErrorLn $ "Error: parse error in inline configuration:"- putErrorLn err- putErrorLn $ " in the string \"" ++ input ++ "\"."- [] -> error "cannot happen"- -- TODO: don't output anything when there are errors unless user- -- adds some override?- let- hasErrors =- if config & _conf_errorHandling & _econf_Werror & confUnpack- then not $ null errsWarns- else 0 < maximum (-1 : fmap customErrOrder errsWarns)- outputOnErrs =- config- & _conf_errorHandling- & _econf_produceOutputOnErrors- & confUnpack- shouldOutput =- not suppressOutput- && not checkMode- && (not hasErrors || outputOnErrs)-- when shouldOutput- $ addTraceSep (_conf_debug config)- $ case outputPathM of- Nothing -> liftIO $ Text.IO.putStr $ outSText- Just p -> liftIO $ do- let isIdentical = case inputPathM of- Nothing -> False- Just _ -> not hasChanges- unless isIdentical $ Text.IO.writeFile p $ outSText-- when (checkMode && hasChanges) $ case inputPathM of- Nothing -> pure ()- Just p -> liftIO $ putStrLn $ "formatting would modify: " ++ p-- when hasErrors $ ExceptT.throwE 70- return (if hasChanges then Changes else NoChanges)- where- addTraceSep conf =- if or- [ confUnpack $ _dconf_dump_annotations conf- , confUnpack $ _dconf_dump_ast_unknown conf- , confUnpack $ _dconf_dump_ast_full conf- , confUnpack $ _dconf_dump_bridoc_raw conf- , confUnpack $ _dconf_dump_bridoc_simpl_alt conf- , confUnpack $ _dconf_dump_bridoc_simpl_floating conf- , confUnpack $ _dconf_dump_bridoc_simpl_columns conf- , confUnpack $ _dconf_dump_bridoc_simpl_indent conf- , confUnpack $ _dconf_dump_bridoc_final conf- ]- then trace "----"- else id
source/test-suite/Main.hs view
@@ -1,6 +1,6 @@ import qualified Control.Monad as Monad import qualified Data.List as List-import qualified Language.Haskell.Brittany.Main as Brittany+import qualified Brittany.Main as Brittany import qualified System.Directory as Directory import qualified System.FilePath as FilePath import qualified Test.Hspec as Hspec