833 lines
31 KiB
Haskell
833 lines
31 KiB
Haskell
{-# LANGUAGE NoImplicitPrelude #-}
|
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
|
|
module Language.Haskell.Brittany.Internal.ToBriDocTools where
|
|
|
|
import qualified Control.Monad.Writer.Strict as Writer
|
|
import qualified Data.Char as Char
|
|
import Data.Data
|
|
import qualified Data.Generics as SYB
|
|
import qualified Data.Sequence as Seq
|
|
import qualified Data.Text as Text
|
|
import DataTreePrint
|
|
import GHC ( EpAnn(EpAnn, EpAnnNotUsed)
|
|
, EpAnnComments
|
|
( EpaComments
|
|
, EpaCommentsBalanced
|
|
)
|
|
, GenLocated(L)
|
|
, LEpaComment
|
|
, Located
|
|
, LocatedA
|
|
, moduleName
|
|
, moduleNameString
|
|
)
|
|
import qualified GHC
|
|
import GHC.Data.FastString ( FastString )
|
|
import qualified GHC.OldList as List
|
|
import GHC.Parser.Annotation ( AnnKeywordId(..) )
|
|
import GHC.Types.Name ( getOccString )
|
|
import GHC.Types.Name.Occurrence ( occNameString )
|
|
import GHC.Types.Name.Reader ( RdrName(..) )
|
|
import qualified GHC.Types.SrcLoc as GHC
|
|
import 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
|
|
import Language.Haskell.Brittany.Internal.Types
|
|
import Language.Haskell.Brittany.Internal.Utils
|
|
import qualified Language.Haskell.GHC.ExactPrint
|
|
as ExactPrint
|
|
import qualified Language.Haskell.GHC.ExactPrint.Utils
|
|
as ExactPrint
|
|
|
|
|
|
|
|
-- | Use ExactPrint's output for this node; add a newly generated inline comment
|
|
-- at insertion position (meant to point out to the user that this node is
|
|
-- not handled by brittany yet). Useful when starting implementing new
|
|
-- syntactic constructs when children are not handled yet.
|
|
briDocByExact
|
|
:: (ExactPrint.ExactPrint ast, Data ast)
|
|
=> LocatedA ast
|
|
-> ToBriDocM BriDocNumbered
|
|
briDocByExact ast = do
|
|
traceIfDumpConf
|
|
"ast"
|
|
_dconf_dump_ast_unknown
|
|
(printTreeWithCustom 160 customLayouterF ast)
|
|
mModify (+ connectedCommentCount ast)
|
|
docExt ast True
|
|
|
|
-- | Use ExactPrint's output for this node.
|
|
-- Consider that for multi-line input, the indentation of the code produced
|
|
-- by ExactPrint might be different, and even incompatible with the indentation
|
|
-- of its surroundings as layouted by brittany. But there are safe uses of
|
|
-- this, e.g. for any top-level declarations.
|
|
briDocByExactNoComment
|
|
:: (ExactPrint.ExactPrint (GenLocated l ast), Data ast, Data l)
|
|
=> GenLocated l ast
|
|
-> ToBriDocM BriDocNumbered
|
|
briDocByExactNoComment ast = do
|
|
traceIfDumpConf
|
|
"ast"
|
|
_dconf_dump_ast_unknown
|
|
(printTreeWithCustom 160 customLayouterF ast)
|
|
mModify (+ connectedCommentCount ast)
|
|
docExt ast False
|
|
|
|
-- | Use ExactPrint's output for this node, presuming that this output does
|
|
-- not contain any newlines. If this property is not met, the semantics
|
|
-- depend on the @econf_AllowRiskyExactPrintUse@ config flag.
|
|
briDocByExactInlineOnly
|
|
-- :: ( ExactPrint.ExactPrint (GHC.XRec GhcPs a)
|
|
-- , Data (GHC.XRec GhcPs a)
|
|
-- , Data a
|
|
-- , Data (GHC.Anno a)
|
|
-- , Outputable (GHC.Anno a)
|
|
-- )
|
|
:: ( GHC.Anno a ~ GHC.SrcSpanAnn' (EpAnn ann)
|
|
, ExactPrint.ExactPrint (GenLocated (GHC.SrcSpanAnn' (EpAnn ann)) a)
|
|
, Data a
|
|
, Data (GHC.Anno a)
|
|
, Outputable (GHC.Anno a)
|
|
)
|
|
=> String
|
|
-> GenLocated (GHC.SrcSpanAnn' (EpAnn ann)) a
|
|
-> ToBriDocM (BriDocNumbered)
|
|
briDocByExactInlineOnly infoStr ast = do
|
|
traceIfDumpConf "ast"
|
|
_dconf_dump_ast_unknown
|
|
(printTreeWithCustom 160 customLayouterF ast)
|
|
let exactPrinted =
|
|
dropWhile Text.null $ Text.lines $ Text.pack $ ExactPrint.exactPrint ast
|
|
fallbackMode <-
|
|
mAsk <&> _conf_errorHandling .> _econf_ExactPrintFallback .> confUnpack
|
|
let exactPrintNode t = allocateNode $ BDExternal False t
|
|
let errorAction = do
|
|
mTell [ErrorUnknownNode infoStr ast]
|
|
docLit
|
|
$ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"
|
|
mModify (+ connectedCommentCount ast)
|
|
case (fallbackMode, exactPrinted) of
|
|
(ExactPrintFallbackModeNever, _ ) -> errorAction
|
|
(_ , [t]) -> exactPrintNode
|
|
(Text.dropWhile Char.isSpace . Text.dropWhileEnd Char.isSpace $ t)
|
|
(ExactPrintFallbackModeRisky, _) -> exactPrintNode
|
|
(Text.unlines exactPrinted)
|
|
_ -> errorAction
|
|
|
|
rdrNameToText :: RdrName -> Text
|
|
-- rdrNameToText = Text.pack . show . flip runSDoc unsafeGlobalDynFlags . ppr
|
|
rdrNameToText (Unqual occname) = Text.pack $ occNameString occname
|
|
rdrNameToText (Qual mname occname) =
|
|
Text.pack $ moduleNameString mname ++ "." ++ occNameString occname
|
|
rdrNameToText (Orig modul occname) =
|
|
Text.pack $ moduleNameString (moduleName modul) ++ occNameString occname
|
|
rdrNameToText (Exact name) = Text.pack $ getOccString name
|
|
|
|
lrdrNameToText :: GenLocated l RdrName -> Text
|
|
lrdrNameToText (L _ n) = rdrNameToText n
|
|
|
|
class PrintRdrNameWithAnns l where
|
|
printRdrNameWithAnns :: GenLocated l RdrName -> Text
|
|
|
|
instance PrintRdrNameWithAnns GHC.SrcSpanAnnN where
|
|
printRdrNameWithAnns (L (GHC.SrcSpanAnn epAnn _) name) =
|
|
case epAnn of
|
|
EpAnn _ (GHC.NameAnn GHC.NameParens _ _ _ _) _ -> f "(" name ")"
|
|
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
|
|
EpAnnNotUsed -> rdrNameToText name
|
|
where
|
|
f a b c = Text.pack a <> rdrNameToText b <> Text.pack c
|
|
|
|
lrdrNameToTextAnnGen
|
|
:: (MonadMultiReader Config m, PrintRdrNameWithAnns l)
|
|
=> (Text -> Text)
|
|
-> GenLocated l RdrName
|
|
-> m Text
|
|
-- TODO this doesn't need to be monadic. I am pretty sure it started of as
|
|
-- a pure function, then at some point annotations were inspected
|
|
-- (from reader) but now it is pure again.
|
|
-- Leaving it as pseudo-monadic is harmless though (I think? Maybe I should
|
|
-- check I don't force some mapM/sequence/… garbage at common callsides
|
|
-- for this).
|
|
lrdrNameToTextAnnGen f ast = pure $ f $ printRdrNameWithAnns ast
|
|
|
|
lrdrNameToTextAnn
|
|
:: (MonadMultiReader Config m, PrintRdrNameWithAnns l)
|
|
=> GenLocated l RdrName
|
|
-> m Text
|
|
lrdrNameToTextAnn = lrdrNameToTextAnnGen id
|
|
|
|
lrdrNameToTextAnnTypeEqualityIsSpecial
|
|
:: (MonadMultiReader Config m, PrintRdrNameWithAnns l)
|
|
=> GenLocated l RdrName
|
|
-> m Text
|
|
lrdrNameToTextAnnTypeEqualityIsSpecial ast = do
|
|
let
|
|
f x = if x == Text.pack "Data.Type.Equality~"
|
|
then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh
|
|
else x
|
|
lrdrNameToTextAnnGen f ast
|
|
|
|
-- | Same as lrdrNameToTextAnnTypeEqualityIsSpecial, but also inspects
|
|
-- the annotations for a (parent) node for a tick to be added to the
|
|
-- literal.
|
|
-- Excessively long name to reflect on us having to work around such
|
|
-- excessively obscure special cases in the exactprint API.
|
|
lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick
|
|
:: (MonadMultiReader Config m, PrintRdrNameWithAnns l)
|
|
=> Located ast
|
|
-> GenLocated l RdrName
|
|
-> m Text
|
|
lrdrNameToTextAnnTypeEqualityIsSpecialAndRespectTick _ast1 ast2 = do
|
|
-- TODO92
|
|
-- hasQuote <- hasAnnKeyword ast1 AnnSimpleQuote
|
|
x <- lrdrNameToTextAnn ast2
|
|
let
|
|
lit = if x == Text.pack "Data.Type.Equality~"
|
|
then Text.pack "~" -- rraaaahhh special casing rraaahhhhhh
|
|
else x
|
|
return lit -- $ if hasQuote then Text.cons '\'' lit else lit
|
|
|
|
askIndent :: (MonadMultiReader Config m) => m Int
|
|
askIndent = confUnpack . _lconfig_indentAmount . _conf_layout <$> mAsk
|
|
|
|
-- TODO92 this is not filtering enough yet, see old code below
|
|
hasAnyCommentsBelow :: Data ast => ast -> Bool
|
|
hasAnyCommentsBelow =
|
|
getAny . SYB.everything (<>) (SYB.mkQ (Any False) (\(_ :: LEpaComment) -> Any True))
|
|
-- -- | True if there are any comments that are
|
|
-- -- a) connected to any node below (in AST sense) the given node AND
|
|
-- -- b) after (in source code order) the node.
|
|
-- hasAnyCommentsBelow :: Data ast => GHC.Located ast -> ToBriDocM Bool
|
|
-- hasAnyCommentsBelow ast@(L l _) =
|
|
-- List.any (\(c, _) -> ExactPrint.commentIdentifier c > ExactPrint.Utils.rs l)
|
|
-- <$> astConnectedComments ast
|
|
|
|
-- extractRestComments
|
|
-- :: ExactPrint.Annotation -> [(ExactPrint.Comment, ExactPrint.DeltaPos)]
|
|
-- extractRestComments ann =
|
|
-- ExactPrint.annFollowingComments ann
|
|
-- ++ (ExactPrint.annsDP ann >>= \case
|
|
-- (ExactPrint.AnnComment com, dp) -> [(com, dp)]
|
|
-- _ -> []
|
|
-- )
|
|
|
|
-- filterAnns :: Data.Data.Data ast => ast -> ExactPrint.Anns -> ExactPrint.Anns
|
|
-- filterAnns ast = Map.filterWithKey (\k _ -> k `Set.member` foldedAnnKeys ast)
|
|
|
|
hasCommentsBetween
|
|
:: Data ast
|
|
=> ast
|
|
-> Maybe GHC.RealSrcSpan
|
|
-> Maybe GHC.RealSrcSpan
|
|
-> Bool
|
|
hasCommentsBetween ast left right = do
|
|
getAny
|
|
$ SYB.everything
|
|
(<>)
|
|
(SYB.mkQ
|
|
(Any False)
|
|
(\(L (GHC.Anchor pos _) _ :: LEpaComment) -> Any
|
|
( ( maybe True
|
|
(\l -> GHC.realSrcSpanStart pos >= GHC.realSrcSpanEnd l)
|
|
left
|
|
)
|
|
&& (maybe True
|
|
(\l -> GHC.realSrcSpanEnd pos <= GHC.realSrcSpanStart l)
|
|
right
|
|
)
|
|
)
|
|
)
|
|
)
|
|
ast
|
|
|
|
startsWithComments :: EpAnn a -> Bool
|
|
startsWithComments = \case
|
|
EpAnnNotUsed -> False
|
|
EpAnn (GHC.Anchor srcSpan _) _ comms -> case comms of
|
|
EpaComments cs -> anyCheck cs
|
|
EpaCommentsBalanced comms1 comms2 -> anyCheck comms1 || anyCheck comms2
|
|
where
|
|
anyCheck cs =
|
|
any
|
|
(\(L _ (GHC.EpaComment _ commSpan)) ->
|
|
GHC.realSrcSpanStart srcSpan == GHC.realSrcSpanStart commSpan
|
|
)
|
|
cs
|
|
|
|
|
|
-- mAnn <- astAnn ast
|
|
-- let
|
|
-- go1 [] = False
|
|
-- go1 ((ExactPrint.G kw, _dp) : rest) | kw == leftKey = go2 rest
|
|
-- go1 (_ : rest) = go1 rest
|
|
-- go2 [] = False
|
|
-- go2 ((ExactPrint.AnnComment _, _dp) : _rest) = True
|
|
-- go2 ((ExactPrint.G kw, _dp) : _rest) | kw == rightKey = False
|
|
-- go2 (_ : rest) = go2 rest
|
|
-- case mAnn of
|
|
-- Nothing -> pure False
|
|
-- Just ann -> pure $ go1 $ ExactPrint.annsDP ann
|
|
|
|
-- | True if there are any comments that are connected to any node below (in AST
|
|
-- sense) the given node
|
|
hasAnyCommentsConnected :: (Data ann, Data ast) => GHC.GenLocated ann ast -> Bool
|
|
hasAnyCommentsConnected =
|
|
getAny . SYB.everything (<>) (SYB.mkQ (Any False) (\(_ :: LEpaComment) -> Any True))
|
|
|
|
connectedCommentCount :: (Data ann, Data ast) => GHC.GenLocated ann ast -> CommentCounter
|
|
connectedCommentCount =
|
|
getSum . SYB.everything (<>) (SYB.mkQ (Sum 0) (\(_ :: LEpaComment) -> Sum 1))
|
|
|
|
-- | True if there are any regular comments connected to any node below (in AST
|
|
-- sense) the given node
|
|
-- hasAnyRegularCommentsConnected :: GenLocated ann ast -> Bool
|
|
-- hasAnyRegularCommentsConnected ast =
|
|
-- any isRegularComment $ astConnectedComments ast
|
|
|
|
-- | Regular comments are comments that are actually "source code comments",
|
|
-- i.e. things that start with "--" or "{-". In contrast to comment-annotations
|
|
-- used by ghc-exactprint for capturing symbols (and their exact positioning).
|
|
--
|
|
-- Only the type instance layouter makes use of this filter currently, but
|
|
-- it might make sense to apply it more aggressively or make it the default -
|
|
-- I believe that most of the time we branch on the existence of comments, we
|
|
-- only care about "regular" comments. We simply did not need the distinction
|
|
-- because "irregular" comments are not that common outside of type/data decls.
|
|
-- isRegularComment :: (ExactPrint.Comment, ExactPrint.DeltaPos) -> Bool
|
|
-- isRegularComment = (== Nothing) . ExactPrint.Types.commentOrigin . fst
|
|
|
|
astConnectedComments
|
|
:: (Data ann, Data ast)
|
|
=> GHC.GenLocated ann ast
|
|
-> [LEpaComment]
|
|
astConnectedComments =
|
|
SYB.listify (\(_ :: LEpaComment) -> True)
|
|
-- anns <- filterAnns ast <$> mAsk
|
|
-- pure $ extractAllComments =<< Map.elems anns
|
|
--
|
|
-- hasAnyCommentsPrior :: Data ast => GHC.Located ast -> ToBriDocM Bool
|
|
-- hasAnyCommentsPrior ast = astAnn ast <&> \case
|
|
-- Nothing -> False
|
|
-- Just (ExactPrint.Types.Ann _ priors _ _ _ _) -> not $ null priors
|
|
|
|
-- hasAnyRegularCommentsRest :: Data ast => GHC.Located ast -> ToBriDocM Bool
|
|
-- hasAnyRegularCommentsRest ast = astAnn ast <&> \case
|
|
-- Nothing -> False
|
|
-- Just ann -> any isRegularComment (extractRestComments ann)
|
|
|
|
-- hasAnnKeywordComment
|
|
-- :: GHC.LocatedA ast -> AnnKeywordId -> Bool
|
|
-- hasAnnKeywordComment (L (GHC.SrcSpanAnn ann _) _) annKeyword = False -- _ ann
|
|
-- Nothing -> False
|
|
-- Just ann -> any hasK (extractAllComments ann)
|
|
-- where hasK = (== Just annKeyword) . ExactPrint.Types.commentOrigin . fst
|
|
|
|
-- new BriDoc stuff
|
|
|
|
allocateNode
|
|
:: MonadMultiState NodeAllocIndex m => BriDocWrapped -> m BriDocNumbered
|
|
allocateNode bd = do
|
|
i <- allocNodeIndex
|
|
return (i, bd)
|
|
|
|
allocNodeIndex :: MonadMultiState NodeAllocIndex m => m Int
|
|
allocNodeIndex = do
|
|
NodeAllocIndex i <- mGet
|
|
mSet $ NodeAllocIndex (i + 1)
|
|
return i
|
|
|
|
docEmpty :: ToBriDocM BriDocNumbered
|
|
docEmpty = allocateNode BDEmpty
|
|
|
|
docLit :: Text -> ToBriDocM BriDocNumbered
|
|
docLit t = allocateNode $ BDLit t
|
|
|
|
docLitS :: String -> ToBriDocM BriDocNumbered
|
|
docLitS s = allocateNode $ BDLit $ Text.pack s
|
|
|
|
docExt
|
|
:: (ExactPrint.ExactPrint (GenLocated l ast))
|
|
=> GenLocated l ast
|
|
-> Bool
|
|
-> ToBriDocM BriDocNumbered
|
|
docExt x shouldAddComment = allocateNode $ BDExternal
|
|
-- (ExactPrint.Types.mkAnnKey x)
|
|
-- (foldedAnnKeys x)
|
|
shouldAddComment
|
|
(Text.pack
|
|
$ List.dropWhile ((==) '\n')
|
|
$ ExactPrint.exactPrint
|
|
$ ExactPrint.makeDeltaAst x
|
|
)
|
|
|
|
docAlt :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered
|
|
docAlt l = allocateNode . BDAlt =<< sequence l
|
|
|
|
newtype CollectAltM a = CollectAltM (Writer.Writer [ToBriDocM BriDocNumbered] a)
|
|
deriving (Functor, Applicative, Monad)
|
|
|
|
addAlternativeCond :: Bool -> ToBriDocM BriDocNumbered -> CollectAltM ()
|
|
addAlternativeCond cond doc = when cond (addAlternative doc)
|
|
|
|
addAlternative :: ToBriDocM BriDocNumbered -> CollectAltM ()
|
|
addAlternative = CollectAltM . Writer.tell . (: [])
|
|
|
|
runFilteredAlternative :: CollectAltM () -> ToBriDocM BriDocNumbered
|
|
runFilteredAlternative (CollectAltM action) = docAlt $ Writer.execWriter action
|
|
|
|
|
|
docSeq :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered
|
|
docSeq [] = docEmpty
|
|
docSeq l = allocateNode . BDSeq =<< sequence l
|
|
|
|
docSeqSep :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered
|
|
docSeqSep = docSeq . List.intersperse docSeparator
|
|
|
|
docLines :: [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered
|
|
docLines l = allocateNode . BDLines =<< sequence l
|
|
|
|
docCols :: ColSig -> [ToBriDocM BriDocNumbered] -> ToBriDocM BriDocNumbered
|
|
docCols sig l = allocateNode . BDCols sig =<< sequence l
|
|
|
|
docAddBaseY :: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docAddBaseY ind bdm = allocateNode . BDAddBaseY ind =<< bdm
|
|
|
|
docSetBaseY :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docSetBaseY bdm = do
|
|
bd <- bdm
|
|
-- the order here is important so that these two nodes can be treated
|
|
-- properly over at `transformAlts`.
|
|
allocateNode $ BDBaseYPushCur bd
|
|
|
|
docSetIndentLevel :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docSetIndentLevel bdm = do
|
|
bd <- bdm
|
|
n1 <- allocateNode $ BDIndentLevelPushCur bd
|
|
n2 <- allocateNode $ BDIndentLevelPop n1
|
|
return n2
|
|
|
|
docSetBaseAndIndent :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docSetBaseAndIndent = docSetBaseY . docSetIndentLevel
|
|
|
|
docSeparator :: ToBriDocM BriDocNumbered
|
|
docSeparator = allocateNode BDSeparator
|
|
|
|
docNonBottomSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docNonBottomSpacing bdm = allocateNode . BDForceAlt (NonBottomSpacing False) =<< bdm
|
|
|
|
docNonBottomSpacingS :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docNonBottomSpacingS bdm = allocateNode . BDForceAlt (NonBottomSpacing True) =<< bdm
|
|
|
|
docSetParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docSetParSpacing bdm = allocateNode . BDForceAlt SetParSpacing =<< bdm
|
|
|
|
docForceParSpacing :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
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
|
|
|
|
appSep :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
appSep x = docSeq [x, docSeparator]
|
|
|
|
docCommaSep :: ToBriDocM BriDocNumbered
|
|
docCommaSep = appSep $ docLit $ Text.pack ","
|
|
|
|
docParenLSep :: ToBriDocM BriDocNumbered
|
|
docParenLSep = appSep docParenL
|
|
|
|
-- TODO: we don't make consistent use of these (yet). However, I think the
|
|
-- most readable approach overall might be something else: define
|
|
-- `lit = docLit . Text.pack` and `prepSep = docSeq [docSeparator, x]`.
|
|
-- I think those two would make the usage most readable.
|
|
-- lit "(" and appSep (lit "(") are understandable and short without
|
|
-- introducing a new top-level binding for all types of parentheses.
|
|
docParenL :: ToBriDocM BriDocNumbered
|
|
docParenL = docLit $ Text.pack "("
|
|
|
|
docParenR :: ToBriDocM BriDocNumbered
|
|
docParenR = docLit $ Text.pack ")"
|
|
|
|
docParenHashLSep :: ToBriDocM BriDocNumbered
|
|
docParenHashLSep = docSeq [docLit $ Text.pack "(#", docSeparator]
|
|
|
|
docParenHashRSep :: ToBriDocM BriDocNumbered
|
|
docParenHashRSep = docSeq [docSeparator, docLit $ Text.pack "#)"]
|
|
|
|
docBracketL :: ToBriDocM BriDocNumbered
|
|
docBracketL = docLit $ Text.pack "["
|
|
|
|
docBracketR :: ToBriDocM BriDocNumbered
|
|
docBracketR = docLit $ Text.pack "]"
|
|
|
|
|
|
docTick :: ToBriDocM BriDocNumbered
|
|
docTick = docLit $ Text.pack "'"
|
|
|
|
|
|
docPar
|
|
:: ToBriDocM BriDocNumbered
|
|
-> ToBriDocM BriDocNumbered
|
|
-> ToBriDocM BriDocNumbered
|
|
docPar lineM indentedM = do
|
|
line <- lineM
|
|
indented <- indentedM
|
|
allocateNode $ BDPar BrIndentNone line indented
|
|
|
|
docForceSingleline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docForceSingleline bdm = allocateNode . BDForceAlt ForceSingleline =<< bdm
|
|
|
|
docForceMultiline :: ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docForceMultiline bdm = allocateNode . BDForceAlt ForceMultiline =<< bdm
|
|
|
|
docEnsureIndent
|
|
:: BrIndent -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docEnsureIndent ind mbd = mbd >>= \bd -> allocateNode $ BDEnsureIndent ind bd
|
|
|
|
docAddEntryDelta :: GHC.DeltaPos -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docAddEntryDelta dp bdm = do
|
|
bd <- bdm
|
|
allocateNode (BDEntryDelta dp bd)
|
|
|
|
docFlushRemaining :: FastString -> ToBriDocM BriDocNumbered -> ToBriDocM BriDocNumbered
|
|
docFlushRemaining fileThing = docFlushCommsPost False
|
|
(Just $ GHC.mkRealSrcLoc fileThing 999999 999999)
|
|
|
|
-- CLASS DocHandleComms --------------------------------------------------------
|
|
|
|
class DocHandleComms ann a where
|
|
docHandleComms :: HasCallStack => ann -> a -> a
|
|
|
|
instance DocHandleComms [LEpaComment] (ToBriDocM BriDocNumbered) where
|
|
docHandleComms comms bdm = do
|
|
bd <- bdm
|
|
i1 <- allocNodeIndex
|
|
pure (i1, BDQueueComments comms bd)
|
|
|
|
instance DocHandleComms (EpAnn a) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms epAnn bdm = case epAnn of
|
|
EpAnn anch _ (EpaComments []) -> do
|
|
bd <- bdm
|
|
i1 <- allocNodeIndex
|
|
pure
|
|
(i1, BDFlushCommentsPrior (GHC.realSrcSpanStart $ GHC.anchor anch) bd)
|
|
EpAnn anch _ (EpaComments comms) -> do
|
|
bd <- bdm
|
|
i1 <- allocNodeIndex
|
|
i2 <- allocNodeIndex
|
|
pure
|
|
( i1
|
|
, BDFlushCommentsPrior (GHC.realSrcSpanStart $ GHC.anchor anch)
|
|
(i2, BDQueueComments (reverse comms) bd)
|
|
)
|
|
EpAnn anch _ (EpaCommentsBalanced commsB commsA) -> do
|
|
bd <- bdm
|
|
i1 <- allocNodeIndex
|
|
i2 <- allocNodeIndex
|
|
pure
|
|
( i1
|
|
, BDQueueComments
|
|
(reverse commsB ++ reverse commsA)
|
|
( i2
|
|
, BDFlushCommentsPrior (GHC.realSrcSpanStart $ GHC.anchor anch) bd
|
|
)
|
|
)
|
|
EpAnnNotUsed -> bdm
|
|
|
|
instance DocHandleComms (Maybe (EpAnn a)) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms Nothing = id
|
|
docHandleComms (Just epAnn) = docHandleComms epAnn
|
|
|
|
instance DocHandleComms (GHC.RealSrcSpan) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms loc = docHandleComms (GHC.realSrcSpanStart loc)
|
|
|
|
instance DocHandleComms (GHC.RealSrcLoc) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms loc bdm = do
|
|
bd <- bdm
|
|
i1 <- allocNodeIndex
|
|
pure (i1, BDFlushCommentsPrior loc bd)
|
|
|
|
instance DocHandleComms (Maybe GHC.RealSrcSpan) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms Nothing bdm = bdm
|
|
docHandleComms (Just loc) bdm = docHandleComms loc bdm
|
|
|
|
instance DocHandleComms (Maybe GHC.RealSrcLoc) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms Nothing bdm = bdm
|
|
docHandleComms (Just loc) bdm = docHandleComms loc bdm
|
|
|
|
instance DocHandleComms (GHC.SrcLoc) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (GHC.RealSrcLoc loc _) bdm = docHandleComms loc bdm
|
|
docHandleComms (GHC.UnhelpfulLoc _) bdm = bdm
|
|
|
|
instance DocHandleComms (GHC.LocatedA ast) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (L (GHC.SrcSpanAnn epAnn span) _) bdm = case span of
|
|
GHC.RealSrcSpan s _ -> docHandleComms s $ docHandleComms epAnn bdm
|
|
GHC.UnhelpfulSpan _ -> bdm
|
|
|
|
instance DocHandleComms (GHC.LocatedL ast) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (L (GHC.SrcSpanAnn epAnn span) _) bdm = case span of
|
|
GHC.RealSrcSpan s _ -> docHandleComms s $ docHandleComms epAnn bdm
|
|
GHC.UnhelpfulSpan _ -> bdm
|
|
|
|
instance DocHandleComms (GHC.LocatedC ast) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (L (GHC.SrcSpanAnn epAnn span) _) bdm = case span of
|
|
GHC.RealSrcSpan s _ -> docHandleComms s $ docHandleComms epAnn bdm
|
|
GHC.UnhelpfulSpan _ -> bdm
|
|
|
|
instance DocHandleComms (GHC.LocatedN ast) (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (L (GHC.SrcSpanAnn epAnn span) _) bdm = case span of
|
|
GHC.RealSrcSpan s _ -> docHandleComms s $ docHandleComms epAnn bdm
|
|
GHC.UnhelpfulSpan _ -> bdm
|
|
|
|
|
|
instance DocHandleComms ann (ToBriDocM BriDocNumbered)
|
|
=> DocHandleComms ann (ToBriDocM [BriDocNumbered]) where
|
|
docHandleComms ann bdm = do
|
|
x <- bdm
|
|
case x of
|
|
[] -> error "docHandleComms empty list"
|
|
-- TODO92
|
|
-- do
|
|
-- el <- docHandleComms ann docEmpty
|
|
-- pure [el]
|
|
(bd1:bdR) -> do
|
|
bd1' <- docHandleComms ann (pure bd1)
|
|
pure (bd1':bdR)
|
|
|
|
instance DocHandleComms ann (ToBriDocM BriDocNumbered)
|
|
=> DocHandleComms ann (ToBriDocM (Seq BriDocNumbered))
|
|
where
|
|
docHandleComms ast bdsm = do
|
|
bds <- bdsm
|
|
case Seq.viewl bds of
|
|
Seq.EmptyL -> return Seq.empty -- TODO: this might be bad. maybe. then again, not really. well.
|
|
bd1 Seq.:< rest -> do
|
|
bd1' <- docHandleComms ast (return bd1)
|
|
return $ bd1' Seq.<| rest
|
|
|
|
|
|
instance DocHandleComms ann (ToBriDocM BriDocNumbered)
|
|
=> DocHandleComms ann [ToBriDocM BriDocNumbered] where
|
|
docHandleComms ann bdms = do
|
|
case bdms of
|
|
[] -> error "docHandleComms empty list"
|
|
-- [docHandleComms ann docEmpty]
|
|
(bd1:bdR) -> (docHandleComms ann bd1:bdR)
|
|
|
|
instance DocHandleComms GHC.EpaLocation (ToBriDocM BriDocNumbered) where
|
|
docHandleComms loc bdm = docHandleComms (GHC.epaLocationRealSrcSpan loc) bdm
|
|
|
|
instance DocHandleComms GHC.SrcSpan (ToBriDocM BriDocNumbered) where
|
|
docHandleComms (GHC.RealSrcSpan s _) bdm = docHandleComms s bdm
|
|
docHandleComms (GHC.UnhelpfulSpan _) bdm = bdm
|
|
|
|
|
|
-- CLASS ObtainAnnPos ----------------------------------------------------------
|
|
|
|
class ObtainAnnPos key ann where
|
|
obtainAnnPos :: ann -> key -> Maybe GHC.RealSrcSpan
|
|
|
|
instance ObtainAnnPos AnnKeywordId GHC.AddEpAnn where
|
|
obtainAnnPos (GHC.AddEpAnn eKW loc) kw = if eKW == kw
|
|
then Just (GHC.epaLocationRealSrcSpan loc)
|
|
else Nothing
|
|
|
|
instance ObtainAnnPos AnnKeywordId (EpAnn GHC.AnnsModule) where
|
|
obtainAnnPos = \case
|
|
EpAnnNotUsed -> \_kw -> Nothing
|
|
EpAnn _ (GHC.AnnsModule l annList) _ -> \kw ->
|
|
obtainAnnPos l kw <|> obtainAnnPos annList kw
|
|
|
|
instance ObtainAnnPos AnnKeywordId (Maybe GHC.AddEpAnn) where
|
|
obtainAnnPos Nothing _ = Nothing
|
|
obtainAnnPos (Just addEpAnn) kw = obtainAnnPos addEpAnn kw
|
|
|
|
instance ObtainAnnPos AnnKeywordId [GHC.AddEpAnn] where
|
|
obtainAnnPos list kw =
|
|
case [ loc | GHC.AddEpAnn eKW loc <- list, eKW == kw ] of
|
|
[] -> Nothing
|
|
locs -> Just (GHC.epaLocationRealSrcSpan $ minimum locs)
|
|
|
|
instance ObtainAnnPos AnnKeywordId (EpAnn [GHC.AddEpAnn]) where
|
|
obtainAnnPos EpAnnNotUsed _kw = Nothing
|
|
obtainAnnPos (EpAnn _ list _) kw = obtainAnnPos list kw
|
|
|
|
instance ObtainAnnPos AnnKeywordId (EpAnn GHC.AnnList) where
|
|
obtainAnnPos = \case
|
|
EpAnnNotUsed -> \_kw -> Nothing
|
|
EpAnn _ annList _ -> \kw -> obtainAnnPos annList kw
|
|
|
|
instance ObtainAnnPos AnnKeywordId GHC.AnnList where
|
|
obtainAnnPos (GHC.AnnList _ op cl addEpAnn _) kw =
|
|
obtainAnnPos op kw <|> obtainAnnPos cl kw <|> obtainAnnPos addEpAnn kw
|
|
|
|
instance ObtainAnnPos AnnKeywordId (EpAnn GHC.GrhsAnn) where
|
|
obtainAnnPos = \case
|
|
EpAnn _ (GHC.GrhsAnn _ addEpAnn) _ -> obtainAnnPos addEpAnn
|
|
EpAnnNotUsed -> \_kw -> Nothing
|
|
|
|
instance ObtainAnnPos AnnKeywordId GHC.SrcSpanAnnL where
|
|
obtainAnnPos = \case
|
|
GHC.SrcSpanAnn epAnn _ -> obtainAnnPos epAnn
|
|
|
|
class ObtainAnnPos AnnKeywordId ann => ObtainAnnDeltaPos ann where
|
|
obtainAnnDeltaPos :: ann -> AnnKeywordId -> Maybe GHC.DeltaPos
|
|
|
|
instance ObtainAnnDeltaPos (EpAnn GHC.AnnsModule) where
|
|
obtainAnnDeltaPos = \case
|
|
EpAnnNotUsed -> \_kw -> Nothing
|
|
EpAnn _ (GHC.AnnsModule l annList) epaComms -> \kw -> do
|
|
loc <- GHC.realSrcSpanStart
|
|
<$> (obtainAnnPos l kw <|> obtainAnnPos annList kw)
|
|
let pos = (GHC.srcLocLine loc, GHC.srcLocCol loc)
|
|
pure $ ExactPrint.pos2delta
|
|
(maximum $ (1, 1) :
|
|
[ ExactPrint.ss2posEnd $ GHC.anchor anch
|
|
| L anch _ <- case epaComms of
|
|
EpaCommentsBalanced cs1 cs2 -> cs1 ++ cs2
|
|
EpaComments cs -> cs
|
|
, let compPos = ExactPrint.ss2posEnd (GHC.anchor anch)
|
|
, compPos <= pos
|
|
]
|
|
)
|
|
pos
|
|
|
|
class DocFlushCommsPost ann a where
|
|
docFlushCommsPost :: Bool -> ann -> a -> a
|
|
|
|
instance DocFlushCommsPost (Maybe GHC.RealSrcLoc) (ToBriDocM BriDocNumbered) where
|
|
docFlushCommsPost shouldMark = \case
|
|
Nothing -> id
|
|
Just loc -> \bdm -> do
|
|
i1 <- allocNodeIndex
|
|
bd <- bdm
|
|
pure (i1, BDFlushCommentsPost loc shouldMark bd)
|
|
|
|
instance DocFlushCommsPost (Maybe GHC.RealSrcSpan) (ToBriDocM BriDocNumbered) where
|
|
docFlushCommsPost shouldMark = \case
|
|
Nothing -> id
|
|
Just loc -> \bdm -> do
|
|
i1 <- allocNodeIndex
|
|
bd <- bdm
|
|
pure (i1, BDFlushCommentsPost (GHC.realSrcSpanEnd loc) shouldMark bd)
|
|
|
|
instance DocFlushCommsPost GHC.EpaLocation (ToBriDocM BriDocNumbered) where
|
|
docFlushCommsPost shouldMark epaLocation =
|
|
docFlushCommsPost shouldMark (Just $ GHC.epaLocationRealSrcSpan epaLocation)
|
|
|
|
instance DocFlushCommsPost ann (ToBriDocM BriDocNumbered)
|
|
=> DocFlushCommsPost ann (ToBriDocM [BriDocNumbered]) where
|
|
docFlushCommsPost shouldMark loc bdm = do
|
|
bds <- bdm
|
|
case bds of
|
|
[] -> do
|
|
e <- docFlushCommsPost shouldMark loc docEmpty
|
|
pure [e]
|
|
_ -> do
|
|
e <- docFlushCommsPost shouldMark loc (pure $ List.last bds)
|
|
pure (List.init bds ++ [e])
|
|
|
|
instance DocFlushCommsPost (Maybe GHC.RealSrcLoc) a
|
|
=> DocFlushCommsPost (LocatedA ast) a where
|
|
docFlushCommsPost shouldMark (L ann _) =
|
|
docFlushCommsPost shouldMark $ case GHC.locA ann of
|
|
GHC.RealSrcSpan s _ -> Just $ GHC.realSrcSpanEnd s
|
|
GHC.UnhelpfulSpan{} -> Nothing
|
|
|
|
unknownNodeError
|
|
:: (Data a, Data (GHC.Anno a), Outputable (GHC.Anno a))
|
|
=> String
|
|
-> GHC.XRec GhcPs a
|
|
-> ToBriDocM BriDocNumbered
|
|
unknownNodeError infoStr ast = do
|
|
mTell [ErrorUnknownNode infoStr ast]
|
|
docLit $ Text.pack "{- BRITTANY ERROR UNHANDLED SYNTACTICAL CONSTRUCT -}"
|
|
|
|
spacifyDocs :: [ToBriDocM BriDocNumbered] -> [ToBriDocM BriDocNumbered]
|
|
spacifyDocs [] = []
|
|
spacifyDocs ds = fmap appSep (List.init ds) ++ [List.last ds]
|
|
|
|
shareDoc :: ToBriDocM a -> ToBriDocM (ToBriDocM a)
|
|
shareDoc = fmap pure
|
|
|
|
|
|
obtainListElemStartCommaLocs
|
|
:: LocatedA ast -> (Maybe GHC.RealSrcLoc, Maybe GHC.RealSrcLoc)
|
|
obtainListElemStartCommaLocs = \case
|
|
L (GHC.SrcSpanAnn elemEpAnn _) _ -> case elemEpAnn of
|
|
EpAnn anch (GHC.AnnListItem [item]) _ ->
|
|
( Just $ GHC.realSrcSpanStart $ GHC.anchor anch
|
|
-- yes, we want `realSrcSpanStart span2` here, but have it flow
|
|
-- to the end of bd. We want any comments _before_ the _start_
|
|
-- of the comma to be inserted _after_ the element.
|
|
, Just $ GHC.realSrcSpanStart $ case item of
|
|
GHC.AddCommaAnn span -> GHC.epaLocationRealSrcSpan span
|
|
GHC.AddSemiAnn span -> GHC.epaLocationRealSrcSpan span
|
|
GHC.AddVbarAnn span -> GHC.epaLocationRealSrcSpan span
|
|
GHC.AddRarrowAnn span -> GHC.epaLocationRealSrcSpan span
|
|
GHC.AddRarrowAnnU span -> GHC.epaLocationRealSrcSpan span
|
|
GHC.AddLollyAnnU span -> GHC.epaLocationRealSrcSpan span
|
|
)
|
|
EpAnn anch _ _ -> (Just $ GHC.realSrcSpanStart $ GHC.anchor anch, Nothing)
|
|
EpAnnNotUsed -> (Nothing, Nothing)
|
|
|
|
|
|
docHandleListElemComms
|
|
:: (LocatedA ast -> ToBriDocM BriDocNumbered)
|
|
-> LocatedA ast
|
|
-> ToBriDocM BriDocNumbered
|
|
docHandleListElemComms layouter e = case obtainListElemStartCommaLocs e of
|
|
(posStart, posComma) ->
|
|
docHandleComms posStart $ docFlushCommsPost True posComma $ layouter e
|
|
|
|
docHandleListElemCommsProperPost
|
|
:: (LocatedA ast -> ToBriDocM BriDocNumbered)
|
|
-> [LocatedA ast]
|
|
-> ToBriDocM
|
|
[(Maybe GHC.RealSrcLoc, LocatedA ast, ToBriDocM BriDocNumbered)]
|
|
docHandleListElemCommsProperPost layouter es = case es of
|
|
[] -> pure []
|
|
(e1 : rest) -> case obtainListElemStartCommaLocs e1 of
|
|
(posStart, posComma) -> do
|
|
res <- go posComma rest
|
|
pure
|
|
$ ( Nothing
|
|
, e1
|
|
, docHandleComms posStart $ layouter e1
|
|
)
|
|
: res
|
|
where
|
|
go _intoComma [] = pure []
|
|
go intoComma (e1 : rest) = case obtainListElemStartCommaLocs e1 of
|
|
(posStart, posComma) -> do
|
|
res <- go posComma rest
|
|
pure
|
|
$ ( intoComma
|
|
, e1
|
|
, docHandleComms posStart $ layouter e1
|
|
)
|
|
: res
|
|
|
|
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
|