319 lines
11 KiB
Haskell
319 lines
11 KiB
Haskell
{-# 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
|
|
|
|
|
|
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 $ callLayouter layout_expr op1
|
|
, callLayouter layout_expr 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 $ callLayouter layout_expr 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, callLayouter layout_type 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 $ callLayouter layout_type 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 docSetParSpacing 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
|
|
$ docSetParSpacing
|
|
$ 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)
|
|
(sharedOps <&> \(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)
|
|
((if hasParen then sharedOps else sharedOpsFlat) <&> \(od, ed) ->
|
|
docCols ColOpPrefix [appSep od, docSetBaseY ed]
|
|
)
|