#define INSERTTRACES 0
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TypeApplications #-}
#if !INSERTTRACES
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
#endif
module Language.Haskell.Brittany.Internal.BackendUtils
( layoutWriteAppend
, layoutWriteAppendMultiline
, layoutWriteNewlineBlock
, layoutWriteNewline
, layoutWriteEnsureNewlineBlock
, layoutWriteEnsureBlock
, layoutWithAddBaseCol
, layoutWithAddBaseColBlock
, layoutWithAddBaseColN
, layoutWithAddBaseColNBlock
, layoutBaseYPushCur
, layoutBaseYPop
, layoutIndentLevelPushCur
, layoutIndentLevelPop
, layoutWriteEnsureAbsoluteN
, layoutAddSepSpace
, layoutSetCommentCol
, layoutMoveToCommentPos
, layoutIndentRestorePostComment
, moveToExactAnn
, ppmMoveToExactLoc
, layoutWritePriorComments
, layoutWritePostComments
, layoutRemoveIndentLevelLinger
)
where
#include "prelude.inc"
import Language.Haskell.Brittany.Internal.Types
import Language.Haskell.Brittany.Internal.Config.Types
import Language.Haskell.GHC.ExactPrint.Types ( AnnKey
, Annotation
, KeywordId
)
import qualified Data.Text.Lazy.Builder as Text.Builder
import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint
import Language.Haskell.Brittany.Internal.Utils
import GHC ( Located, GenLocated(L), moduleNameString )
traceLocal
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m, Show a)
=> a
-> m ()
#if INSERTTRACES
traceLocal x = do
mGet >>= tellDebugMessShow @LayoutState
tellDebugMessShow x
#else
traceLocal _ = return ()
#endif
layoutWriteAppend
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Text
-> m ()
layoutWriteAppend t = do
traceLocal ("layoutWriteAppend", t)
state <- mGet
case _lstate_curYOrAddNewline state of
Right i -> do
#if INSERTTRACES
tellDebugMessShow (" inserted newlines: ", i)
#endif
replicateM_ i $ mTell $ Text.Builder.fromString $ "\n"
Left{} -> do
#if INSERTTRACES
tellDebugMessShow (" inserted no newlines")
#endif
return ()
let spaces = case _lstate_addSepSpace state of
Just i -> i
Nothing -> 0
#if INSERTTRACES
tellDebugMessShow (" inserted spaces: ", spaces)
#endif
mTell $ Text.Builder.fromText $ Text.pack (replicate spaces ' ')
mTell $ Text.Builder.fromText $ t
mModify $ \s -> s
{ _lstate_curYOrAddNewline = Left $ case _lstate_curYOrAddNewline s of
Left c -> c + Text.length t + spaces
Right{} -> Text.length t + spaces
, _lstate_addSepSpace = Nothing
}
layoutWriteAppendSpaces
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Int
-> m ()
layoutWriteAppendSpaces i = do
traceLocal ("layoutWriteAppendSpaces", i)
unless (i == 0) $ do
state <- mGet
mSet $ state
{ _lstate_addSepSpace = Just $ maybe i (+i) $ _lstate_addSepSpace state
}
layoutWriteAppendMultiline
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Text
-> m ()
layoutWriteAppendMultiline t = do
traceLocal ("layoutWriteAppendMultiline", t)
case Text.lines t of
[] -> layoutWriteAppend t
(l:lr) -> do
layoutWriteAppend l
lr `forM_` \x -> do
layoutWriteNewline
layoutWriteAppend x
layoutWriteNewlineBlock
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> m ()
layoutWriteNewlineBlock = do
traceLocal ("layoutWriteNewlineBlock")
state <- mGet
mSet $ state { _lstate_curYOrAddNewline = Right 1
, _lstate_addSepSpace = Just $ lstate_baseY state
}
layoutSetCommentCol
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutSetCommentCol = do
state <- mGet
let col = case _lstate_curYOrAddNewline state of
Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)
Right{} -> lstate_baseY state
traceLocal ("layoutSetCommentCol", col)
unless (Data.Maybe.isJust $ _lstate_commentCol state)
$ mSet state { _lstate_commentCol = Just col }
layoutMoveToCommentPos
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Int
-> Int
-> m ()
layoutMoveToCommentPos y x = do
traceLocal ("layoutMoveToCommentPos", y, x)
state <- mGet
mSet state
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
Left i -> if y == 0 then Left i else Right y
Right{} -> Right y
, _lstate_addSepSpace =
Just $ if Data.Maybe.isJust (_lstate_commentCol state)
then case _lstate_curYOrAddNewline state of
Left{} -> if y == 0 then x else _lstate_indLevelLinger state + x
Right{} -> _lstate_indLevelLinger state + x
else if y == 0 then x else _lstate_indLevelLinger state + x
, _lstate_commentCol =
Just $ case _lstate_commentCol state of
Just existing -> existing
Nothing -> case _lstate_curYOrAddNewline state of
Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)
Right{} -> lstate_baseY state
}
layoutWriteNewline
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> m ()
layoutWriteNewline = do
traceLocal ("layoutWriteNewline")
state <- mGet
mSet $ state
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
Left{} -> Right 1
Right i -> Right (i + 1)
, _lstate_addSepSpace = Nothing
}
layoutWriteEnsureNewlineBlock
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> m ()
layoutWriteEnsureNewlineBlock = do
traceLocal ("layoutWriteEnsureNewlineBlock")
state <- mGet
mSet $ state
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
Left{} -> Right 1
Right i -> Right $ max 1 i
, _lstate_addSepSpace = Just $ lstate_baseY state
, _lstate_commentCol = Nothing
}
layoutWriteEnsureAbsoluteN
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Int
-> m ()
layoutWriteEnsureAbsoluteN n = do
state <- mGet
let diff = case _lstate_curYOrAddNewline state of
Left i -> n - i
Right{} -> n
traceLocal ("layoutWriteEnsureAbsoluteN", n, diff)
when (diff > 0) $ do
mSet $ state { _lstate_addSepSpace = Just diff
}
layoutBaseYPushInternal
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m)
=> Int
-> m ()
layoutBaseYPushInternal i = do
traceLocal ("layoutBaseYPushInternal", i)
mModify $ \s -> s { _lstate_baseYs = i : _lstate_baseYs s }
layoutBaseYPopInternal
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutBaseYPopInternal = do
traceLocal ("layoutBaseYPopInternal")
mModify $ \s -> s { _lstate_baseYs = List.tail $ _lstate_baseYs s }
layoutIndentLevelPushInternal
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m)
=> Int
-> m ()
layoutIndentLevelPushInternal i = do
traceLocal ("layoutIndentLevelPushInternal", i)
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
, _lstate_indLevels = i : _lstate_indLevels s
}
layoutIndentLevelPopInternal
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutIndentLevelPopInternal = do
traceLocal ("layoutIndentLevelPopInternal")
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
, _lstate_indLevels = List.tail $ _lstate_indLevels s
}
layoutRemoveIndentLevelLinger :: ( MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
) => m ()
layoutRemoveIndentLevelLinger = do
#if INSERTTRACES
tellDebugMessShow ("layoutRemoveIndentLevelLinger")
#endif
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
}
layoutWithAddBaseCol
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiReader Config m
, MonadMultiWriter (Seq String) m
)
=> m ()
-> m ()
layoutWithAddBaseCol m = do
#if INSERTTRACES
tellDebugMessShow ("layoutWithAddBaseCol")
#endif
amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
state <- mGet
layoutBaseYPushInternal $ lstate_baseY state + amount
m
layoutBaseYPopInternal
layoutWithAddBaseColBlock
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiReader Config m
, MonadMultiWriter (Seq String) m
)
=> m ()
-> m ()
layoutWithAddBaseColBlock m = do
#if INSERTTRACES
tellDebugMessShow ("layoutWithAddBaseColBlock")
#endif
amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
state <- mGet
layoutBaseYPushInternal $ lstate_baseY state + amount
layoutWriteEnsureBlock
m
layoutBaseYPopInternal
layoutWithAddBaseColNBlock
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Int
-> m ()
-> m ()
layoutWithAddBaseColNBlock amount m = do
traceLocal ("layoutWithAddBaseColNBlock", amount)
state <- mGet
layoutBaseYPushInternal $ lstate_baseY state + amount
layoutWriteEnsureBlock
m
layoutBaseYPopInternal
layoutWriteEnsureBlock
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> m ()
layoutWriteEnsureBlock = do
traceLocal ("layoutWriteEnsureBlock")
state <- mGet
let
diff = case (_lstate_addSepSpace state, _lstate_curYOrAddNewline state) of
(Nothing, Left i ) -> lstate_baseY state - i
(Nothing, Right{}) -> lstate_baseY state
(Just sp, Left i ) -> max sp (lstate_baseY state - i)
(Just sp, Right{}) -> max sp (lstate_baseY state)
when (diff > 0) $ do
mSet $ state { _lstate_addSepSpace = Just $ diff }
layoutWithAddBaseColN
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Int
-> m ()
-> m ()
layoutWithAddBaseColN amount m = do
#if INSERTTRACES
tellDebugMessShow ("layoutWithAddBaseColN", amount)
#endif
state <- mGet
layoutBaseYPushInternal $ lstate_baseY state + amount
m
layoutBaseYPopInternal
layoutBaseYPushCur
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutBaseYPushCur = do
traceLocal ("layoutBaseYPushCur")
state <- mGet
case _lstate_commentCol state of
Nothing ->
case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of
(Left i , Just j ) -> layoutBaseYPushInternal (i + j)
(Left i , Nothing) -> layoutBaseYPushInternal i
(Right{}, _ ) -> layoutBaseYPushInternal $ lstate_baseY state
Just cCol -> layoutBaseYPushInternal cCol
layoutBaseYPop
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutBaseYPop = do
traceLocal ("layoutBaseYPop")
layoutBaseYPopInternal
layoutIndentLevelPushCur
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutIndentLevelPushCur = do
traceLocal ("layoutIndentLevelPushCur")
state <- mGet
let y = case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of
(Left i , Just j ) -> i + j
(Left i , Nothing) -> i
(Right{}, Just j ) -> j
(Right{}, Nothing) -> 0
layoutIndentLevelPushInternal y
layoutIndentLevelPop
:: (MonadMultiState LayoutState m, MonadMultiWriter (Seq String) m) => m ()
layoutIndentLevelPop = do
traceLocal ("layoutIndentLevelPop")
layoutIndentLevelPopInternal
layoutRemoveIndentLevelLinger
layoutAddSepSpace :: (MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m)
=> m ()
layoutAddSepSpace = do
#if INSERTTRACES
tellDebugMessShow ("layoutAddSepSpace")
#endif
state <- mGet
mSet $ state
{ _lstate_addSepSpace = Just $ fromMaybe 1 $ _lstate_addSepSpace state }
moveToExactAnn
:: ( MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiReader (Map AnnKey Annotation) m
, MonadMultiWriter (Seq String) m
)
=> AnnKey
-> m ()
moveToExactAnn annKey = do
traceLocal ("moveToExactAnn", annKey)
anns <- mAsk
case Map.lookup annKey anns of
Nothing -> return ()
Just ann -> do
let ExactPrint.DP (y, _x) = ExactPrint.annEntryDelta ann
mModify $ \state ->
let upd = case _lstate_curYOrAddNewline state of
Left i -> if y == 0 then Left i else Right y
Right i -> Right $ max y i
in state
{ _lstate_curYOrAddNewline = upd
, _lstate_addSepSpace = if Data.Either.isRight upd
then
_lstate_commentCol state
<|> _lstate_addSepSpace state
<|> Just (lstate_baseY state)
else Nothing
, _lstate_commentCol = Nothing
}
ppmMoveToExactLoc
:: MonadMultiWriter Text.Builder.Builder m
=> ExactPrint.DeltaPos
-> m ()
ppmMoveToExactLoc (ExactPrint.DP (x, y)) = do
replicateM_ x $ mTell $ Text.Builder.fromString "\n"
replicateM_ y $ mTell $ Text.Builder.fromString " "
layoutWritePriorComments
:: ( Data.Data.Data ast
, MonadMultiWriter Text.Builder.Builder m
, MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m
)
=> Located ast
-> m ()
layoutWritePriorComments ast = do
mAnn <- do
state <- mGet
let key = ExactPrint.mkAnnKey ast
let anns = _lstate_comments state
let mAnn = ExactPrint.annPriorComments <$> Map.lookup key anns
mSet $ state
{ _lstate_comments =
Map.adjust (\ann -> ann { ExactPrint.annPriorComments = [] }) key anns
}
return mAnn
#if INSERTTRACES
tellDebugMessShow ("layoutWritePriorComments", ExactPrint.mkAnnKey ast, mAnn)
#endif
case mAnn of
Nothing -> return ()
Just priors -> do
when (not $ null priors) $ layoutSetCommentCol
priors `forM_` \( ExactPrint.Comment comment _ _
, ExactPrint.DP (x, y)
) -> do
replicateM_ x layoutWriteNewline
layoutWriteAppendSpaces y
layoutWriteAppendMultiline $ Text.pack $ comment
layoutWritePostComments :: (Data.Data.Data ast,
MonadMultiWriter Text.Builder.Builder m,
MonadMultiState LayoutState m
, MonadMultiWriter (Seq String) m)
=> Located ast -> m ()
layoutWritePostComments ast = do
mAnn <- do
state <- mGet
let key = ExactPrint.mkAnnKey ast
let anns = _lstate_comments state
let mAnn = ExactPrint.annFollowingComments <$> Map.lookup key anns
mSet $ state
{ _lstate_comments =
Map.adjust (\ann -> ann { ExactPrint.annFollowingComments = [] })
key
anns
}
return mAnn
#if INSERTTRACES
tellDebugMessShow ("layoutWritePostComments", ExactPrint.mkAnnKey ast, mAnn)
#endif
case mAnn of
Nothing -> return ()
Just posts -> do
when (not $ null posts) $ layoutSetCommentCol
posts `forM_` \( ExactPrint.Comment comment _ _
, ExactPrint.DP (x, y)
) -> do
replicateM_ x layoutWriteNewline
layoutWriteAppend $ Text.pack $ replicate y ' '
layoutWriteAppendMultiline $ Text.pack $ comment
layoutIndentRestorePostComment
:: ( MonadMultiState LayoutState m
, MonadMultiWriter Text.Builder.Builder m
, MonadMultiWriter (Seq String) m
)
=> m ()
layoutIndentRestorePostComment = do
state <- mGet
let mCommentCol = _lstate_commentCol state
let eCurYAddNL = _lstate_curYOrAddNewline state
#if INSERTTRACES
tellDebugMessShow ("layoutIndentRestorePostComment", mCommentCol)
#endif
mModify $ \s -> s { _lstate_commentCol = Nothing }
case (mCommentCol, eCurYAddNL) of
(Just commentCol, Left{}) -> do
layoutWriteEnsureNewlineBlock
layoutWriteEnsureAbsoluteN $ commentCol + fromMaybe 0 (_lstate_addSepSpace state)
_ -> return ()