523 lines
17 KiB
Haskell
523 lines
17 KiB
Haskell
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE NoImplicitPrelude #-}
|
|
|
|
module Language.Haskell.Brittany.Internal.BackendUtils where
|
|
|
|
import qualified Data.Data
|
|
import qualified Data.Either
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Maybe
|
|
import qualified Data.Semigroup as Semigroup
|
|
import qualified Data.Text as Text
|
|
import qualified Data.Text.Lazy.Builder as Text.Builder
|
|
import GHC (Located)
|
|
import qualified GHC.OldList as List
|
|
import Language.Haskell.Brittany.Internal.Config.Types
|
|
import Language.Haskell.Brittany.Internal.Prelude
|
|
import Language.Haskell.Brittany.Internal.PreludeUtils
|
|
import Language.Haskell.Brittany.Internal.Types
|
|
import Language.Haskell.Brittany.Internal.Utils
|
|
import Language.Haskell.GHC.ExactPrint.Types (AnnKey, Annotation)
|
|
import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint
|
|
|
|
|
|
|
|
traceLocal
|
|
:: (MonadMultiState LayoutState m)
|
|
=> a
|
|
-> m ()
|
|
traceLocal _ = return ()
|
|
|
|
|
|
layoutWriteAppend
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Text
|
|
-> m ()
|
|
layoutWriteAppend t = do
|
|
traceLocal ("layoutWriteAppend", t)
|
|
state <- mGet
|
|
case _lstate_curYOrAddNewline state of
|
|
Right i -> do
|
|
replicateM_ i $ mTell $ Text.Builder.fromString $ "\n"
|
|
Left{} -> do
|
|
return ()
|
|
let spaces = fromMaybe 0 $ _lstate_addSepSpace state
|
|
mTell $ Text.Builder.fromText $ Text.pack (replicate spaces ' ')
|
|
mTell $ Text.Builder.fromText $ t
|
|
mModify $ \s -> s
|
|
{ _lstate_curYOrAddNewline = Left $ case _lstate_curYOrAddNewline s of
|
|
Left c -> c + Text.length t + spaces
|
|
Right{} -> Text.length t + spaces
|
|
, _lstate_addSepSpace = Nothing
|
|
}
|
|
|
|
layoutWriteAppendSpaces
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Int
|
|
-> m ()
|
|
layoutWriteAppendSpaces i = do
|
|
traceLocal ("layoutWriteAppendSpaces", i)
|
|
unless (i == 0) $ do
|
|
state <- mGet
|
|
mSet $ state
|
|
{ _lstate_addSepSpace = Just $ maybe i (+i) $ _lstate_addSepSpace state
|
|
}
|
|
|
|
layoutWriteAppendMultiline
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> [Text]
|
|
-> m ()
|
|
layoutWriteAppendMultiline ts = do
|
|
traceLocal ("layoutWriteAppendMultiline", ts)
|
|
case ts of
|
|
[] -> layoutWriteAppend (Text.pack "") -- need to write empty, too.
|
|
(l:lr) -> do
|
|
layoutWriteAppend l
|
|
lr `forM_` \x -> do
|
|
layoutWriteNewline
|
|
layoutWriteAppend x
|
|
|
|
-- adds a newline and adds spaces to reach the base column.
|
|
layoutWriteNewlineBlock
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> m ()
|
|
layoutWriteNewlineBlock = do
|
|
traceLocal ("layoutWriteNewlineBlock")
|
|
state <- mGet
|
|
mSet $ state { _lstate_curYOrAddNewline = Right 1
|
|
, _lstate_addSepSpace = Just $ lstate_baseY state
|
|
}
|
|
|
|
-- layoutMoveToIndentCol :: ( MonadMultiState LayoutState m
|
|
-- , MonadMultiWriter (Seq String) m) => Int -> m ()
|
|
-- layoutMoveToIndentCol i = do
|
|
-- #if INSERTTRACES
|
|
-- tellDebugMessShow ("layoutMoveToIndentCol", i)
|
|
-- #endif
|
|
-- state <- mGet
|
|
-- mSet $ state
|
|
-- { _lstate_addSepSpace = Just
|
|
-- $ if isJust $ _lstate_addNewline state
|
|
-- then i
|
|
-- else _lstate_indLevelLinger state + i - _lstate_curY state
|
|
-- }
|
|
|
|
layoutSetCommentCol
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutSetCommentCol = do
|
|
state <- mGet
|
|
let col = case _lstate_curYOrAddNewline state of
|
|
Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)
|
|
Right{} -> lstate_baseY state
|
|
traceLocal ("layoutSetCommentCol", col)
|
|
unless (Data.Maybe.isJust $ _lstate_commentCol state)
|
|
$ mSet state { _lstate_commentCol = Just col }
|
|
|
|
-- This is also used to move to non-comments in a couple of places. Seems
|
|
-- to be harmless so far..
|
|
layoutMoveToCommentPos
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Int
|
|
-> Int
|
|
-> Int
|
|
-> m ()
|
|
layoutMoveToCommentPos y x commentLines = do
|
|
traceLocal ("layoutMoveToCommentPos", y, x, commentLines)
|
|
state <- mGet
|
|
mSet state
|
|
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
|
|
Left i -> if y == 0 then Left i else Right y
|
|
Right{} -> Right y
|
|
, _lstate_addSepSpace =
|
|
Just $ if Data.Maybe.isJust (_lstate_commentCol state)
|
|
then case _lstate_curYOrAddNewline state of
|
|
Left{} -> if y == 0 then x else _lstate_indLevelLinger state + x
|
|
Right{} -> _lstate_indLevelLinger state + x
|
|
else if y == 0 then x else _lstate_indLevelLinger state + x
|
|
, _lstate_commentCol =
|
|
Just $ case _lstate_commentCol state of
|
|
Just existing -> existing
|
|
Nothing -> case _lstate_curYOrAddNewline state of
|
|
Left i -> i + fromMaybe 0 (_lstate_addSepSpace state)
|
|
Right{} -> lstate_baseY state
|
|
, _lstate_commentNewlines =
|
|
_lstate_commentNewlines state + y + commentLines - 1
|
|
}
|
|
|
|
-- | does _not_ add spaces to again reach the current base column.
|
|
layoutWriteNewline
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> m ()
|
|
layoutWriteNewline = do
|
|
traceLocal ("layoutWriteNewline")
|
|
state <- mGet
|
|
mSet $ state
|
|
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
|
|
Left{} -> Right 1
|
|
Right i -> Right (i + 1)
|
|
, _lstate_addSepSpace = Nothing
|
|
}
|
|
|
|
_layoutResetCommentNewlines :: MonadMultiState LayoutState m => m ()
|
|
_layoutResetCommentNewlines = do
|
|
mModify $ \state -> state { _lstate_commentNewlines = 0 }
|
|
|
|
layoutWriteEnsureNewlineBlock
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> m ()
|
|
layoutWriteEnsureNewlineBlock = do
|
|
traceLocal ("layoutWriteEnsureNewlineBlock")
|
|
state <- mGet
|
|
mSet $ state
|
|
{ _lstate_curYOrAddNewline = case _lstate_curYOrAddNewline state of
|
|
Left{} -> Right 1
|
|
Right i -> Right $ max 1 i
|
|
, _lstate_addSepSpace = Just $ lstate_baseY state
|
|
, _lstate_commentCol = Nothing
|
|
}
|
|
|
|
layoutWriteEnsureAbsoluteN
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Int
|
|
-> m ()
|
|
layoutWriteEnsureAbsoluteN n = do
|
|
state <- mGet
|
|
let diff = case (_lstate_commentCol state, _lstate_curYOrAddNewline state) of
|
|
(Just c , _ ) -> n - c
|
|
(Nothing, Left i ) -> n - i
|
|
(Nothing, Right{}) -> n
|
|
traceLocal ("layoutWriteEnsureAbsoluteN", n, diff)
|
|
when (diff > 0) $ do
|
|
mSet $ state { _lstate_addSepSpace = Just diff -- this always sets to
|
|
-- at least (Just 1), so we won't
|
|
-- overwrite any old value in any
|
|
-- bad way.
|
|
}
|
|
|
|
layoutBaseYPushInternal
|
|
:: (MonadMultiState LayoutState m)
|
|
=> Int
|
|
-> m ()
|
|
layoutBaseYPushInternal i = do
|
|
traceLocal ("layoutBaseYPushInternal", i)
|
|
mModify $ \s -> s { _lstate_baseYs = i : _lstate_baseYs s }
|
|
|
|
layoutBaseYPopInternal
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutBaseYPopInternal = do
|
|
traceLocal ("layoutBaseYPopInternal")
|
|
mModify $ \s -> s { _lstate_baseYs = List.tail $ _lstate_baseYs s }
|
|
|
|
layoutIndentLevelPushInternal
|
|
:: (MonadMultiState LayoutState m)
|
|
=> Int
|
|
-> m ()
|
|
layoutIndentLevelPushInternal i = do
|
|
traceLocal ("layoutIndentLevelPushInternal", i)
|
|
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
|
|
, _lstate_indLevels = i : _lstate_indLevels s
|
|
}
|
|
|
|
layoutIndentLevelPopInternal
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutIndentLevelPopInternal = do
|
|
traceLocal ("layoutIndentLevelPopInternal")
|
|
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
|
|
, _lstate_indLevels = List.tail $ _lstate_indLevels s
|
|
}
|
|
|
|
layoutRemoveIndentLevelLinger :: ( MonadMultiState LayoutState m) => m ()
|
|
layoutRemoveIndentLevelLinger = do
|
|
mModify $ \s -> s { _lstate_indLevelLinger = lstate_indLevel s
|
|
}
|
|
|
|
layoutWithAddBaseCol
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
, MonadMultiReader Config m
|
|
)
|
|
=> m ()
|
|
-> m ()
|
|
layoutWithAddBaseCol m = do
|
|
amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
|
|
state <- mGet
|
|
layoutBaseYPushInternal $ lstate_baseY state + amount
|
|
m
|
|
layoutBaseYPopInternal
|
|
|
|
layoutWithAddBaseColBlock
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
, MonadMultiReader Config m
|
|
)
|
|
=> m ()
|
|
-> m ()
|
|
layoutWithAddBaseColBlock m = do
|
|
amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
|
|
state <- mGet
|
|
layoutBaseYPushInternal $ lstate_baseY state + amount
|
|
layoutWriteEnsureBlock
|
|
m
|
|
layoutBaseYPopInternal
|
|
|
|
layoutWithAddBaseColNBlock
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Int
|
|
-> m ()
|
|
-> m ()
|
|
layoutWithAddBaseColNBlock amount m = do
|
|
traceLocal ("layoutWithAddBaseColNBlock", amount)
|
|
state <- mGet
|
|
layoutBaseYPushInternal $ lstate_baseY state + amount
|
|
layoutWriteEnsureBlock
|
|
m
|
|
layoutBaseYPopInternal
|
|
|
|
layoutWriteEnsureBlock
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> m ()
|
|
layoutWriteEnsureBlock = do
|
|
traceLocal ("layoutWriteEnsureBlock")
|
|
state <- mGet
|
|
let
|
|
diff = case (_lstate_addSepSpace state, _lstate_curYOrAddNewline state) of
|
|
(Nothing, Left i ) -> lstate_baseY state - i
|
|
(Nothing, Right{}) -> lstate_baseY state
|
|
(Just sp, Left i ) -> max sp (lstate_baseY state - i)
|
|
(Just sp, Right{}) -> max sp (lstate_baseY state)
|
|
-- when (diff>0) $ layoutWriteNewlineBlock
|
|
when (diff > 0) $ do
|
|
mSet $ state { _lstate_addSepSpace = Just $ diff }
|
|
|
|
layoutWithAddBaseColN
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Int
|
|
-> m ()
|
|
-> m ()
|
|
layoutWithAddBaseColN amount m = do
|
|
state <- mGet
|
|
layoutBaseYPushInternal $ lstate_baseY state + amount
|
|
m
|
|
layoutBaseYPopInternal
|
|
|
|
layoutBaseYPushCur
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutBaseYPushCur = do
|
|
traceLocal ("layoutBaseYPushCur")
|
|
state <- mGet
|
|
case _lstate_commentCol state of
|
|
Nothing ->
|
|
case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of
|
|
(Left i , Just j ) -> layoutBaseYPushInternal (i + j)
|
|
(Left i , Nothing) -> layoutBaseYPushInternal i
|
|
(Right{}, _ ) -> layoutBaseYPushInternal $ lstate_baseY state
|
|
Just cCol -> layoutBaseYPushInternal cCol
|
|
|
|
layoutBaseYPop
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutBaseYPop = do
|
|
traceLocal ("layoutBaseYPop")
|
|
layoutBaseYPopInternal
|
|
|
|
layoutIndentLevelPushCur
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutIndentLevelPushCur = do
|
|
traceLocal ("layoutIndentLevelPushCur")
|
|
state <- mGet
|
|
let y = case (_lstate_curYOrAddNewline state, _lstate_addSepSpace state) of
|
|
(Left i , Just j ) -> i + j
|
|
(Left i , Nothing) -> i
|
|
(Right{}, Just j ) -> j
|
|
(Right{}, Nothing) -> 0
|
|
layoutIndentLevelPushInternal y
|
|
|
|
layoutIndentLevelPop
|
|
:: (MonadMultiState LayoutState m) => m ()
|
|
layoutIndentLevelPop = do
|
|
traceLocal ("layoutIndentLevelPop")
|
|
layoutIndentLevelPopInternal
|
|
-- why are comment indentations relative to the previous indentation on
|
|
-- the first node of an additional indentation, and relative to the outer
|
|
-- indentation after the last node of some indented stuff? sure does not
|
|
-- make sense.
|
|
layoutRemoveIndentLevelLinger
|
|
|
|
layoutAddSepSpace :: (MonadMultiState LayoutState m)
|
|
=> m ()
|
|
layoutAddSepSpace = do
|
|
state <- mGet
|
|
mSet $ state
|
|
{ _lstate_addSepSpace = Just $ fromMaybe 1 $ _lstate_addSepSpace state }
|
|
|
|
-- TODO: when refactoring is complete, the other version of this method
|
|
-- can probably be removed.
|
|
moveToExactAnn
|
|
:: ( MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
, MonadMultiReader (Map AnnKey Annotation) m
|
|
)
|
|
=> AnnKey
|
|
-> m ()
|
|
moveToExactAnn annKey = do
|
|
traceLocal ("moveToExactAnn", annKey)
|
|
anns <- mAsk
|
|
case Map.lookup annKey anns of
|
|
Nothing -> return ()
|
|
Just ann -> do
|
|
-- curY <- mGet <&> _lstate_curY
|
|
let ExactPrint.DP (y, _x) = ExactPrint.annEntryDelta ann
|
|
-- mModify $ \state -> state { _lstate_addNewline = Just x }
|
|
moveToY y
|
|
|
|
moveToY :: MonadMultiState LayoutState m => Int -> m ()
|
|
moveToY y = mModify $ \state ->
|
|
let upd = case _lstate_curYOrAddNewline state of
|
|
Left i -> if y == 0 then Left i else Right y
|
|
Right i -> Right $ max y i
|
|
in state
|
|
{ _lstate_curYOrAddNewline = upd
|
|
, _lstate_addSepSpace = if Data.Either.isRight upd
|
|
then
|
|
_lstate_commentCol state
|
|
<|> _lstate_addSepSpace state
|
|
<|> Just (lstate_baseY state)
|
|
else Nothing
|
|
, _lstate_commentCol = Nothing
|
|
}
|
|
-- fixMoveToLineByIsNewline :: MonadMultiState
|
|
-- LayoutState m => Int -> m Int
|
|
-- fixMoveToLineByIsNewline x = do
|
|
-- newLineState <- mGet <&> _lstate_isNewline
|
|
-- return $ if newLineState == NewLineStateYes
|
|
-- then x-1
|
|
-- else x
|
|
|
|
ppmMoveToExactLoc
|
|
:: MonadMultiWriter Text.Builder.Builder m
|
|
=> ExactPrint.DeltaPos
|
|
-> m ()
|
|
ppmMoveToExactLoc (ExactPrint.DP (x, y)) = do
|
|
replicateM_ x $ mTell $ Text.Builder.fromString "\n"
|
|
replicateM_ y $ mTell $ Text.Builder.fromString " "
|
|
|
|
-- TODO: update and use, or clean up. Currently dead code.
|
|
layoutWritePriorComments
|
|
:: ( Data.Data.Data ast
|
|
, MonadMultiWriter Text.Builder.Builder m
|
|
, MonadMultiState LayoutState m
|
|
)
|
|
=> Located ast
|
|
-> m ()
|
|
layoutWritePriorComments ast = do
|
|
mAnn <- do
|
|
state <- mGet
|
|
let key = ExactPrint.mkAnnKey ast
|
|
let anns = _lstate_comments state
|
|
let mAnn = ExactPrint.annPriorComments <$> Map.lookup key anns
|
|
mSet $ state
|
|
{ _lstate_comments =
|
|
Map.adjust (\ann -> ann { ExactPrint.annPriorComments = [] }) key anns
|
|
}
|
|
return mAnn
|
|
case mAnn of
|
|
Nothing -> return ()
|
|
Just priors -> do
|
|
unless (null priors) $ layoutSetCommentCol
|
|
priors `forM_` \( ExactPrint.Comment comment _ _
|
|
, ExactPrint.DP (x, y)
|
|
) -> do
|
|
replicateM_ x layoutWriteNewline
|
|
layoutWriteAppendSpaces y
|
|
layoutWriteAppendMultiline $ Text.lines $ Text.pack comment
|
|
|
|
-- TODO: update and use, or clean up. Currently dead code.
|
|
-- this currently only extracs from the `annsDP` field of Annotations.
|
|
-- per documentation, this seems sufficient, as the
|
|
-- "..`annFollowingComments` are only added by AST transformations ..".
|
|
layoutWritePostComments :: (Data.Data.Data ast,
|
|
MonadMultiWriter Text.Builder.Builder m,
|
|
MonadMultiState LayoutState m)
|
|
=> Located ast -> m ()
|
|
layoutWritePostComments ast = do
|
|
mAnn <- do
|
|
state <- mGet
|
|
let key = ExactPrint.mkAnnKey ast
|
|
let anns = _lstate_comments state
|
|
let mAnn = ExactPrint.annFollowingComments <$> Map.lookup key anns
|
|
mSet $ state
|
|
{ _lstate_comments =
|
|
Map.adjust (\ann -> ann { ExactPrint.annFollowingComments = [] })
|
|
key
|
|
anns
|
|
}
|
|
return mAnn
|
|
case mAnn of
|
|
Nothing -> return ()
|
|
Just posts -> do
|
|
unless (null posts) $ layoutSetCommentCol
|
|
posts `forM_` \( ExactPrint.Comment comment _ _
|
|
, ExactPrint.DP (x, y)
|
|
) -> do
|
|
replicateM_ x layoutWriteNewline
|
|
layoutWriteAppend $ Text.pack $ replicate y ' '
|
|
mModify $ \s -> s { _lstate_addSepSpace = Nothing }
|
|
layoutWriteAppendMultiline $ Text.lines $ Text.pack $ comment
|
|
|
|
layoutIndentRestorePostComment
|
|
:: ( MonadMultiState LayoutState m
|
|
, MonadMultiWriter Text.Builder.Builder m
|
|
)
|
|
=> m ()
|
|
layoutIndentRestorePostComment = do
|
|
state <- mGet
|
|
let mCommentCol = _lstate_commentCol state
|
|
let eCurYAddNL = _lstate_curYOrAddNewline state
|
|
mModify $ \s -> s { _lstate_commentCol = Nothing
|
|
, _lstate_commentNewlines = 0
|
|
}
|
|
case (mCommentCol, eCurYAddNL) of
|
|
(Just commentCol, Left{}) -> do
|
|
layoutWriteEnsureNewlineBlock
|
|
layoutWriteEnsureAbsoluteN $ commentCol + fromMaybe 0 (_lstate_addSepSpace state)
|
|
_ -> return ()
|
|
|
|
-- layoutWritePriorCommentsRestore :: (Data.Data.Data ast,
|
|
-- MonadMultiWriter Text.Builder.Builder m,
|
|
-- MonadMultiState LayoutState m
|
|
-- , MonadMultiWriter (Seq String) m)
|
|
-- => Located ast -> m ()
|
|
-- layoutWritePriorCommentsRestore x = do
|
|
-- layoutWritePriorComments x
|
|
-- layoutIndentRestorePostComment
|
|
--
|
|
-- layoutWritePostCommentsRestore :: (Data.Data.Data ast,
|
|
-- MonadMultiWriter Text.Builder.Builder m,
|
|
-- MonadMultiState LayoutState m
|
|
-- , MonadMultiWriter (Seq String) m)
|
|
-- => Located ast -> m ()
|
|
-- layoutWritePostCommentsRestore x = do
|
|
-- layoutWritePostComments x
|
|
-- layoutIndentRestorePostComment
|