Implement fixity-aware-ops feature

ghc92
Lennart Spitzner 2023-04-05 14:44:53 +00:00
parent 75d17b961c
commit 676695a609
24 changed files with 1854 additions and 865 deletions

View File

@ -129,6 +129,7 @@ library
Language.Haskell.Brittany.Internal.ToBriDoc.DataDecl
Language.Haskell.Brittany.Internal.ToBriDoc.Decl
Language.Haskell.Brittany.Internal.ToBriDoc.Expr
Language.Haskell.Brittany.Internal.ToBriDoc.OpTree
Language.Haskell.Brittany.Internal.ToBriDoc.IE
Language.Haskell.Brittany.Internal.ToBriDoc.Import
Language.Haskell.Brittany.Internal.ToBriDoc.Module
@ -137,6 +138,7 @@ library
Language.Haskell.Brittany.Internal.ToBriDoc.Type
Language.Haskell.Brittany.Internal.Components.BriDoc
Language.Haskell.Brittany.Internal.Components.Obfuscation
Language.Haskell.Brittany.Internal.Components.OpTree
Language.Haskell.Brittany.Internal.S1_Parsing
Language.Haskell.Brittany.Internal.S2_SplitModule
Language.Haskell.Brittany.Internal.S3_ToBriDocTools

View File

@ -0,0 +1,232 @@
#group expression/op-precedence
#test basic precedence-aware layouting
operatorExpr1 =
( foo1 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ foo2 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ foo3 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ foo4 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
+ foo5 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr5
+ foo6 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr6
+ foo7 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr7
+ foo8 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr8
+ foo9 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr9
)
#test nested different precedences
operatorExpr2 =
( foo1 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ foo2 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ foo3 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ foo4 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
== foo5 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr5
+ foo6 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr6
+ foo7 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr7
+ foo8 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr8
+ foo9 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr9
)
#test simple comment respecting
alternatives = -- a
( -- b
alternativeOne -- c
<|> alterantiveTwo -- d
<|> alternativeThree -- e
) -- f
#golden retaining comments while minimizing duplicated parens
#pending
-- brittany { lconfig_operatorParenthesisRefactorMode: PRMMinimize }
alternatives = -- a
x
+ ( -- b
( -- c
alternativeOne -- c
<|> alterantiveTwo -- d
<|> alternativeThree -- e
) -- f
) -- g
#expected
-- brittany { lconfig_operatorParenthesisRefactorMode: PRMMinimize }
alternatives = -- a
x
+ -- b
( -- c
alternativeOne -- c
<|> alterantiveTwo -- d
<|> alternativeThree -- e
) -- f -- g
#golden refactoring unnecessary parens basic example
-- brittany { lconfig_operatorParenthesisRefactorMode: PRMMinimize }
operatorExpr1 =
( (goo1 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1)
+ goo2 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ goo3 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ goo4 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
)
#expected
-- brittany { lconfig_operatorParenthesisRefactorMode: PRMMinimize }
operatorExpr1 =
( goo1 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ goo2 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ goo3 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ goo4 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
)
#test feature flag fixityAwareOps works
-- brittany { lconfig_fixityAwareOps: False }
operatorExpr1 =
( goo1
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ goo2
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ goo3
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ goo4
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
+ goo5
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr5
+ goo6
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr6
+ goo7
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr7
+ goo8
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr8
+ goo9
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr9
)
#golden op-app simple golden test
operatorExpr1 =
( goo1
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ goo2
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ goo3
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ goo4
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
+ goo5
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr5
+ goo6
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr6
+ goo7
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr7
+ goo8
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr8
+ goo9
* barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr9
)
#expected
operatorExpr1 =
( goo1 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr1
+ goo2 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2
+ goo3 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr3
+ goo4 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr4
+ goo5 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr5
+ goo6 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr6
+ goo7 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr7
+ goo8 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr8
+ goo9 * barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr9
)
#golden op-app mixed golden
doop =
some long invocation ==
loooooooooongman + (third nested expression)
- 4 && {- meow -} 5
- 6
> 7
`mod` loooooooooongwoman || ill just invoke a function with these args
|| foo && dooasdoiaosdoi ** oaisdoioasido <= asduiuaisduiasdu + asdahjsd + ahsjdhjhasd
#expected
doop =
some long invocation == loooooooooongman + (third nested expression) - 4
&& {- meow -} 5 - 6 > 7 `mod` loooooooooongwoman
|| ill just invoke a function with these args
|| foo
&& dooasdoiaosdoi ** oaisdoioasido
<= asduiuaisduiasdu + asdahjsd + ahsjdhjhasd
#golden op-app mixed golden with added parens
-- brittany { lconfig_fixityBasedAddAlignParens: True }
doop =
some long invocation ==
loooooooooongman + (third nested expression)
- 4 && {- meow -} 5
- 6
> 7
`mod` loooooooooongwoman || ill just invoke a function with these args
|| foo && dooasdoiaosdoi ** oaisdoioasido <= asduiuaisduiasdu + asdahjsd + ahsjdhjhasd
#expected
-- brittany { lconfig_fixityBasedAddAlignParens: True }
doop =
( some long invocation == loooooooooongman + (third nested expression) - 4
&& {- meow -} 5 - 6 > 7 `mod` loooooooooongwoman
|| ill just invoke a function with these args
|| ( foo
&& dooasdoiaosdoi ** oaisdoioasido
<= asduiuaisduiasdu + asdahjsd + ahsjdhjhasd
)
)
#golden multiline mixed op expression 1
-- brittany { lconfig_fixityBasedAddAlignParens: True }
meow =
[docSeparator, docForceSL od1, docSeparator, docForceSL ed1]
++ join
[ [docSeparator, docForceSingleline od, docSeparator, docForceSingleline ed]
| (od, ed) <- ems
]
++ [docSeparator, docForceSingleline odN, docSeparator, lastWrap edN]
#expected
-- brittany { lconfig_fixityBasedAddAlignParens: True }
meow =
( [docSeparator, docForceSL od1, docSeparator, docForceSL ed1]
++ join
[ [ docSeparator
, docForceSingleline od
, docSeparator
, docForceSingleline ed
]
| (od, ed) <- ems
]
++ [docSeparator, docForceSingleline odN, docSeparator, lastWrap edN]
)
#golden multiline mixed op expression 2
-- brittany { lconfig_fixityBasedAddAlignParens: True }
meow =
[docSeparator, docForceSingleline od1, docSeparator, docForceSingleline ed1, something]
++ join
[ [docSeparator, docForceSingleline od, docSeparator, docForceSingleline ed]
| (od, ed) <- ems
]
++ [docSeparator, docForceSingleline odN, docSeparator, lastWrap edN, something]
#expected
-- brittany { lconfig_fixityBasedAddAlignParens: True }
meow =
( [ docSeparator
, docForceSingleline od1
, docSeparator
, docForceSingleline ed1
, something
]
++ join
[ [ docSeparator
, docForceSingleline od
, docSeparator
, docForceSingleline ed
]
| (od, ed) <- ems
]
++ [ docSeparator
, docForceSingleline odN
, docSeparator
, lastWrap edN
, something
]
)

View File

@ -161,10 +161,8 @@ readMergePersConfig path shouldCreate conf = do
fileConf <- case Data.Yaml.decodeEither contents of
Left e -> do
liftIO
$ putStrErrLn
$ "error reading in brittany config from "
++ path
++ ":"
$ putStrErrLn
$ "error reading in brittany config from " ++ path ++ ":"
liftIO $ putStrErrLn e
mzero
Right x -> return x

View File

@ -1032,7 +1032,7 @@ func
#test some indentation thingy
func =
(lkjadljasldjalskdjaldjalsdjkalsdjlaksdjlasjdlajsaldskj
( lkjadljasldjalskdjaldjalsdjkalsdjlaksdjlasjdlajsaldskj
$ abc
$ def
$ ghi
@ -1100,9 +1100,7 @@ readMergePersConfig path shouldCreate conf = do
Left e -> do
liftIO
$ putStrErrLn
$ "error reading in brittany config from "
++ path
++ ":"
$ "error reading in brittany config from " ++ path ++ ":"
liftIO $ putStrErrLn e
mzero
Right x -> return x

View File

@ -55,9 +55,9 @@ parsePrintModule traceFunc configWithDebugs inputText = runExceptT $ do
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 @CPPMode
let cppMode = config_pp & _ppconf_CPPMode & confUnpack
let hackAroundIncludes =
config_pp & _ppconf_hackAroundIncludes & confUnpack @Bool
config_pp & _ppconf_hackAroundIncludes & confUnpack
(parsedSource, hasCPP) <- do
let hackF s = if "#include" `isPrefixOf` s
then "-- BRITANY_INCLUDE_HACK " ++ s
@ -84,7 +84,7 @@ parsePrintModule traceFunc configWithDebugs inputText = runExceptT $ do
$ extractCommentConfigs (useTraceFunc traceFunc) parsedSource
let moduleConfig = cZipWith fromOptionIdentity config inlineConf
let disableFormatting =
moduleConfig & _conf_disable_formatting & confUnpack @Bool
moduleConfig & _conf_disable_formatting & confUnpack
if disableFormatting
then do
return inputText

View File

@ -87,11 +87,7 @@ data BriDocW (w :: IsWrapped)
-- the following constructors are only relevant for the alt transformation
-- and are removed afterwards. They should never occur in any (BriDocRec w)
-- after the alt transformation.
| BDForceMultiline (BriDocRec w)
| BDForceSingleline (BriDocRec w)
| BDNonBottomSpacing Bool (BriDocRec w)
| BDSetParSpacing (BriDocRec w)
| BDForceParSpacing (BriDocRec w)
| BDForceAlt ForceAlt (BriDocRec w)
-- pseudo-deprecated
| BDDebug String (BriDocRec w)
@ -102,8 +98,19 @@ type BriDoc = BriDocW 'Unwrapped
type BriDocWrapped = BriDocW 'Wrapped
type BriDocNumbered = (Int, BriDocWrapped)
data ForceAlt
= ForceMultiline
| ForceSingleline
| NonBottomSpacing Bool
| SetParSpacing
| ForceParSpacing
| ForceZeroAdd
deriving (Eq, Ord, Data.Data.Data, Show)
data BrIndent = BrIndentNone
| BrIndentRegular
| BrIndentRegularForce
| BrIndentSpecial Int
deriving (Eq, Ord, Data.Data.Data, Show)
@ -131,11 +138,7 @@ instance Uniplate.Uniplate BriDoc where
uniplate (BDEntryDelta dp bd ) = plate BDEntryDelta |- dp |* 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 (BDForceAlt forceFlag bd) = plate BDForceAlt |- forceFlag |* bd
uniplate (BDDebug s bd ) = plate BDDebug |- s |* bd
-- this might not work. is not used anywhere either.
@ -161,11 +164,7 @@ briDocSeqSpine = \case
BDEntryDelta _dp 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
BDForceAlt _ bd -> briDocSeqSpine bd
BDDebug _s bd -> briDocSeqSpine bd
briDocForceSpine :: BriDoc -> BriDoc
@ -198,11 +197,7 @@ unwrapBriDocNumbered tpl = case snd tpl of
BDEntryDelta dp bd -> BDEntryDelta dp $ rec bd
BDLines lines -> BDLines $ rec <$> lines
BDEnsureIndent ind bd -> BDEnsureIndent ind $ rec bd
BDForceMultiline bd -> BDForceMultiline $ rec bd
BDForceSingleline bd -> BDForceSingleline $ rec bd
BDNonBottomSpacing b bd -> BDNonBottomSpacing b $ rec bd
BDSetParSpacing bd -> BDSetParSpacing $ rec bd
BDForceParSpacing bd -> BDForceParSpacing $ rec bd
BDForceAlt forceFlag bd -> BDForceAlt forceFlag $ rec bd
BDDebug s bd -> BDDebug (s ++ "@" ++ show (fst tpl)) $ rec bd
where rec = unwrapBriDocNumbered

View File

@ -0,0 +1,289 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ViewPatterns #-}
module Language.Haskell.Brittany.Internal.Components.OpTree where
import Language.Haskell.Brittany.Internal.Prelude
import qualified Data.Text as Text
import GHC ( RealSrcLoc )
import GHC.Types.Fixity ( Fixity(Fixity)
, FixityDirection
( InfixL
, InfixN
, InfixR
)
)
import GHC.Types.SourceText ( SourceText(NoSourceText) )
import qualified Safe
import qualified Data.Char
import Language.Haskell.Brittany.Internal.Components.BriDoc
import Language.Haskell.Brittany.Internal.Utils
import Language.Haskell.Brittany.Internal.Types
data OpTree
= OpUnknown Bool -- Z paren?
(Maybe RealSrcLoc) -- paren open loc
(Maybe RealSrcLoc) -- paren close loc
OpTree -- left operand
[(BriDocNumbered, BriDocNumbered)] -- list of (next operand, symbol)
| OpKnown Bool -- with paren?
(Maybe RealSrcLoc) -- paren open loc
(Maybe RealSrcLoc) -- paren close loc
Fixity -- only Just after (successful!) lookup phase
OpTree
[(BriDocNumbered, OpTree)]
| OpLeaf BriDocNumbered
displayOpTree :: OpTree -> String
displayOpTree = \case
OpUnknown p _ _ leftTree rs ->
( "(OpUnknown "
++ show p
++ " "
++ displayOpTree leftTree
++ " ["
++ intercalate
","
[ ("(" ++ showOp op ++ "," ++ show x ++ ")") | (op, (x, _)) <- rs ]
++ "]"
)
OpKnown p _ _ fixity tree ops ->
( "OpKnown "
++ show p
++ " "
++ showOutputable fixity
++ " ("
++ displayOpTree tree
++ ")"
++ join [ (showOp op ++ "(" ++ displayOpTree t ++ ")") | (op, t) <- ops ]
)
OpLeaf (x, _) -> show x
where
showOp :: BriDocNumbered -> String
showOp = \case
(_, BDFlushCommentsPrior _ x) -> showOp x
(_, BDQueueComments _ x ) -> showOp x
(_, BDLit x ) -> Text.unpack x
-- (_, BDFlushCommentsPrior _ (_, BDFlushCommentsPrior _ (_, x)))
-- | trace (show $ toConstr x) False -> "meow"
(i, _ ) -> show i
-- lookupFixities :: Monad m => OpTree -> m OpTree
-- lookupFixities = \case
-- OpNode par Nothing opDoc chldrn -> do
-- pure $ OpNode par (hardcodedFixity (Text.unpack opDoc)) opDoc chldrn
-- x@OpNode{} -> pure x
-- x@OpLeaf{} -> pure x
data ReformatParenMode
= ReformatParenModeKeep -- don't modify parens at all
| ReformatParenModeClean -- remove unnecessary parens
| ReformatParenModeAll -- add superfluous parens everywhere
-- [(Bool, Fixity, Text, [OpTree])]
-- a == b + c || d * e /= f
-- _ a
-- == a, _ b
-- == a, + b, _ c
-- == a, + b c
-- == a (+ b c)
-- || (== a (+ b c)), _ d
-- || (== a (+ b c)), * d, _ e
-- || (== a (+ b c)), * d e
-- || (== a (+ b c)), /= (* d e), _ f
-- || (== a (+ b c)), /= (* d e) f
-- || (== a (+ b c)) (/= (* d e) f)
data StackElem = StackElem Fixity [(OpTree, BriDocNumbered)]
type Stack = [StackElem]
balanceOpTree :: Bool -> OpTree -> ([BrittanyError], OpTree)
balanceOpTree allowUnqualify = \case
x@OpLeaf{} -> ([], x)
x@OpKnown{} -> ([], x)
x@(OpUnknown paren locO locC left rest) ->
let (warns, balancedLeft) = balanceOpTree allowUnqualify left
in case go [] rest balancedLeft of
Right (leaf@OpLeaf{}) -> (warns, leaf)
Right (OpKnown _paren _ _ fixity c cs) ->
(warns, OpKnown paren locO locC fixity c cs)
Right t -> (warns, t)
Left moreWarns ->
( warns
++ [ LayoutWarning ("Fixity of operator not known: " ++ w)
| w <- moreWarns
]
, x
)
where
-- singleton :: BriDocNumbered -> StackElem
-- singleton x = StackElem (Fixity NoSourceText 99 InfixN) (OpLeaf x) []
go
:: Stack
-> [(BriDocNumbered, BriDocNumbered)]
-> OpTree
-> Either [String] OpTree
go [] [] _ = Left []
go [StackElem fxty cs] [] c =
let (e1, eops) = shiftOps cs c in Right (known fxty e1 eops)
go (StackElem fxty cs : StackElem fixity cs2 : rest) [] c =
-- go (StackElem fixity (OpKnown False fxty (reverse cs) : cs2) : rest) []
let (e1, eops) = shiftOps cs c
in go (StackElem fixity cs2 : rest) [] (known fxty e1 eops)
go stack input@((opDoc, val) : inputR) c = case stack of
[] -> do
fxty <- docFixity opDoc
go [StackElem fxty [(c, opDoc)]] inputR (OpLeaf val)
(StackElem fixityS cs : stackR) -> do
let Fixity _ precS dirS = fixityS
fxty@(Fixity _ prec dir) <- docFixity opDoc
case compare prec precS of
GT -> go (StackElem fxty [(c, opDoc)] : stack) inputR (OpLeaf val)
LT -> do
let (e1, eops) = shiftOps cs c
go stackR input (known fixityS e1 eops)
EQ -> case (dir, dirS) of
(InfixR, InfixR) ->
go (StackElem fixityS ((c, opDoc) : cs) : stackR)
inputR
(OpLeaf val)
(InfixL, InfixL) ->
go (StackElem fixityS ((c, opDoc) : cs) : stackR)
inputR
(OpLeaf val)
_ -> Left []
docFixity :: BriDocNumbered -> Either [String] Fixity
docFixity (_, x) = case x of
BDLit (Text.unpack -> s) -> case hardcodedFixity allowUnqualify s of
Just f -> Right f
Nothing -> Left [s]
BDFlushCommentsPrior _ d -> docFixity d
BDQueueComments _ d -> docFixity d
_ -> Left []
shiftOps
:: [(OpTree, BriDocNumbered)]
-> OpTree
-> (OpTree, [(BriDocNumbered, OpTree)])
shiftOps ops final = case reverse ops of
[] -> (final, [])
((e1, o1) : rest) ->
( e1
, let (finalOp, list) =
mapAccumL (\op (nextE, nextOp) -> (nextOp, (op, nextE))) o1 rest
in list ++ [(finalOp, final)]
)
known = OpKnown False Nothing Nothing
addAllParens :: Bool -> OpTree -> OpTree
addAllParens topLevelParen = \case
x@OpLeaf{} -> x
x@OpUnknown{} -> x
OpKnown _paren locO locC fixity c cs ->
OpKnown topLevelParen
locO
locC
fixity
(addAllParens True c)
[ (op, addAllParens True tree) | (op, tree) <- cs ]
remSuperfluousParens :: Int -> OpTree -> OpTree
remSuperfluousParens outerFixity = \case
x@OpLeaf{} -> x
x@OpUnknown{} -> x
OpKnown paren locO locC fixity c cs ->
OpKnown
(paren && outerFixity > fixLevel fixity)
locO
locC
fixity
(remSuperfluousParens (fixLevel fixity) c)
[ (op, remSuperfluousParens (fixLevel fixity) tree) | (op, tree) <- cs ]
where fixLevel (Fixity _ i _) = i
hardcodedFixity :: Bool -> String -> Maybe Fixity
hardcodedFixity allowUnqualify = \case
"." -> Just $ Fixity NoSourceText 9 InfixR
"!!" -> Just $ Fixity NoSourceText 9 InfixL
"**" -> Just $ Fixity NoSourceText 8 InfixR
"^" -> Just $ Fixity NoSourceText 8 InfixR
"^^" -> Just $ Fixity NoSourceText 8 InfixR
"*" -> Just $ Fixity NoSourceText 7 InfixL
"/" -> Just $ Fixity NoSourceText 7 InfixL
"`quot`" -> Just $ Fixity NoSourceText 7 InfixL
"`rem`" -> Just $ Fixity NoSourceText 7 InfixL
"`div`" -> Just $ Fixity NoSourceText 7 InfixL
"`mod`" -> Just $ Fixity NoSourceText 7 InfixL
"+" -> Just $ Fixity NoSourceText 6 InfixL
"-" -> Just $ Fixity NoSourceText 6 InfixL
":" -> Just $ Fixity NoSourceText 5 InfixR
"==" -> Just $ Fixity NoSourceText 4 InfixN
"/=" -> Just $ Fixity NoSourceText 4 InfixN
"<" -> Just $ Fixity NoSourceText 4 InfixN
"<=" -> Just $ Fixity NoSourceText 4 InfixN
">" -> Just $ Fixity NoSourceText 4 InfixN
">=" -> Just $ Fixity NoSourceText 4 InfixN
"&&" -> Just $ Fixity NoSourceText 3 InfixR
"||" -> Just $ Fixity NoSourceText 2 InfixR
">>=" -> Just $ Fixity NoSourceText 1 InfixL
">>" -> Just $ Fixity NoSourceText 1 InfixL
"=<<" -> Just $ Fixity NoSourceText 1 InfixR
"$" -> Just $ Fixity NoSourceText 0 InfixR
"`seq`" -> Just $ Fixity NoSourceText 0 InfixR
"$!" -> Just $ Fixity NoSourceText 0 InfixR
"!" -> Just $ Fixity NoSourceText 9 InfixL
"//" -> Just $ Fixity NoSourceText 9 InfixL
"<>" -> Just $ Fixity NoSourceText 6 InfixR
"<$" -> Just $ Fixity NoSourceText 4 InfixL
"<$>" -> Just $ Fixity NoSourceText 4 InfixL
"<&>" -> Just $ Fixity NoSourceText 1 InfixL
"&" -> Just $ Fixity NoSourceText 1 InfixL
"<*>" -> Just $ Fixity NoSourceText 4 InfixL
"<**>" -> Just $ Fixity NoSourceText 4 InfixL
"*>" -> Just $ Fixity NoSourceText 4 InfixL
"<*" -> Just $ Fixity NoSourceText 4 InfixL
"`elem`" -> Just $ Fixity NoSourceText 4 InfixN
"`notElem`" -> Just $ Fixity NoSourceText 4 InfixN
"++" -> Just $ Fixity NoSourceText 5 InfixR
"%" -> Just $ Fixity NoSourceText 7 InfixL
"<|>" -> Just $ Fixity NoSourceText 3 InfixL
".&." -> Just $ Fixity NoSourceText 7 InfixL
".|." -> Just $ Fixity NoSourceText 5 InfixL
"`xor`" -> Just $ Fixity NoSourceText 6 InfixL
"`shift`" -> Just $ Fixity NoSourceText 8 InfixL
"`rotate`" -> Just $ Fixity NoSourceText 8 InfixL
"`shiftL`" -> Just $ Fixity NoSourceText 8 InfixL
"`shiftR`" -> Just $ Fixity NoSourceText 8 InfixL
"`rotateL`" -> Just $ Fixity NoSourceText 8 InfixL
"`rotateR`" -> Just $ Fixity NoSourceText 8 InfixL
".^." -> Just $ Fixity NoSourceText 6 InfixL
".>>." -> Just $ Fixity NoSourceText 8 InfixL
".<<." -> Just $ Fixity NoSourceText 8 InfixL
"!>>." -> Just $ Fixity NoSourceText 8 InfixL
"!<<." -> Just $ Fixity NoSourceText 8 InfixL
">=>" -> Just $ Fixity NoSourceText 1 InfixR
"<=<" -> Just $ Fixity NoSourceText 1 InfixR
":~:" -> Just $ Fixity NoSourceText 4 InfixN
":~~:" -> Just $ Fixity NoSourceText 4 InfixN
-- non-base from random sources.
"<|" -> Just $ Fixity NoSourceText 5 InfixR
"|>" -> Just $ Fixity NoSourceText 5 InfixL
"><" -> Just $ Fixity NoSourceText 5 InfixR
"$+$" -> Just $ Fixity NoSourceText 5 InfixL
"\\\\" -> Just $ Fixity NoSourceText 5 InfixN
".>" -> Just $ Fixity NoSourceText 9 InfixL
":?" -> Just $ Fixity NoSourceText 7 InfixN
":-" -> Just $ Fixity NoSourceText 9 InfixR
str -> case (Safe.headMay str, Safe.lastMay str) of
(Just '\'', _) -> hardcodedFixity False (drop 1 str)
(Just '`', Just '`') -> Just $ Fixity NoSourceText 9 InfixL
(Just c, _) | Data.Char.isAlpha c && allowUnqualify -> hardcodedFixity False
$ dropWhile (\x -> (Data.Char.isAlpha x || x == '.')) str
_ -> Nothing

View File

@ -59,6 +59,11 @@ staticDefaultConfig = Config
, _lconfig_allowHangingQuasiQuotes = coerce True
, _lconfig_experimentalSemicolonNewlines = coerce False
-- , _lconfig_allowSinglelineRecord = coerce False
, _lconfig_fixityAwareOps = coerce True
, _lconfig_fixityAwareTypeOps = coerce False
, _lconfig_fixityBasedAddAlignParens = coerce False
, _lconfig_operatorParenthesisRefactorMode = coerce PRMKeep
, _lconfig_operatorAllowUnqualify = coerce True
}
, _conf_errorHandling = ErrorHandlingConfig
{ _econf_produceOutputOnErrors = coerce False
@ -159,6 +164,11 @@ cmdlineConfigParser = do
, _lconfig_allowHangingQuasiQuotes = mempty
, _lconfig_experimentalSemicolonNewlines = mempty
-- , _lconfig_allowSinglelineRecord = mempty
, _lconfig_fixityAwareOps = mempty
, _lconfig_fixityAwareTypeOps = mempty
, _lconfig_fixityBasedAddAlignParens = mempty
, _lconfig_operatorParenthesisRefactorMode = mempty
, _lconfig_operatorAllowUnqualify = mempty
}
, _conf_errorHandling = ErrorHandlingConfig
{ _econf_produceOutputOnErrors = wrapLast $ falseToNothing outputOnErrors

View File

@ -4,6 +4,7 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Language.Haskell.Brittany.Internal.Config.Types where
@ -16,9 +17,14 @@ import Language.Haskell.Brittany.Internal.Prelude
confUnpack :: forall b a . Coercible a b => Identity a -> b
confUnpack
:: forall a . Coercible a (ConfUnpacked a) => Identity a -> ConfUnpacked a
confUnpack (Identity x) = coerce x
type family ConfUnpacked a where
ConfUnpacked (Last a) = a
ConfUnpacked a = a
data CDebugConfig f = DebugConfig
{ _dconf_dump_config :: f (Semigroup.Last Bool)
, _dconf_dump_annotations :: f (Semigroup.Last Bool)
@ -137,6 +143,38 @@ data CLayoutConfig f = LayoutConfig
-- -- > { x :: Double
-- -- > , y :: Double
-- -- > }
, _lconfig_fixityAwareOps :: f (Last Bool)
-- enables fixity-based layouting, e.g.
-- > foo =
-- > ( aaaaaaaaaaaaaa * bbbbbbbbbbbbbbbbbbb
-- > + ccccccccccc * ddddddddddddddddd
-- > )
-- Note how the layout puts multiplication in a line because brittany
-- is aware that there are implicit parens around the lines.
-- The fixities are currently taken from a static (baked-in) lookup table
-- that was derived from the base library.
-- Operator applications with unknown fixities will be treated as if
-- this feature was turned off. In other words, such applications will be
-- treated as if all operators had the same fixity without changing
-- parentheses in any way.
, _lconfig_fixityAwareTypeOps :: f (Last Bool)
-- Same as above, but for type-level operators. Not yet implemented, but
-- reserved for future use.
, _lconfig_fixityBasedAddAlignParens :: f (Last Bool)
-- Layouts multiple-line operator applications with parentheses if
-- permitted by layout. Note how the arguments are properly aligned:
-- > ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-- > <> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-- > <> ccccccccccccccccccccccc
-- > )
-- while this has operands at different indentations:
-- > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-- > <> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-- > <> ccccccccccccccccccccccc
-- But this _does_ waste a line for the closing paren, so no clear winner.
-- Only has an effect in combination with fixityAware(Type)Ops.
, _lconfig_operatorParenthesisRefactorMode :: f (Last ParenRefactorMode)
, _lconfig_operatorAllowUnqualify :: f (Last Bool)
}
deriving Generic
@ -255,3 +293,13 @@ data ExactPrintFallbackMode
-- THIS MAY THEORETICALLY CHANGE SEMANTICS OF
-- A PROGRAM BY TRANSFORMING IT.
deriving (Show, Generic, Data)
data ParenRefactorMode
= PRMKeep
-- ^ neither add parens (beyond _lconfig_fixityBasedAddAlignParens)
-- Unknown operators will force Keep behaviour.
| PRMMinimize
-- ^ remove superfluous parens
| PRMMaximize
-- ^ insert parens around all operator applications.
deriving (Show, Generic, Data)

View File

@ -75,6 +75,13 @@ instance ToJSON ExactPrintFallbackMode where
toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany
toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany
instance FromJSON ParenRefactorMode where
parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany
instance ToJSON ParenRefactorMode where
toJSON = Aeson.genericToJSON aesonDecodeOptionsBrittany
toEncoding = Aeson.genericToEncoding aesonDecodeOptionsBrittany
instance FromJSON (CLayoutConfig Maybe) where
parseJSON = Aeson.genericParseJSON aesonDecodeOptionsBrittany

View File

@ -31,6 +31,7 @@ import GHC.Types.Name.Occurrence ( occNameString )
import GHC.Types.Name.Reader ( RdrName(..) )
import qualified GHC.Types.SrcLoc as GHC
import GHC.Utils.Outputable ( Outputable )
import Data.Coerce ( Coercible )
import Language.Haskell.Brittany.Internal.Components.BriDoc
import Language.Haskell.Brittany.Internal.Config.Types
import Language.Haskell.Brittany.Internal.Prelude
@ -137,6 +138,7 @@ instance PrintRdrNameWithAnns GHC.SrcSpanAnnN where
EpAnn _ (GHC.NameAnn GHC.NameParensHash _ _ _ _) _ -> f "(#" name "#)"
EpAnn _ (GHC.NameAnn GHC.NameBackquotes _ _ _ _) _ -> f "`" name "`"
EpAnn _ (GHC.NameAnn GHC.NameSquare _ _ _ _) _ -> f "[" name "]"
EpAnn _ (GHC.NameAnnQuote _ _ _) _ -> f "'" name ""
-- TODO92 There are way more possible constructors here
-- see https://hackage.haskell.org/package/ghc-9.2.5/docs/GHC-Parser-Annotation.html#t:NameAnn
EpAnn _ _ _ -> rdrNameToText name
@ -397,16 +399,19 @@ docSeparator :: ToBriDocM BriDocNumbered
docSeparator = allocateNode BDSeparator
docNonBottomSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docNonBottomSpacing bdm = allocateNode . BDNonBottomSpacing False =<< bdm
docNonBottomSpacing bdm = allocateNode . BDForceAlt (NonBottomSpacing False) =<< bdm
docNonBottomSpacingS :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docNonBottomSpacingS bdm = allocateNode . BDNonBottomSpacing True =<< bdm
docNonBottomSpacingS bdm = allocateNode . BDForceAlt (NonBottomSpacing True) =<< bdm
docSetParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docSetParSpacing bdm = allocateNode . BDSetParSpacing =<< bdm
docSetParSpacing bdm = allocateNode . BDForceAlt SetParSpacing =<< bdm
docForceParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docForceParSpacing bdm = allocateNode . BDForceParSpacing =<< bdm
docForceParSpacing bdm = allocateNode . BDForceAlt ForceParSpacing =<< bdm
docForceZeroAdd :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docForceZeroAdd bdm = allocateNode . BDForceAlt ForceZeroAdd =<< bdm
docDebug :: String -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docDebug s bdm = allocateNode . BDDebug s =<< bdm
@ -459,10 +464,10 @@ docPar lineM indentedM = do
allocateNode $ BDPar BrIndentNone line indented
docForceSingleline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docForceSingleline bdm = allocateNode . BDForceSingleline =<< bdm
docForceSingleline bdm = allocateNode . BDForceAlt ForceSingleline =<< bdm
docForceMultiline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
docForceMultiline bdm = allocateNode . BDForceMultiline =<< bdm
docForceMultiline bdm = allocateNode . BDForceAlt ForceMultiline =<< bdm
docEnsureIndent
:: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
@ -774,3 +779,9 @@ docHandleListElemCommsProperPost layouter es = case es of
epaLocationRealSrcSpanStart :: GHC.EpaLocation -> GHC.RealSrcLoc
epaLocationRealSrcSpanStart = GHC.realSrcSpanStart . GHC.epaLocationRealSrcSpan
askLayoutConf
:: Coercible a (ConfUnpacked a)
=> (CLayoutConfig Identity -> Identity a)
-> ToBriDocM (ConfUnpacked a)
askLayoutConf f = mAsk <&> _conf_layout .> f .> confUnpack

View File

@ -142,9 +142,10 @@ layoutBriDocM = \case
layoutAddSepSpace
BDAddBaseY indent bd -> do
let indentF = case indent of
BrIndentNone -> id
BrIndentRegular -> layoutWithAddBaseCol
BrIndentSpecial i -> layoutWithAddBaseColN i
BrIndentNone -> id
BrIndentRegular -> layoutWithAddBaseCol
BrIndentRegularForce -> layoutWithAddBaseCol
BrIndentSpecial i -> layoutWithAddBaseColN i
indentF $ layoutBriDocM bd
BDBaseYPushCur bd -> do
layoutBaseYPushCur
@ -158,18 +159,20 @@ layoutBriDocM = \case
layoutIndentLevelPop
BDEnsureIndent indent bd -> do
let indentF = case indent of
BrIndentNone -> id
BrIndentRegular -> layoutWithAddBaseCol
BrIndentSpecial i -> layoutWithAddBaseColN i
BrIndentNone -> id
BrIndentRegular -> layoutWithAddBaseCol
BrIndentRegularForce -> 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
BrIndentNone -> id
BrIndentRegular -> layoutWithAddBaseCol
BrIndentRegularForce -> layoutWithAddBaseCol
BrIndentSpecial i -> layoutWithAddBaseColN i
indentF $ do
layoutWriteNewline
layoutBriDocM indented
@ -183,8 +186,7 @@ layoutBriDocM = \case
BDLines lines -> alignColsLines layoutBriDocM lines
BDAlt [] -> error "empty BDAlt"
BDAlt (alt : _) -> layoutBriDocM alt
BDForceMultiline bd -> layoutBriDocM bd
BDForceSingleline bd -> layoutBriDocM bd
BDForceAlt _ bd -> layoutBriDocM bd
BDForwardLineMode bd -> layoutBriDocM bd
BDExternal shouldAddComment t -> do
let tlines = Text.lines $ t <> Text.pack "\n"
@ -276,17 +278,23 @@ layoutBriDocM = \case
printComments comms
mModify (\s -> s + CommentCounter (length comms))
do
marker <- mGet <&> _lstate_markerForDelta
state <- mGet
mModify $ \s -> s { _lstate_markerForDelta = Nothing }
case marker of
Nothing -> pure ()
case _lstate_markerForDelta state of
Just m -> do
let p1 = (srcLocLine m, srcLocCol m)
let p2 = (srcLocLine loc, srcLocCol loc)
let newlinePlanned = case _lstate_plannedSpace state of
PlannedNone -> False
PlannedSameline{} -> False
PlannedNewline{} -> True
PlannedDelta{} -> True
-- traceShow (ExactPrint.pos2delta p1 p2) $ pure ()
case ExactPrint.pos2delta p1 p2 of
SameLine{} -> pure ()
DifferentLine n _ -> layoutWriteNewlines n
DifferentLine n _ | newlinePlanned -> layoutWriteNewlines n
| otherwise -> pure ()
_ -> pure ()
layoutBriDocM bd
BDFlushCommentsPost loc shouldMark bd -> do
layoutBriDocM bd
@ -297,20 +305,10 @@ layoutBriDocM = \case
comms <- takeBefore loc
mModify (\s -> s + CommentCounter (length comms))
printComments comms
BDNonBottomSpacing _ bd -> layoutBriDocM bd
BDSetParSpacing bd -> layoutBriDocM bd
BDForceParSpacing bd -> layoutBriDocM bd
BDDebug s bd -> do
mTell $ TextL.Builder.fromText $ Text.pack $ "{-" ++ s ++ "-}"
layoutBriDocM bd
mergeOn :: Ord b => (a -> b) -> [a] -> [a] -> [a]
mergeOn _f xs [] = xs
mergeOn _f [] ys = ys
mergeOn f xs@(x:xr) ys@(y:yr)
| f x <= f y = x : mergeOn f xr ys
| otherwise = y : mergeOn f xs yr
takeBefore
:: MonadMultiState [LEpaComment] m => RealSrcLoc -> m [LEpaComment]
takeBefore loc = do

View File

@ -60,7 +60,7 @@ processModule
-> IO ([BrittanyError], TextL.Text)
processModule traceFunc conf inlineConf parsedModule = do
let shouldReformatHead =
conf & _conf_layout & _lconfig_reformatModulePreamble & confUnpack @Bool
conf & _conf_layout & _lconfig_reformatModulePreamble & confUnpack
let
wrapNonDeclToBriDoc = MultiRWSS.withMultiReader conf
. MultiRWSS.withMultiState_ (CommentCounter 0)
@ -143,18 +143,18 @@ processModule traceFunc conf inlineConf parsedModule = do
-- trace ("---- DEBUGMESSAGES ---- ")
-- . foldr (seq . join trace) id debugStrings
debugStrings `forM_` \s -> useTraceFunc traceFunc s
moduleElementsStream
(\el rest -> do
case el of
MEExactModuleHead{} -> useTraceFunc traceFunc "MEExactModuleHead"
MEPrettyModuleHead{} -> useTraceFunc traceFunc "MEPrettyModuleHead"
MEImportDecl{} -> useTraceFunc traceFunc "MEImportDecl"
MEDecl{} -> useTraceFunc traceFunc "MEDecl"
MEComment{} -> useTraceFunc traceFunc "MEComment"
MEWhitespace dp -> useTraceFunc traceFunc ("MEWhitespace " ++ show dp)
rest
)
(\_ -> pure ())
-- moduleElementsStream
-- (\el rest -> do
-- case el of
-- MEExactModuleHead{} -> useTraceFunc traceFunc "MEExactModuleHead"
-- MEPrettyModuleHead{} -> useTraceFunc traceFunc "MEPrettyModuleHead"
-- MEImportDecl{} -> useTraceFunc traceFunc "MEImportDecl"
-- MEDecl{} -> useTraceFunc traceFunc "MEDecl"
-- MEComment{} -> useTraceFunc traceFunc "MEComment"
-- MEWhitespace dp -> useTraceFunc traceFunc ("MEWhitespace " ++ show dp)
-- rest
-- )
-- (\_ -> pure ())
pure (errs, TextL.Builder.toLazyText out)
commentToDoc :: (Int, EpaCommentTok) -> ToBriDocM BriDocNumbered
@ -213,7 +213,7 @@ getDeclConfig config inlineConf decl = cZipWith fromOptionIdentity config
ppToplevelDecl :: LHsDecl GhcPs -> [(Int, EpaCommentTok)] -> PPMLocal ()
ppToplevelDecl decl immediateAfterComms = do
exactprintOnly <- mAsk <&> \declConfig ->
declConfig & _conf_roundtrip_exactprint_only & confUnpack @Bool
declConfig & _conf_roundtrip_exactprint_only & confUnpack
bd <- fmap fst $ if exactprintOnly
then briDocMToPPM
$ docSeq (briDocByExact decl : map commentToDoc immediateAfterComms)

View File

@ -0,0 +1,311 @@
{-# LANGUAGE NoImplicitPrelude #-}
module Language.Haskell.Brittany.Internal.ToBriDoc.OpTree where
import qualified Data.Text as Text
import GHC ( GenLocated(L) )
import GHC.Hs
import GHC.Types.Fixity ( Fixity(Fixity) )
import qualified GHC.Types.SrcLoc as GHC
import Language.Haskell.Brittany.Internal.Components.BriDoc
import Language.Haskell.Brittany.Internal.Components.OpTree
import Language.Haskell.Brittany.Internal.Config.Types
import Language.Haskell.Brittany.Internal.Prelude
import Language.Haskell.Brittany.Internal.S3_ToBriDocTools
import Language.Haskell.Brittany.Internal.Types
import Language.Haskell.Brittany.Internal.Utils
import {-# SOURCE #-} Language.Haskell.Brittany.Internal.ToBriDoc.Expr
import {-# SOURCE #-} Language.Haskell.Brittany.Internal.ToBriDoc.Type
gatherOpTreeE
:: Bool
-> Bool
-> (ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered)
-> Maybe GHC.RealSrcLoc
-> Maybe GHC.RealSrcLoc
-> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]
-> LHsExpr GhcPs
-> ToBriDocM (OpTree, Bool)
gatherOpTreeE hasParen hasComms commWrap locOpen locClose opExprList = \case
(L _ (OpApp epAnn l1 op1 r1)) ->
gatherOpTreeE
hasParen
(hasComms || hasAnyCommentsBelow epAnn)
commWrap
locOpen
locClose
((docHandleComms epAnn $ layoutExpr op1, layoutExpr r1) : opExprList)
l1
(L _ (HsPar epAnn inner)) -> do
let AnnParen _ spanOpen spanClose = anns epAnn
let mergePoses locMay span = case locMay of
Nothing -> Just (epaLocationRealSrcSpanStart span)
Just loc -> Just (min (epaLocationRealSrcSpanStart span) loc)
(innerTree, innerHasComms) <-
gatherOpTreeE True
(hasComms || hasAnyCommentsBelow epAnn)
(commWrap . docHandleComms epAnn)
(mergePoses locOpen spanOpen)
(mergePoses locClose spanClose)
[]
inner
if null opExprList
then pure (innerTree, innerHasComms)
else do
numberedRights <- opExprList `forM` \(x, y) -> do
x' <- x
y' <- y
pure (x', y')
pure
$ ( OpUnknown hasParen locOpen locClose innerTree numberedRights
, innerHasComms
)
final -> do
numberedLeft <- commWrap $ layoutExpr final
numberedRights <- opExprList `forM` \(x, y) -> do
x' <- x
y' <- y
pure (x', y')
pure
$ ( OpUnknown hasParen
locOpen
locClose
(OpLeaf $ numberedLeft)
numberedRights
, hasComms
)
gatherOpTreeT
:: Bool
-> Bool
-> (ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered)
-> Maybe GHC.RealSrcLoc
-> Maybe GHC.RealSrcLoc
-> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]
-> LHsType GhcPs
-> ToBriDocM (OpTree, Bool)
gatherOpTreeT hasParen hasComms commWrap locOpen locClose opExprList = \case
(L _ (HsOpTy NoExtField l1 op1 r1)) ->
gatherOpTreeT
hasParen
hasComms
commWrap
locOpen
locClose
((docLit $ printRdrNameWithAnns op1, layoutType r1) : opExprList)
l1
(L _ (HsParTy epAnn inner)) -> do
let AnnParen _ spanOpen spanClose = anns epAnn
let mergePoses locMay span = case locMay of
Nothing -> Just (epaLocationRealSrcSpanStart span)
Just loc -> Just (min (epaLocationRealSrcSpanStart span) loc)
(innerTree, innerHasComms) <-
gatherOpTreeT True
(hasComms || hasAnyCommentsBelow epAnn)
(commWrap . docHandleComms epAnn)
(mergePoses locOpen spanOpen)
(mergePoses locClose spanClose)
[]
inner
if null opExprList
then pure (innerTree, innerHasComms)
else do
numberedRights <- opExprList `forM` \(x, y) -> do
x' <- x
y' <- y
pure (x', y')
pure
$ ( OpUnknown hasParen locOpen locClose innerTree numberedRights
, innerHasComms
)
final -> do
numberedLeft <- commWrap $ layoutType final
numberedRights <- opExprList `forM` \(x, y) -> do
x' <- x
y' <- y
pure (x', y')
pure
$ ( OpUnknown hasParen
locOpen
locClose
(OpLeaf $ numberedLeft)
numberedRights
, hasComms
)
processOpTree :: (OpTree, Bool) -> ToBriDocM BriDocNumbered
processOpTree (unknownTree, hasComments) = do
enabled <- askLayoutConf _lconfig_fixityAwareOps
refactorMode <- askLayoutConf _lconfig_operatorParenthesisRefactorMode
allowOpUnqualify <- askLayoutConf _lconfig_operatorAllowUnqualify
let (warns, balancedTree) = if enabled
then balanceOpTree allowOpUnqualify unknownTree
else ([], unknownTree)
mTell warns
let processedTree = case refactorMode of
PRMKeep -> balancedTree
PRMMinimize -> remSuperfluousParens 11 balancedTree
PRMMaximize -> addAllParens False balancedTree
-- tellDebugMess $ displayOpTree balancedTree
-- tellDebugMess $ displayOpTree processedTree
layoutOpTree (not hasComments) processedTree
layoutOpTree :: Bool -> OpTree -> ToBriDocM BriDocNumbered
layoutOpTree allowSinglelinePar = \case
OpUnknown hasParen locO locC leftTree docOps -> do
let sharedOps = fmap (\(a, b) -> (pure a, pure b)) docOps
leftDoc <- layoutOpTree True leftTree
coreAlternative hasParen
locO
locC
Nothing
(pure leftDoc)
sharedOps
sharedOps
docForceSingleline
OpKnown hasParen locO locC fixity treeL docOps -> do
let Fixity _ _prec _ = fixity
docL <- shareDoc $ layoutOpTree True treeL
let flattenList ops = case ops of
[] -> pure []
[(op, tree)] -> case treeL of
OpLeaf{} -> flattenInner op tree
_ -> do
treeDoc <- shareDoc $ layoutOpTree True tree
pure [(pure op, treeDoc)]
((op1, tree1@OpLeaf{}) : tR) -> do
tree1Doc <- shareDoc $ layoutOpTree True tree1
flattenRest <- flattenList tR
pure $ (pure op1, tree1Doc) : flattenRest
_ -> simpleTransform ops
flattenInner op = \case
OpKnown False _ _ _ innerL innerOps | isPrec0 fixity -> do
flattenList ((op, innerL) : innerOps)
tree -> do
treeDoc <- shareDoc $ layoutOpTree True tree
pure [(pure op, treeDoc)]
simpleTransform
:: [(BriDocNumbered, OpTree)]
-> ToBriDocM [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]
simpleTransform = mapM $ \(op, subTree) -> do
subDoc <- shareDoc $ layoutOpTree True subTree
pure (pure op, subDoc)
sharedOpsFlat <- flattenList docOps
sharedOps <- simpleTransform docOps
coreAlternative hasParen
locO
locC
(Just fixity)
docL
sharedOps
sharedOpsFlat
docForceParSpacing
OpLeaf l -> pure l
where
isPrec0 = \case
Fixity _ prec _ -> prec == 0
coreAlternative
:: Bool
-> Maybe GHC.RealSrcLoc
-> Maybe GHC.RealSrcLoc
-> Maybe Fixity
-> ToBriDocM BriDocNumbered
-> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]
-> [(ToBriDocM BriDocNumbered, ToBriDocM BriDocNumbered)]
-> (ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered)
-> ToBriDocM BriDocNumbered
coreAlternative hasParen locO locC fixity docL sharedOps sharedOpsFlat lastWrap
= do
let wrapParenIfSl x inner = if x then wrapParenSl inner else inner
wrapParenSl inner = docAlt
[ docSeq
[ docLit $ Text.pack "("
, docHandleComms locO $ docForceSingleline inner
, docHandleComms locC $ docLit $ Text.pack ")"
]
, docLines
[ docSeq [docLitS "(", docHandleComms locO inner]
, docHandleComms locC $ docLit $ Text.pack ")"
]
]
wrapParenMlIf x innerHead innerLines = if x
then wrapParenMl innerHead innerLines
else docPar innerHead (docLines innerLines)
wrapParenMl innerHead innerLines = docSetBaseY $ docLines
( [ docCols
ColOpPrefix
[ appSep $ docLit $ Text.pack "("
, docHandleComms locO $ innerHead
]
]
++ innerLines
++ [docHandleComms locC $ docLit $ Text.pack ")"]
)
configAllowsParInsert <- askLayoutConf _lconfig_fixityBasedAddAlignParens
let allowParIns = configAllowsParInsert && case fixity of
Nothing -> False
Just (Fixity _ prec _) -> prec > 0
runFilteredAlternative $ do
-- > one + two + three
-- or
-- > one + two + case x of
-- > _ -> three
addAlternativeCond allowSinglelinePar $ wrapParenIfSl hasParen $ docSeq
([docForceSingleline docL] ++ case splitFirstLast sharedOps of
FirstLastEmpty -> []
FirstLastSingleton (od, ed) ->
[docSeparator, docForceSingleline od, docSeparator, lastWrap ed]
FirstLast (od1, ed1) ems (odN, edN) ->
( [ docSeparator
, docForceSingleline od1
, docSeparator
, docForceSingleline ed1
]
++ join
[ [ docSeparator
, docForceSingleline od
, docSeparator
, docForceSingleline ed
]
| (od, ed) <- ems
]
++ [ docSeparator
, docForceSingleline odN
, docSeparator
, lastWrap edN
]
)
)
-- this case rather leads to some unfortunate layouting than to anything
-- useful; disabling for now. (it interfers with cols stuff.)
addAlternativeCond (not hasParen) $ docPar
(docHandleComms locO $ docForceSingleline $ docL)
(docFlushCommsPost False locC $ docLines $ sharedOps <&> \(od, ed) ->
docCols ColOpPrefix [appSep od, docForceSingleline ed]
)
-- > ( one
-- > + two
-- > + three
-- > )
addAlternativeCond (allowParIns && not hasParen)
$ docForceZeroAdd
$ wrapParenMl
(docSetBaseY docL)
(sharedOpsFlat <&> \(od, ed) ->
docCols ColOpPrefix [appSep od, docSetBaseY ed]
)
-- > one
-- > + two
-- > + three
addAlternative
$ wrapParenMlIf
hasParen
-- ((if not hasParen then docAddBaseY BrIndentRegular else id) docL)
(if hasParen then docSetBaseY docL else docL)
(sharedOpsFlat <&> \(od, ed) ->
docCols ColOpPrefix [appSep od, docSetBaseY ed]
)

View File

@ -17,6 +17,7 @@ import Language.Haskell.Brittany.Internal.Types
import Language.Haskell.Brittany.Internal.Components.BriDoc
import Language.Haskell.Brittany.Internal.Utils
(FirstLastView(..), splitFirstLast)
import Language.Haskell.Brittany.Internal.ToBriDoc.OpTree
@ -262,8 +263,9 @@ layoutType ltype@(L _ typ) = docHandleComms ltype $ case typ of
(docAddBaseY (BrIndentSpecial 2) line1)
(docLines $ lines ++ [wrapEnd end])
]
HsOpTy{} -> -- TODO
briDocByExactInlineOnly "HsOpTy{}" ltype
HsOpTy{} -> do
treeAndHasComms <- gatherOpTreeT False False id Nothing Nothing [] ltype
processOpTree treeAndHasComms
-- 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

View File

@ -0,0 +1,10 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Language.Haskell.Brittany.Internal.ToBriDoc.Type where
import GHC.Hs
import Language.Haskell.Brittany.Internal.Types
layoutType :: ToBriDoc HsType

View File

@ -36,9 +36,10 @@ data VerticalSpacingPar
data VerticalSpacing
= VerticalSpacing
{ _vs_sameLine :: !Int
, _vs_paragraph :: !VerticalSpacingPar
, _vs_parFlag :: !Bool
{ _vs_sameLine :: !Int
, _vs_paragraph :: !VerticalSpacingPar
, _vs_parFlag :: !Bool
, _vs_onlyZeroAddInd :: !Bool
}
deriving (Eq, Show)
@ -54,6 +55,7 @@ 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_indentPrepForced :: Bool
, _acp_forceMLFlag :: AltLineModeState
}
deriving Show
@ -99,7 +101,7 @@ transformAlts
=> BriDocNumbered
-> MultiRWSS.MultiRWS r w s BriDoc
transformAlts =
MultiRWSS.withMultiStateA (AltCurPos 0 0 0 AltLineModeStateNone)
MultiRWSS.withMultiStateA (AltCurPos 0 0 0 False AltLineModeStateNone)
. Memo.startEvalMemoT
. fmap unwrapBriDocNumbered
. rec
@ -163,14 +165,18 @@ transformAlts =
BDSeparator -> processSpacingSimple bdX $> bdX
BDAddBaseY indent bd -> do
acp <- mGet
indAdd <- fixIndentationForMultiple acp indent
mSet $ acp { _acp_indentPrep = max (_acp_indentPrep acp) indAdd }
(indAdd, forced) <- fixIndentationForMultiple acp indent
mSet $ acp
{ _acp_indentPrep = max (_acp_indentPrep acp) indAdd
, _acp_indentPrepForced = forced || _acp_indentPrepForced acp
}
r <- rec bd
acp' <- mGet
mSet $ acp' { _acp_indent = _acp_indent acp }
return $ case indent of
BrIndentNone -> r
BrIndentRegular -> reWrap $ BDAddBaseY (BrIndentSpecial indAdd) r
BrIndentRegularForce -> reWrap $ BDAddBaseY (BrIndentSpecial indAdd) r
BrIndentSpecial i -> reWrap $ BDAddBaseY (BrIndentSpecial i) r
BDBaseYPushCur bd -> do
acp <- mGet
@ -188,16 +194,24 @@ transformAlts =
BDPar 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 parentForced = _acp_indentPrepForced acp
let
(indAdd, forced) = case indent of
BrIndentNone -> (0 , parentForced)
BrIndentRegular -> (indAmount, parentForced)
BrIndentRegularForce -> (indAmount, True )
BrIndentSpecial i -> (i , parentForced)
let ind = _acp_indent acp + _acp_indentPrep acp + indAdd
mSet $ acp { _acp_indent = ind, _acp_indentPrep = 0 }
mSet $ acp
{ _acp_indent = ind, _acp_indentPrep = 0
, _acp_indentPrepForced = False
}
sameLine' <- rec sameLine
mModify $ \acp' -> acp' { _acp_line = ind, _acp_indent = ind }
mModify $ \acp' -> acp'
{ _acp_line = ind, _acp_indent = ind
, _acp_indentPrepForced = forced
}
indented' <- rec indented
return $ reWrap $ BDPar indent sameLine' indented'
BDAlt [] -> error "empty BDAlt" -- returning BDEmpty instead is a
@ -214,12 +228,14 @@ transformAlts =
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
lineCheck (LineModeValid (VerticalSpacing _ p _ z)) =
let pRes = case _acp_forceMLFlag acp of
AltLineModeStateNone -> True
AltLineModeStateForceSL{} -> p == VerticalSpacingParNone
AltLineModeStateForceML{} -> p /= VerticalSpacingParNone
AltLineModeStateContradiction -> False
zRes = not z || not (_acp_indentPrepForced acp)
in pRes && zRes
-- TODO: use COMPLETE pragma instead?
lineCheck _ = error "ghc exhaustive check is insufficient"
lconf <- _conf_layout <$> mAsk
@ -244,11 +260,14 @@ transformAlts =
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
lineCheck (VerticalSpacing _ p _ z) =
let pRes = case _acp_forceMLFlag acp of
AltLineModeStateNone -> True
AltLineModeStateForceSL{} -> p == VerticalSpacingParNone
AltLineModeStateForceML{} -> p /= VerticalSpacingParNone
AltLineModeStateContradiction -> False
zRes = not z || not (_acp_indentPrepForced acp)
in pRes && zRes
lconf <- _conf_layout <$> mAsk
let
options = -- trace ("considering options:" ++ show (length alts, acp)) $
@ -263,32 +282,27 @@ transformAlts =
$ fromMaybe (-- trace ("choosing last") $
List.last alts)
$ Data.List.Extra.firstJust (fmap snd) checkedOptions
BDForceMultiline bd -> do
BDForceAlt ForceMultiline bd -> do
acp <- mGet
x <- do
mSet $ mergeLineMode acp (AltLineModeStateForceML False)
rec bd
acp' <- mGet
mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }
return $ x
BDForceSingleline bd -> do
mSet $ mergeLineMode acp (AltLineModeStateForceML False)
x <- rec bd
mModify $ \acp' -> acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }
pure $ x
BDForceAlt ForceSingleline bd -> do
acp <- mGet
x <- do
mSet $ mergeLineMode acp AltLineModeStateForceSL
rec bd
acp' <- mGet
mSet $ acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }
return $ x
mSet $ mergeLineMode acp AltLineModeStateForceSL
x <- rec bd
mModify $ \acp' -> acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }
pure $ x
BDForwardLineMode 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
mSet $ acp
{ _acp_forceMLFlag = altLineModeRefresh $ _acp_forceMLFlag acp
}
x <- rec bd
mModify $ \acp' -> acp' { _acp_forceMLFlag = _acp_forceMLFlag acp }
pure $ x
BDForceAlt ForceZeroAdd bd -> rec bd
BDExternal{} -> processSpacingSimple bdX $> bdX
BDPlain{} -> processSpacingSimple bdX $> bdX
BDQueueComments comms bd ->
@ -305,18 +319,23 @@ transformAlts =
reWrap . BDFlushCommentsPost loc shouldMark <$> rec bd
BDLines [] -> return $ reWrap BDEmpty -- evil transformation. or harmless.
BDLines (l : lr) -> do
ind <- _acp_indent <$> mGet
initialAcp <- mGet
l' <- rec l
lr' <- lr `forM` \x -> do
mModify $ \acp -> acp { _acp_line = ind, _acp_indent = ind }
mModify $ \acp -> acp
{ _acp_line = _acp_indent initialAcp
, _acp_indent = _acp_indent initialAcp
, _acp_indentPrepForced = _acp_indentPrepForced initialAcp
}
rec x
return $ reWrap $ BDLines (l' : lr')
BDEnsureIndent indent bd -> do
acp <- mGet
indAdd <- fixIndentationForMultiple acp indent
(indAdd, forced) <- fixIndentationForMultiple acp indent
mSet $ acp
{ _acp_indentPrep = 0
-- TODO: i am not sure this is valid, in general.
, _acp_indentPrepForced = forced
, _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
@ -331,10 +350,12 @@ transformAlts =
BrIndentNone -> r
BrIndentRegular ->
reWrap $ BDEnsureIndent (BrIndentSpecial indAdd) r
BrIndentRegularForce ->
reWrap $ BDEnsureIndent (BrIndentSpecial indAdd) r
BrIndentSpecial i -> reWrap $ BDEnsureIndent (BrIndentSpecial i) r
BDNonBottomSpacing _ bd -> rec bd
BDSetParSpacing bd -> rec bd
BDForceParSpacing bd -> rec bd
BDForceAlt (NonBottomSpacing _) bd -> rec bd
BDForceAlt SetParSpacing bd -> rec bd
BDForceAlt ForceParSpacing bd -> rec bd
BDDebug s bd -> do
acp :: AltCurPos <- mGet
tellDebugMess
@ -354,7 +375,7 @@ transformAlts =
-> m ()
processSpacingSimple bd = getSpacing bd >>= \case
LineModeInvalid -> error "processSpacingSimple inv"
LineModeValid (VerticalSpacing i VerticalSpacingParNone _) -> do
LineModeValid (VerticalSpacing i VerticalSpacingParNone _ _) -> do
acp <- mGet
mSet $ acp { _acp_line = _acp_line acp + i }
LineModeValid VerticalSpacing{} -> error "processSpacingSimple par"
@ -365,9 +386,9 @@ transformAlts =
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 _)
hasSpace2 lconf (AltCurPos line _indent _ _ _) (VerticalSpacing sameLine VerticalSpacingParNone _ _)
= line + sameLine <= confUnpack (_lconfig_cols lconf)
hasSpace2 lconf (AltCurPos line indent indentPrep _) (VerticalSpacing sameLine (VerticalSpacingParSome par) _)
hasSpace2 lconf (AltCurPos line indent indentPrep _ _) (VerticalSpacing sameLine (VerticalSpacingParSome par) _ _)
= line
+ sameLine
<= confUnpack (_lconfig_cols lconf)
@ -375,7 +396,7 @@ transformAlts =
+ indentPrep
+ par
<= confUnpack (_lconfig_cols lconf)
hasSpace2 lconf (AltCurPos line _indent _ _) (VerticalSpacing sameLine VerticalSpacingParAlways{} _)
hasSpace2 lconf (AltCurPos line _indent _ _ _) (VerticalSpacing sameLine VerticalSpacingParAlways{} _ _)
= line + sameLine <= confUnpack (_lconfig_cols lconf)
getSpacing
@ -389,40 +410,45 @@ getSpacing !bridoc = rec bridoc
rec (brDcId, brDc) = do
config <- mAsk
let colMax = config & _conf_layout & _lconfig_cols & confUnpack
result <- case brDc of
let indAmount = confUnpack $ _lconfig_indentAmount $ _conf_layout $ config
result :: LineModeValidity VerticalSpacing <- case brDc of
-- BDWrapAnnKey _annKey bd -> rec bd
BDEmpty ->
return $ LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False
BDLit t -> return $ LineModeValid $ VerticalSpacing
(Text.length t)
VerticalSpacingParNone
False
pure
$ LineModeValid
$ VerticalSpacing 0 VerticalSpacingParNone False False
BDLit t ->
pure
$ LineModeValid
$ VerticalSpacing (Text.length t) VerticalSpacingParNone False False
BDSeq list -> sumVs <$> rec `mapM` list
BDCols _sig list -> sumVs <$> rec `mapM` list
BDSeparator ->
return $ LineModeValid $ VerticalSpacing 1 VerticalSpacingParNone False
pure
$ LineModeValid
$ VerticalSpacing 1 VerticalSpacingParNone False False
BDAddBaseY 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
}
pure
[ vs
{ _vs_paragraph = case _vs_paragraph vs of
VerticalSpacingParNone -> VerticalSpacingParNone
VerticalSpacingParAlways i ->
VerticalSpacingParAlways $ case indent of
BrIndentNone -> i
BrIndentRegular -> i + indAmount
BrIndentRegularForce -> i + indAmount
BrIndentSpecial j -> i + j
VerticalSpacingParSome i ->
VerticalSpacingParSome $ case indent of
BrIndentNone -> i
BrIndentRegular -> i + indAmount
BrIndentRegularForce -> i + indAmount
BrIndentSpecial j -> i + j
}
| vs <- mVs
, indent == BrIndentNone || _vs_onlyZeroAddInd vs == False
]
BDBaseYPushCur bd -> do
mVs <- rec bd
return $ mVs <&> \vs -> vs
@ -446,8 +472,8 @@ getSpacing !bridoc = rec bridoc
mVs <- rec sameLine
mIndSp <- rec indented
return
$ [ VerticalSpacing lsp pspResult parFlagResult
| VerticalSpacing lsp mPsp _ <- mVs
$ [ VerticalSpacing lsp pspResult parFlagResult False -- TODO92 should we turn this on?
| VerticalSpacing lsp mPsp _ _ <- mVs
, indSp <- mIndSp
, lineMax <- getMaxVS $ mIndSp
, let
@ -468,49 +494,59 @@ getSpacing !bridoc = rec bridoc
BDPar{} -> error "BDPar with indent in getSpacing"
BDAlt [] -> error "empty BDAlt"
BDAlt (alt : _) -> rec alt
BDForceMultiline bd -> do
BDForceAlt ForceMultiline bd -> do
mVs <- rec bd
return $ mVs >>= _vs_paragraph .> \case
VerticalSpacingParNone -> LineModeInvalid
_ -> mVs
BDForceSingleline bd -> do
BDForceAlt ForceSingleline bd -> do
mVs <- rec bd
return $ mVs >>= _vs_paragraph .> \case
VerticalSpacingParNone -> mVs
_ -> LineModeInvalid
BDForceAlt ForceZeroAdd bd -> do
mVs <- rec bd
pure $ [ vs { _vs_onlyZeroAddInd = True } | vs <- mVs ]
BDForwardLineMode bd -> rec bd
BDExternal _ txt -> return $ LineModeValid $ case Text.lines txt of
[t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False
_ -> VerticalSpacing 999 VerticalSpacingParNone False
[t] ->
VerticalSpacing (Text.length t) VerticalSpacingParNone False False
_ -> VerticalSpacing 999 VerticalSpacingParNone False False
BDPlain txt -> return $ LineModeValid $ case Text.lines txt of
[t] -> VerticalSpacing (Text.length t) VerticalSpacingParNone False
_ -> VerticalSpacing 999 VerticalSpacingParNone False
[t] ->
VerticalSpacing (Text.length t) VerticalSpacingParNone False False
_ -> VerticalSpacing 999 VerticalSpacingParNone False False
BDQueueComments _comms bd -> rec bd
BDFlushCommentsPrior _loc bd -> rec bd
BDFlushCommentsPost _loc _shouldMark bd -> rec bd
BDEntryDelta _dp bd -> rec bd
BDLines [] ->
return $ LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False
pure
$ LineModeValid
$ VerticalSpacing 0 VerticalSpacingParNone False False
-- TODO92 should we set _vs_onlyZeroAddInd here too?
-- did not do that before, but it makes sense for lines..
BDLines (l1 : lR) -> do
mVs <- rec l1
mVRs <- rec `mapM` lR
let lSps = mVs : mVRs
return
$ [ VerticalSpacing lsp (VerticalSpacingParSome $ lineMax) False
| VerticalSpacing lsp _ _ <- mVs
$ [ VerticalSpacing lsp (VerticalSpacingParSome $ lineMax) False False
-- TODO92 should we set _vs_onlyZeroAddInd here too?
-- did not do that before, but it makes sense for lines..
| VerticalSpacing lsp _ _ _ <- mVs
, lineMax <- getMaxVS $ maxVs $ lSps
]
BDEnsureIndent 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
BDNonBottomSpacing b bd -> do
let addInd = case indent of
BrIndentNone -> 0
BrIndentRegular -> indAmount
BrIndentRegularForce -> indAmount
BrIndentSpecial i -> i
return $ mVs <&> \(VerticalSpacing lsp psp pf _) ->
VerticalSpacing (lsp + addInd) psp pf False
BDForceAlt (NonBottomSpacing b) bd -> do
mVs <- rec bd
return $ mVs <|> LineModeValid
(VerticalSpacing
@ -520,11 +556,12 @@ getSpacing !bridoc = rec bridoc
else VerticalSpacingParAlways colMax
)
False
False
)
BDSetParSpacing bd -> do
BDForceAlt SetParSpacing bd -> do
mVs <- rec bd
return $ mVs <&> \vs -> vs { _vs_parFlag = True }
BDForceParSpacing bd -> do
BDForceAlt ForceParSpacing bd -> do
mVs <- rec bd
return
$ [ vs
@ -541,12 +578,12 @@ getSpacing !bridoc = rec bridoc
++ "): mVs="
++ show r
return r
return result
pure result
maxVs
:: [LineModeValidity VerticalSpacing] -> LineModeValidity VerticalSpacing
maxVs = foldl'
(liftM2
(\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) -> VerticalSpacing
(\(VerticalSpacing x1 x2 _ _) (VerticalSpacing y1 y2 _ _) -> VerticalSpacing
(max x1 y1)
(case (x2, y2) of
(x, VerticalSpacingParNone) -> x
@ -561,28 +598,31 @@ getSpacing !bridoc = rec bridoc
VerticalSpacingParSome $ max x y
)
False
False
)
)
(LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False)
(LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone False 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
go (VerticalSpacing x1 x2 x3 x4) (VerticalSpacing y1 y2 _ y4) =
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
(x4 || y4)
singleline (LineModeValid x) = _vs_paragraph x == VerticalSpacingParNone
singleline _ = False
isPar (LineModeValid x) = _vs_parFlag x
@ -590,9 +630,10 @@ getSpacing !bridoc = rec bridoc
parFlag = case sps of
[] -> True
_ -> all singleline (List.init sps) && isPar (List.last sps)
initial = LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone parFlag
initial =
LineModeValid $ VerticalSpacing 0 VerticalSpacingParNone parFlag False
getMaxVS :: LineModeValidity VerticalSpacing -> LineModeValidity Int
getMaxVS = fmap $ \(VerticalSpacing x1 x2 _) -> x1 `max` case x2 of
getMaxVS = fmap $ \(VerticalSpacing x1 x2 _ _) -> x1 `max` case x2 of
VerticalSpacingParSome i -> i
VerticalSpacingParNone -> 0
VerticalSpacingParAlways i -> i
@ -621,7 +662,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
config <- mAsk
let colMax = config & _conf_layout & _lconfig_cols & confUnpack
let
hasOkColCount (VerticalSpacing lsp psp _) = lsp <= colMax && case psp of
hasOkColCount (VerticalSpacing lsp psp _ _) = lsp <= colMax && case psp of
VerticalSpacingParNone -> True
VerticalSpacingParSome i -> i <= colMax
VerticalSpacingParAlways{} -> True
@ -638,6 +679,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
let
allowHangingQuasiQuotes =
config & _conf_layout & _lconfig_allowHangingQuasiQuotes & confUnpack
let indAmount = confUnpack $ _lconfig_indentAmount $ _conf_layout $ config
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
@ -690,12 +732,14 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
. preFilterLimit
result <- case brdc of
-- BDWrapAnnKey _annKey bd -> rec bd
BDEmpty -> return $ [VerticalSpacing 0 VerticalSpacingParNone False]
BDLit t ->
return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]
BDEmpty -> return $ [VerticalSpacing 0 VerticalSpacingParNone False False]
BDLit t -> do
let l = Text.length t
pure $ [VerticalSpacing l VerticalSpacingParNone False False]
BDSeq list -> fmap sumVs . mapM filterAndLimit <$> rec `mapM` list
BDCols _sig list -> fmap sumVs . mapM filterAndLimit <$> rec `mapM` list
BDSeparator -> return $ [VerticalSpacing 1 VerticalSpacingParNone False]
BDSeparator ->
pure $ [VerticalSpacing 1 VerticalSpacingParNone False False]
BDAddBaseY indent bd -> do
mVs <- rec bd
return $ mVs <&> \vs -> vs
@ -704,18 +748,13 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
VerticalSpacingParAlways i ->
VerticalSpacingParAlways $ case indent of
BrIndentNone -> i
BrIndentRegular ->
i
+ (confUnpack
$ _lconfig_indentAmount
$ _conf_layout
$ config
)
BrIndentRegular -> i + indAmount
BrIndentRegularForce -> i + indAmount
BrIndentSpecial j -> i + j
VerticalSpacingParSome i -> VerticalSpacingParSome $ case indent of
BrIndentNone -> i
BrIndentRegular ->
i + (confUnpack $ _lconfig_indentAmount $ _conf_layout $ config)
BrIndentRegular -> i + indAmount
BrIndentRegularForce -> i + indAmount
BrIndentSpecial j -> i + j
}
BDBaseYPushCur bd -> do
@ -744,7 +783,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
mVss <- filterAndLimit <$> rec sameLine
indSps <- filterAndLimit <$> rec indented
let mVsIndSp = take limit $ [ (x, y) | x <- mVss, y <- indSps ]
return $ mVsIndSp <&> \(VerticalSpacing lsp mPsp _, indSp) ->
return $ mVsIndSp <&> \(VerticalSpacing lsp mPsp _ _, indSp) ->
VerticalSpacing
lsp
(case mPsp of
@ -760,6 +799,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
== VerticalSpacingParNone
&& _vs_parFlag indSp
)
False -- TODO92 should we turn this on?
BDPar{} -> error "BDPar with indent in getSpacing"
BDAlt [] -> error "empty BDAlt"
@ -767,31 +807,35 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
BDAlt alts -> do
r <- rec `mapM` alts
return $ filterAndLimit =<< r
BDForceMultiline bd -> do
BDForceAlt ForceMultiline bd -> do
mVs <- filterAndLimit <$> rec bd
return $ filter ((/= VerticalSpacingParNone) . _vs_paragraph) mVs
BDForceSingleline bd -> do
BDForceAlt ForceSingleline bd -> do
mVs <- filterAndLimit <$> rec bd
return $ filter ((== VerticalSpacingParNone) . _vs_paragraph) mVs
BDForwardLineMode bd -> rec bd
BDExternal _ txt | [t] <- Text.lines txt ->
return $ [VerticalSpacing (Text.length t) VerticalSpacingParNone False]
BDExternal _ txt | [t] <- Text.lines txt -> do
let l = Text.length t
pure $ [VerticalSpacing l VerticalSpacingParNone False False]
BDExternal{} -> return $ [] -- yes, we just assume that we cannot properly layout
-- this.
BDPlain 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
]
BDPlain t -> do
let tl = Text.length
pure
[ case Text.lines t of
[] -> VerticalSpacing 0 VerticalSpacingParNone False False
[t1] ->
VerticalSpacing (tl t1) VerticalSpacingParNone False False
(t1 : _) ->
VerticalSpacing (tl t1) (VerticalSpacingParAlways 0) True False
| allowHangingQuasiQuotes
]
BDQueueComments _comms bd -> rec bd
BDFlushCommentsPrior _loc bd -> rec bd
BDFlushCommentsPost _loc _shouldMark bd -> rec bd
BDEntryDelta _dp bd -> rec bd
BDLines [] -> return $ [VerticalSpacing 0 VerticalSpacingParNone False]
BDLines [] ->
pure $ [VerticalSpacing 0 VerticalSpacingParNone False False]
BDLines 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
@ -802,7 +846,10 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
let
worbled = fmap reverse $ sequence $ reverse $ lSpss
sumF lSps@(lSp1 : _) =
VerticalSpacing (_vs_sameLine lSp1) (spMakePar $ maxVs lSps) False
VerticalSpacing (_vs_sameLine lSp1)
(spMakePar $ maxVs lSps)
False
False -- TODO92
sumF [] =
error
$ "should not happen. if my logic does not fail"
@ -824,12 +871,12 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
let
addInd = case indent of
BrIndentNone -> 0
BrIndentRegular ->
confUnpack $ _lconfig_indentAmount $ _conf_layout $ config
BrIndentRegular -> indAmount
BrIndentRegularForce -> indAmount
BrIndentSpecial i -> i
return $ mVs <&> \(VerticalSpacing lsp psp parFlag) ->
VerticalSpacing (lsp + addInd) psp parFlag
BDNonBottomSpacing b bd -> do
return $ mVs <&> \(VerticalSpacing lsp psp parFlag _) ->
VerticalSpacing (lsp + addInd) psp parFlag False
BDForceAlt (NonBottomSpacing 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
@ -844,6 +891,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
else VerticalSpacingParAlways colMax
)
False
False
]
else mVs <&> \vs -> vs
{ _vs_sameLine = min colMax (_vs_sameLine vs)
@ -884,16 +932,19 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
-- False)
-- mVs
-- ]
BDSetParSpacing bd -> do
BDForceAlt SetParSpacing bd -> do
mVs <- rec bd
return $ mVs <&> \vs -> vs { _vs_parFlag = True }
BDForceParSpacing bd -> do
BDForceAlt ForceParSpacing bd -> do
mVs <- preFilterLimit <$> rec bd
return
$ [ vs
| vs <- mVs
, _vs_parFlag vs || _vs_paragraph vs == VerticalSpacingParNone
]
BDForceAlt ForceZeroAdd bd -> do
mVs <- preFilterLimit <$> rec bd
pure [ vs { _vs_onlyZeroAddInd = True } | vs <- mVs ]
BDDebug s bd -> do
r <- rec bd
tellDebugMess
@ -907,7 +958,7 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
return result
maxVs :: [VerticalSpacing] -> VerticalSpacing
maxVs = foldl'
(\(VerticalSpacing x1 x2 _) (VerticalSpacing y1 y2 _) -> VerticalSpacing
(\(VerticalSpacing x1 x2 _ _) (VerticalSpacing y1 y2 _ _) -> VerticalSpacing
(max x1 y1)
(case (x2, y2) of
(x, VerticalSpacingParNone) -> x
@ -922,63 +973,73 @@ getSpacings limit bridoc = preFilterLimit <$> rec bridoc
VerticalSpacingParSome $ max x y
)
False
False
)
(VerticalSpacing 0 VerticalSpacingParNone False)
(VerticalSpacing 0 VerticalSpacingParNone False 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
go (VerticalSpacing x1 x2 x3 x4) (VerticalSpacing y1 y2 _ y4) =
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
(x4 || y4)
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
initial = VerticalSpacing 0 VerticalSpacingParNone parFlag False
getMaxVS :: VerticalSpacing -> Int
getMaxVS (VerticalSpacing x1 x2 _) = x1 `max` case x2 of
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
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
:: (MonadMultiReader (CConfig Identity) m)
=> AltCurPos
-> BrIndent
-> m (Int, Bool)
fixIndentationForMultiple acp indent = do
indAmount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
let
indAddRaw = case indent of
BrIndentNone -> 0
BrIndentRegular -> indAmount
BrIndentSpecial i -> i
(indAddRaw, strongFlag) = case indent of
BrIndentNone -> (0, False)
BrIndentRegular -> (indAmount, False)
BrIndentRegularForce -> (indAmount, True)
BrIndentSpecial i -> (i, False)
-- 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
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
, strongFlag
)

View File

@ -5,8 +5,11 @@ module Language.Haskell.Brittany.Internal.Transformations.T2_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.Components.BriDoc
import GHC ( GenLocated(L) )
import Language.Haskell.Brittany.Internal.Components.BriDoc
import Language.Haskell.Brittany.Internal.Prelude
import Language.Haskell.Brittany.Internal.Utils
@ -83,7 +86,7 @@ transformSimplifyFloating = stepBO .> stepFull
descendQueueComments = transformDownMay $ \case
-- queue comments floating in
BDQueueComments comms1 (BDQueueComments comms2 x) ->
Just $ BDQueueComments (comms1 ++ comms2) x
Just $ BDQueueComments (mergeOn (\(L l _) -> l) comms1 comms2) x
BDQueueComments comms1 (BDPar ind line indented) ->
Just $ BDPar ind (BDQueueComments comms1 line) indented
BDQueueComments comms1 (BDSeq (l : lr)) ->
@ -131,11 +134,11 @@ transformSimplifyFloating = stepBO .> stepFull
descendAddB = transformDownMay $ \case
BDAddBaseY BrIndentNone x -> Just x
-- AddIndent floats into Lines.
BDAddBaseY indent (BDLines lines) ->
Just $ BDLines $ BDAddBaseY indent <$> lines
BDAddBaseY _ind (BDLines lines) ->
Just $ BDLines $ lines
-- AddIndent floats into last column
BDAddBaseY indent (BDCols sig cols) ->
Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]
BDAddBaseY ind (BDCols sig cols) ->
Just $ BDCols sig $ List.init cols ++ [BDAddBaseY ind $ List.last cols]
-- merge AddIndent and Par
BDAddBaseY ind1 (BDPar ind2 line indented) ->
Just $ BDPar (mergeIndents ind1 ind2) line indented
@ -148,8 +151,8 @@ transformSimplifyFloating = stepBO .> stepFull
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 (BDBaseYPushCur x) ->
Just $ BDBaseYPushCur x
-- TODO92 We have several rules here in conflict with each other.
-- Unless I forget some detail related to some elements being able
-- to float in further, we probably should define some
@ -193,19 +196,19 @@ transformSimplifyFloating = stepBO .> stepFull
-- copying them here (incompletely).
BDAddBaseY BrIndentNone x -> Just $ x
-- AddIndent floats into Lines.
BDAddBaseY indent (BDLines lines) ->
Just $ BDLines $ BDAddBaseY indent <$> lines
BDAddBaseY _ind (BDLines lines) ->
Just $ BDLines lines
-- AddIndent floats into last column
BDAddBaseY indent (BDCols sig cols) ->
Just $ BDCols sig $ List.init cols ++ [BDAddBaseY indent $ List.last cols]
BDAddBaseY ind (BDCols sig cols) ->
Just $ BDCols sig $ List.init cols ++ [BDAddBaseY ind $ 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 (BDBaseYPushCur x) ->
Just $ BDBaseYPushCur x
-- EnsureIndent float-in
-- BDEnsureIndent indent (BDCols sig (col:colr)) ->
-- Just $ BDCols sig (BDEnsureIndent indent col : (BDAddBaseY indent <$> colr))

View File

@ -129,8 +129,7 @@ transformSimplifyColumns = Uniplate.rewrite $ \case
BDIndentLevelPop{} -> Nothing
BDPar{} -> Nothing
BDAlt{} -> Nothing
BDForceMultiline{} -> Nothing
BDForceSingleline{} -> Nothing
BDForceAlt{} -> Nothing
BDForwardLineMode{} -> Nothing
BDExternal{} -> Nothing
BDPlain{} -> Nothing
@ -140,7 +139,4 @@ transformSimplifyColumns = Uniplate.rewrite $ \case
BDFlushCommentsPost{} -> Nothing
BDEntryDelta{} -> Nothing
BDEnsureIndent{} -> Nothing
BDSetParSpacing{} -> Nothing
BDForceParSpacing{} -> Nothing
BDDebug{} -> Nothing
BDNonBottomSpacing _ x -> Just x

View File

@ -20,7 +20,7 @@ import qualified GHC.Types.Name.Reader as RdrName (rdrNameOcc)
import qualified GHC.Types.SrcLoc as GHC
import qualified GHC.Utils.Outputable as GHC
import qualified GHC.Parser.Annotation as GHC
import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils
-- import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils
import qualified Text.PrettyPrint as PP
import qualified Data.Semigroup as Semigroup
import qualified System.IO.Unsafe as Unsafe
@ -108,7 +108,6 @@ customLayouterNoSrcSpansF layoutF =
`extQ` internalLayouterSrcSpan
`extQ` internalLayouterRdrName
`extQ` realSrcSpan
`extQ` deltaComment
`extQ` anchored
`ext1Q` srcSpanAnn
-- `ext2Q` located
@ -122,9 +121,9 @@ customLayouterNoSrcSpansF layoutF =
anchored (GHC.Anchor _ op) = f op
srcSpanAnn :: forall a . Data a => GHC.SrcSpanAnn' a -> NodeLayouter
srcSpanAnn (GHC.SrcSpanAnn ann _loc) = f ann
deltaComment :: GHC.LEpaComment -> NodeLayouter
deltaComment (GHC.L anchor (GHC.EpaComment token prior)) =
f (GHC.L (ExactPrint.Utils.ss2deltaEnd prior (GHC.anchor anchor)) token)
-- deltaComment :: GHC.LEpaComment -> NodeLayouter
-- deltaComment (GHC.L anchor (GHC.EpaComment token prior)) =
-- f (GHC.L (ExactPrint.Utils.ss2deltaEnd prior (GHC.anchor anchor)) token)
-- located :: (Data b, Data loc) => GHC.GenLocated loc b -> NodeLayouter
-- located (GHC.L ss a) = runDataToLayouter layoutF $ A annStr a
-- where
@ -143,11 +142,34 @@ customLayouterNoAnnsF layoutF =
`extQ` internalLayouterOccName
`extQ` internalLayouterSrcSpan
`extQ` internalLayouterRdrName
`extQ` realSrcSpan
`extQ` realSrcLoc
`ext2Q` located
`extQ` lepaComment
where
DataToLayouter f = defaultLayouterF layoutF
located :: (Data b) => GHC.GenLocated loc b -> NodeLayouter
located (GHC.L _ss a) = runDataToLayouter layoutF a
lepaComment :: GHC.LEpaComment -> NodeLayouter
lepaComment (GHC.L anchor (GHC.EpaComment token _)) = f
( token
, GHC.srcSpanStartLine (GHC.anchor anchor)
, GHC.srcSpanStartCol (GHC.anchor anchor)
, GHC.srcSpanEndLine (GHC.anchor anchor)
, GHC.srcSpanEndCol (GHC.anchor anchor)
)
realSrcSpan :: GHC.RealSrcSpan -> NodeLayouter
realSrcSpan span = internalLayouterSimple
(show
( GHC.srcSpanStartLine span
, GHC.srcSpanStartCol span
, GHC.srcSpanEndLine span
, GHC.srcSpanEndCol span
)
)
realSrcLoc :: GHC.RealSrcLoc -> NodeLayouter
realSrcLoc loc =
internalLayouterSimple (show (GHC.srcLocLine loc, GHC.srcLocCol loc))
internalLayouterSimple :: String -> NodeLayouter
internalLayouterSimple s = NodeLayouter (length s) False (const $ PP.text s)
@ -248,9 +270,9 @@ briDocToDoc :: BriDoc -> PP.Doc
briDocToDoc = astToDoc . removeAnnotations
where
removeAnnotations = Uniplate.transform $ \case
BDFlushCommentsPrior _ x -> x
BDFlushCommentsPost _ _ x -> x
BDQueueComments _ x -> x
-- BDFlushCommentsPrior _ x -> x
-- BDFlushCommentsPost _ _ x -> x
-- BDQueueComments _ x -> x
x -> x
briDocToDocWithAnns :: BriDoc -> PP.Doc
@ -305,3 +327,11 @@ traceIfDumpConf s accessor val = do
Unsafe.unsafePerformIO $ do
f ("---- " ++ s ++ " ----\n" ++ show val)
pure $ pure ()
mergeOn :: Ord b => (a -> b) -> [a] -> [a] -> [a]
mergeOn _f xs [] = xs
mergeOn _f [] ys = ys
mergeOn f xs@(x:xr) ys@(y:yr)
| f x <= f y = x : mergeOn f xr ys
| otherwise = y : mergeOn f xs yr

View File

@ -402,8 +402,7 @@ briDocLineLength briDoc = flip StateS.evalState False $ rec briDoc
BDIndentLevelPop bd -> rec bd
BDPar _ line _ -> rec line
BDAlt{} -> error "briDocLineLength BDAlt"
BDForceMultiline bd -> rec bd
BDForceSingleline bd -> rec bd
BDForceAlt _ bd -> rec bd
BDForwardLineMode bd -> rec bd
BDExternal _ t -> return $ Text.length t
BDPlain t -> return $ Text.length t
@ -416,9 +415,6 @@ briDocLineLength briDoc = flip StateS.evalState False $ rec briDoc
BDEntryDelta _dp bd -> rec bd
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
@ -437,8 +433,7 @@ briDocIsMultiLine briDoc = rec briDoc
BDIndentLevelPop bd -> rec bd
BDPar{} -> True
BDAlt{} -> error "briDocIsMultiLine BDAlt"
BDForceMultiline _ -> True
BDForceSingleline bd -> rec bd
BDForceAlt _ bd -> rec bd
BDForwardLineMode bd -> rec bd
BDExternal _ t | [_] <- Text.lines t -> False
BDExternal{} -> True
@ -452,9 +447,6 @@ briDocIsMultiLine briDoc = rec briDoc
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
briDocToColInfo :: Bool -> BriDoc -> StateS.State ColBuildState ColInfo

View File

@ -285,7 +285,7 @@ layoutBaseYPushCur = do
, _lstate_plannedSpace state
)
layoutBaseYPushInternal $ case _lstate_plannedSpace state of
PlannedNone -> _lstate_curY state
PlannedNone -> _lstate_curY state
PlannedSameline i -> _lstate_curY state + i
PlannedNewline _l -> lstate_baseY state
PlannedDelta _l i -> i

View File

@ -5,12 +5,12 @@
import Data.Coerce (coerce)
import Data.List (groupBy)
import qualified Data.Maybe
import qualified Data.Semigroup as Semigroup
-- import qualified Data.Semigroup as Semigroup
import qualified Data.Text as Text
import qualified Data.Text.IO as Text.IO
import qualified GHC.OldList as List
-- import qualified GHC.OldList as List
import qualified Data.Map.Strict as Map
import Data.These
-- import Data.These
import Language.Haskell.Brittany.Internal
import Language.Haskell.Brittany.Internal.Config.Config
import Language.Haskell.Brittany.Internal.Config.Types
@ -21,6 +21,7 @@ import System.Timeout (timeout)
import Test.Hspec
import qualified Text.Parsec as Parsec
import Text.Parsec.Text (Parser)
import qualified Data.List.Extra
hush :: Either a b -> Maybe b
hush = either (const Nothing) Just
@ -61,7 +62,9 @@ roundTripEqualWithTimeout time t =
data InputLine
= GroupLine Text
| HeaderLine Text
| HeaderLineGolden Text
| PendingLine
| HeaderLineGoldenOutput
| NormalLine Text
| CommentLine
deriving Show
@ -70,6 +73,7 @@ data TestCase = TestCase
{ testName :: Text
, isPending :: Bool
, content :: Text
, expectedOutput :: Maybe Text -- Nothing if input is expected not to change
}
main :: IO ()
@ -146,8 +150,9 @@ main = do
(tests `forM_` \test -> do
(if isPending test then before_ pending else id)
$ it (Text.unpack $ testName test)
$ roundTripEqual conf
$ content test
$ case expectedOutput test of
Nothing -> roundTripEqual conf (content test)
Just expctd -> goldenTest conf (content test) expctd
)
ks
)
@ -187,13 +192,30 @@ main = do
{ testName = n
, isPending = any isPendingLine rest
, content = Text.unlines normalLines
, expectedOutput = Nothing
}
HeaderLineGolden n : rest ->
case Data.List.Extra.wordsBy isGoldenOutputLine rest of
[inputLines, outputLines] ->
let
inputs = Data.Maybe.mapMaybe extractNormal inputLines
outputs = Data.Maybe.mapMaybe extractNormal outputLines
in TestCase
{ testName = n
, isPending = any isPendingLine rest
, content = Text.unlines inputs
, expectedOutput = Just (Text.unlines outputs)
}
_ -> error $ "malformed golden test at " ++ show n
l ->
error $ "first non-empty line must start with #test footest\n" ++ show l
extractNormal (NormalLine l) = Just l
extractNormal _ = Nothing
isPendingLine PendingLine{} = True
isPendingLine _ = False
isGoldenOutputLine = \case
HeaderLineGoldenOutput -> True
_ -> False
specialLineParser :: Parser InputLine
specialLineParser = Parsec.choice
[ [ GroupLine $ Text.pack name
@ -208,11 +230,21 @@ main = do
, name <- Parsec.many1 $ Parsec.noneOf "\r\n:"
, _ <- Parsec.eof
]
, [ HeaderLineGolden $ Text.pack name
| _ <- Parsec.try $ Parsec.string "#golden"
, _ <- Parsec.many1 $ Parsec.oneOf " \t"
, name <- Parsec.many1 $ Parsec.noneOf "\r\n:"
, _ <- Parsec.eof
]
, [ PendingLine
| _ <- Parsec.try $ Parsec.string "#pending"
, _ <- Parsec.optional $ many (Parsec.noneOf "\r\n")
, _ <- Parsec.eof
]
, [ HeaderLineGoldenOutput
| _ <- Parsec.try $ Parsec.string "#expected"
, _ <- Parsec.eof
]
, [ CommentLine
| _ <- Parsec.many $ Parsec.oneOf " \t"
, _ <- Parsec.optional $ Parsec.string "##" <* many
@ -235,8 +267,9 @@ main = do
grouperG _ GroupLine{} = False
grouperG _ _ = True
grouperT :: InputLine -> InputLine -> Bool
grouperT _ HeaderLine{} = False
grouperT _ _ = True
grouperT _ HeaderLine{} = False
grouperT _ HeaderLineGolden{} = False
grouperT _ _ = True
--------------------
@ -247,6 +280,11 @@ roundTripEqual c t =
fmap (fmap PPTextWrapper) (parsePrintModuleTests c "TestFakeFileName.hs" t)
`shouldReturn` Right (PPTextWrapper t)
goldenTest :: Config -> Text -> Text -> Expectation
goldenTest c input expected =
fmap (fmap PPTextWrapper) (parsePrintModuleTests c "TestFakeFileName.hs" input)
`shouldReturn` Right (PPTextWrapper expected)
newtype PPTextWrapper = PPTextWrapper Text
deriving Eq
@ -260,23 +298,28 @@ defaultTestConfig = Config
{ _conf_version = _conf_version staticDefaultConfig
, _conf_debug = _conf_debug staticDefaultConfig
, _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 (60 :: Int)
, _lconfig_importAsColumn = coerce (60 :: 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 True
, _lconfig_allowHangingQuasiQuotes = coerce True
, _lconfig_experimentalSemicolonNewlines = coerce False
{ _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 (60 :: Int)
, _lconfig_importAsColumn = coerce (60 :: 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 True
, _lconfig_allowHangingQuasiQuotes = coerce True
, _lconfig_experimentalSemicolonNewlines = coerce False
-- , _lconfig_allowSinglelineRecord = coerce False
, _lconfig_fixityAwareOps = coerce True
, _lconfig_fixityAwareTypeOps = coerce True
, _lconfig_fixityBasedAddAlignParens = coerce False
, _lconfig_operatorParenthesisRefactorMode = coerce PRMKeep
, _lconfig_operatorAllowUnqualify = coerce True
}
, _conf_errorHandling = (_conf_errorHandling staticDefaultConfig) { _econf_omit_output_valid_check = coerce True }
, _conf_preprocessor = _conf_preprocessor staticDefaultConfig