@@ -7,7 +7,6 @@ module Ormolu.Parser.CommentStream
77 ( CommentStream (..),
88 Comment (..),
99 mkCommentStream,
10− isShebang,
1110 isPrevHaddock,
1211 isMultilineComment,
1312 showCommentStream,
@@ -16,14 +15,14 @@ where
1615
1716import Data.Char (isSpace)
1817import Data.Data (Data)
19−import Data.Either (partitionEithers)
20−import Data.List (dropWhileEnd, isPrefixOf, sortOn)
18+import qualified Data.List as L
2119import Data.List.NonEmpty (NonEmpty (..))
2220import qualified Data.List.NonEmpty as NE
2321import Data.Maybe (mapMaybe)
2422import qualified GHC
2523import qualified Lexer as GHC
2624import Ormolu.Parser.Pragma
25+import Ormolu.Parser.Shebang
2726import Ormolu.Utils (showOutputable)
2827import SrcLoc
2928
@@ -45,37 +44,38 @@ mkCommentStream ::
4544 [Located String] ->
4645 -- | Parser state to use for comment extraction
4746 GHC.PState ->
48− -- | Comment stream, a set of extracted pragmas, and extracted shebangs
49− (CommentStream, [Pragma], [Located String])
47+ -- | Stack header, shebangs, pragmas, and comment stream
48+ ( Maybe (RealLocated Comment),
49+ [Shebang],
50+ [([RealLocated Comment], Pragma)],
51+ CommentStream
52+ )
5053mkCommentStream extraComments pstate =
51− ( CommentStream $
52− mkComment <$> sortOn (realSrcSpanStart . getRealSrcSpan) comments,
54+ ( mstackHeader,
55+ shebangs,
5356 pragmas,
54− shebangs
57+ CommentStream comments
5558 )
5659 where
57− (comments, pragmas) = partitionEithers (partitionComments <$> rawComments)
58− rawComments =
59− mapMaybe toRealSpan $
60+ (comments, pragmas) = extractPragmas rawComments1
61+ (rawComments1, mstackHeader) = extractStackHeader rawComments0
62+ rawComments0 =
63+ L.sortOn (realSrcSpanStart . getRealSrcSpan) . mapMaybe toRealSpan $
6064 otherExtraComments
6165 ++ mapMaybe (liftMaybe . fmap unAnnotationComment) (GHC.comment_q pstate)
6266 ++ concatMap
6367 (mapMaybe (liftMaybe . fmap unAnnotationComment) . snd)
6468 (GHC.annotations_comments pstate)
65− (shebangs, otherExtraComments) = span (isShebang . unLoc) extraComments
66−
67−-- | Return 'True' if given 'String' is a shebang.
68−isShebang :: String -> Bool
69−isShebang str = "#!" `isPrefixOf` str
69+ (shebangs, otherExtraComments) = extractShebangs extraComments
7070
7171-- | Test whether a 'Comment' looks like a Haddock following a definition,
7272-- i.e. something starting with @-- ^@.
7373isPrevHaddock :: Comment -> Bool
74−isPrevHaddock (Comment (x :| _)) = "-- ^" `isPrefixOf` x
74+isPrevHaddock (Comment (x :| _)) = "-- ^" `L.isPrefixOf` x
7575
7676-- | Is this comment multiline-style?
7777isMultilineComment :: Comment -> Bool
78−isMultilineComment (Comment (x :| _)) = "{-" `isPrefixOf` x
78+isMultilineComment (Comment (x :| _)) = "{-" `L.isPrefixOf` x
7979
8080-- | Pretty-print a 'CommentStream'.
8181showCommentStream :: CommentStream -> String
@@ -94,7 +94,7 @@ showCommentStream (CommentStream xs) =
9494mkComment :: RealLocated String -> RealLocated Comment
9595mkComment (L l s) =
9696 L l . Comment . fmap dropTrailing $
97− if "{-" `isPrefixOf` s
97+ if "{-" `L.isPrefixOf` s
9898 then case NE.nonEmpty (lines s) of
9999 Nothing -> s :| []
100100 Just (x :| xs) ->
@@ -106,7 +106,7 @@ mkComment (L l s) =
106106 in x :| (drop n <$> xs)
107107 else s :| []
108108 where
109− dropTrailing = dropWhileEnd isSpace
109+ dropTrailing = L.dropWhileEnd isSpace
110110 startIndent = srcSpanStartCol l - 1
111111
112112-- | Get a 'String' from 'GHC.AnnotationComment'.
@@ -129,12 +129,32 @@ toRealSpan :: Located a -> Maybe (RealLocated a)
129129toRealSpan (L (RealSrcSpan l) a) = Just (L l a)
130130toRealSpan _ = Nothing
131131
132−-- | If a given comment is a pragma, return it in parsed form in 'Right'.
133−-- Otherwise return the original comment unchanged.
134−partitionComments ::
135− RealLocated String ->
136− Either (RealLocated String) Pragma
137−partitionComments input =
138− case parsePragma (unRealSrcSpan input) of
139− Nothing -> Left input
140− Just pragma -> Right pragma
132+-- | Detect and extract stack header if it is present.
133+extractStackHeader ::
134+ [RealLocated String] ->
135+ ([RealLocated String], Maybe (RealLocated Comment))
136+extractStackHeader = \case
137+ [] -> ([], Nothing)
138+ (x : xs) ->
139+ let comment = mkComment x
140+ in if isStackHeader (unRealSrcSpan comment)
141+ then (xs, Just comment)
142+ else (x : xs, Nothing)
143+ where
144+ isStackHeader (Comment (x :| _)) =
145+ "stack" `L.isPrefixOf` dropWhile isSpace (drop 2 x)
146+
147+-- | Extract pragmas and their associated comments.
148+extractPragmas ::
149+ [RealLocated String] ->
150+ ([RealLocated Comment], [([RealLocated Comment], Pragma)])
151+extractPragmas = go id id
152+ where
153+ go csSoFar pragmasSoFar = \case
154+ [] -> (csSoFar [], pragmasSoFar [])
155+ (x : xs) ->
156+ case parsePragma (unRealSrcSpan x) of
157+ Nothing -> go (csSoFar . (mkComment x :)) pragmasSoFar xs
158+ Just pragma ->
159+ let combined = (csSoFar [], pragma)
160+ in go id (pragmasSoFar . (combined :)) xs