stylish-haskell-0.14.0.0: lib/Language/Haskell/Stylish/Comments.hs
--------------------------------------------------------------------------------
-- | Utilities for assocgating comments with things in a list.
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Language.Haskell.Stylish.Comments
( CommentGroup (..)
, commentGroups
, commentGroupHasComments
, commentGroupSort
) where
--------------------------------------------------------------------------------
import Data.Function (on)
import Data.List (sortBy, sortOn)
import Data.Maybe (isNothing, maybeToList)
import qualified GHC.Hs as GHC
import qualified GHC.Types.SrcLoc as GHC
import qualified GHC.Utils.Outputable as GHC
--------------------------------------------------------------------------------
import Language.Haskell.Stylish.Block
import Language.Haskell.Stylish.GHC
--------------------------------------------------------------------------------
data CommentGroup a = CommentGroup
{ cgBlock :: LineBlock
, cgPrior :: [GHC.LEpaComment]
, cgItems :: [(a, Maybe GHC.LEpaComment)]
, cgFollowing :: [GHC.LEpaComment]
}
--------------------------------------------------------------------------------
instance GHC.Outputable a => Show (CommentGroup a) where
show CommentGroup {..} = "(CommentGroup (" ++
show cgBlock ++ ") (" ++
showOutputable cgPrior ++ ") (" ++
showOutputable cgItems ++ ") (" ++
showOutputable cgFollowing ++ "))"
--------------------------------------------------------------------------------
commentGroups
:: forall a.
(a -> Maybe GHC.RealSrcSpan)
-> [a]
-> [GHC.LEpaComment]
-> [CommentGroup a]
commentGroups getSpan allItems allComments =
work Nothing (sortOn fst allItemsWithLines) (sortOn fst commentsWithLines)
where
allItemsWithLines :: [(LineBlock, a)]
allItemsWithLines = do
item <- allItems
s <- maybeToList $ getSpan item
pure (realSrcSpanToLineBlock s, item)
commentsWithLines :: [(LineBlock, GHC.LEpaComment)]
commentsWithLines = do
comment <- allComments
let s = GHC.anchor $ GHC.getLoc comment
pure (realSrcSpanToLineBlock s, comment)
work
:: Maybe (CommentGroup a)
-> [(LineBlock, a)]
-> [(LineBlock, GHC.LEpaComment)]
-> [CommentGroup a]
work mbCurrent items comments = case takeNext items comments of
Nothing -> maybeToList mbCurrent
Just (b, next, items', comments') ->
let (flush, current) = case mbCurrent of
Just c | adjacent (cgBlock c) b
, nextThingItem next
, following@(_ : _) <- cgFollowing c ->
([c {cgFollowing = []}], CommentGroup b following [] [])
Just c | adjacent (cgBlock c) b ->
([], c {cgBlock = cgBlock c <> b})
_ -> (maybeToList mbCurrent, CommentGroup b [] [] [])
current' = case next of
NextItem i -> current {cgItems = cgItems current <> [(i, Nothing)]}
NextComment c
| null (cgItems current) -> current {cgPrior = cgPrior current <> [c]}
| otherwise -> current {cgFollowing = cgFollowing current <> [c]}
NextItemWithComment i c ->
current {cgItems = cgItems current <> [(i, Just c)]} in
flush ++ work (Just current') items' comments'
--------------------------------------------------------------------------------
takeNext
:: [(LineBlock, a)]
-> [(LineBlock, GHC.LEpaComment)]
-> Maybe (LineBlock, NextThing a, [(LineBlock, a)], [(LineBlock, GHC.LEpaComment)])
takeNext [] [] = Nothing
takeNext [] ((cb, c) : comments) =
Just (cb, NextComment c, [], comments)
takeNext ((ib, i) : items) [] =
Just (ib, NextItem i, items, [])
takeNext ((ib, i) : items) ((cb, c) : comments)
| blockStart ib == blockStart cb =
Just (ib <> cb, NextItemWithComment i c, items, comments)
| blockStart ib < blockStart cb =
Just (ib, NextItem i, items, (cb, c) : comments)
| otherwise =
Just (cb, NextComment c, (ib, i) : items, comments)
--------------------------------------------------------------------------------
data NextThing a
= NextComment GHC.LEpaComment
| NextItem a
| NextItemWithComment a GHC.LEpaComment
--------------------------------------------------------------------------------
instance GHC.Outputable a => Show (NextThing a) where
show (NextComment c) = "NextComment " ++ showOutputable c
show (NextItem i) = "NextItem " ++ showOutputable i
show (NextItemWithComment i c) =
"NextItemWithComment " ++ showOutputable i ++ " " ++ showOutputable c
--------------------------------------------------------------------------------
nextThingItem :: NextThing a -> Bool
nextThingItem (NextComment _) = False
nextThingItem (NextItem _) = True
nextThingItem (NextItemWithComment _ _) = True
--------------------------------------------------------------------------------
commentGroupHasComments :: CommentGroup a -> Bool
commentGroupHasComments CommentGroup {..} = not $
null cgPrior && all (isNothing . snd) cgItems && null cgFollowing
--------------------------------------------------------------------------------
commentGroupSort :: (a -> a -> Ordering) -> CommentGroup a -> CommentGroup a
commentGroupSort cmp cg = cg
{ cgItems = sortBy (cmp `on` fst) (cgItems cg)
}