@@ -1,5 +1,6 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RankNTypes #-}
4-- | In most cases import "Ormolu.Printer.Combinators" instead, these
5-- | In most cases import "Ormolu.Printer.Combinators" instead, these
5-- functions are the low-level building blocks and should not be used on
6-- functions are the low-level building blocks and should not be used on
@@ -13,6 +14,7 @@ module Ormolu.Printer.Internal
13 -- * Internal functions
14 -- * Internal functions
16 , ensureIndent
18 , ensureIndent
@@ -21,21 +23,31 @@ module Ormolu.Printer.Internal
21 , enterLayout
23 , enterLayout
23 , relaxedComments
25 , relaxedComments
24− , lookupAnn
26+ -- * Special helpers for comment placement
28import Control.Monad.Reader
38import Control.Monad.Reader
29import Control.Monad.State.Strict
39import Control.Monad.State.Strict
30−import Data.Data (Data)
40+import Data.Coerce
41+import Data.Maybe (listToMaybe)
31import Data.Text (Text)
42import Data.Text (Text)
32import Data.Text.Lazy.Builder
43import Data.Text.Lazy.Builder
33import Debug.Trace
44import Debug.Trace
34−import Language.Haskell.GHC.ExactPrint.Types
45+import GHC
46+import Ormolu.CommentStream
47+import Ormolu.SpanStream
35import SrcLoc
48import SrcLoc
36−import qualified Data.Text.Lazy as TL
37−import qualified Data.Map as M
38import qualified Data.Text as T
49import qualified Data.Text as T
50+import qualified Data.Text.Lazy as TL
40----------------------------------------------------------------------------
52----------------------------------------------------------------------------
41-- The 'R' monad
53-- The 'R' monad
@@ -56,10 +68,10 @@ data RC = RC
56 -- ^ Current layout
68 -- ^ Current layout
57 , rcRelaxedComments :: Bool
69 , rcRelaxedComments :: Bool
58 -- ^ Whether to relax aligning rules for comments
70 -- ^ Whether to relax aligning rules for comments
60− -- ^ The collection of annotations obtained after parsing
61 , rcDebug :: Bool
71 , rcDebug :: Bool
62 -- ^ Whether to print debugging info as we go
72 -- ^ Whether to print debugging info as we go
73+ , rcEnclosingSpan :: Maybe RealSrcSpan
74+ -- ^ Span of enclosing element of AST
65-- | State context of 'R'.
77-- | State context of 'R'.
@@ -69,6 +81,12 @@ data SC = SC
69 -- ^ Index of the next column to render
81 -- ^ Index of the next column to render
70 , scBuilder :: Builder
82 , scBuilder :: Builder
71 -- ^ Rendered source code so far
83 -- ^ Rendered source code so far
84+ , scSpanStream :: SpanStream
86+ , scCommentStream :: CommentStream
89+ -- ^ What to render as newline
74-- | 'Layout' options.
92-- | 'Layout' options.
@@ -83,21 +101,25 @@ data Layout
84 :: Bool -- ^ Whether to print debugging info
102 :: Bool -- ^ Whether to print debugging info
85 -> R () -- ^ Monad to run
103 -> R () -- ^ Monad to run
86− -> Anns -- ^ Annotations to use
104+ -> SpanStream -- ^ Span stream
105+ -> CommentStream -- ^ Comment stream
87 -> Text -- ^ Resulting rendition
106 -> Text -- ^ Resulting rendition
88−runR debug (R m) anns =
107+runR debug (R m) sstream cstream =
89 TL.toStrict . toLazyText . scBuilder $ execState (runReaderT m rc) sc
108 TL.toStrict . toLazyText . scBuilder $ execState (runReaderT m rc) sc
92 { rcIndent = 0
111 { rcIndent = 0
93 , rcLayout = MultiLine
112 , rcLayout = MultiLine
94 , rcRelaxedComments = False
113 , rcRelaxedComments = False
96 , rcDebug = debug
114 , rcDebug = debug
115+ , rcEnclosingSpan = Nothing
99 { scColumn = 0
118 { scColumn = 0
100 , scBuilder = mempty
119 , scBuilder = mempty
120+ , scSpanStream = sstream
121+ , scCommentStream = cstream
122+ , scNewline = newlineRaw
103----------------------------------------------------------------------------
125----------------------------------------------------------------------------
@@ -109,26 +131,49 @@ runR debug (R m) anns =
109spit :: Text -> R ()
131spit :: Text -> R ()
110spit x = do
132spit x = do
111 traceR "spit_before" (Just x)
133 traceR "spit_before" (Just x)
112− R (modify modSC)
134+ R . modify $ \sc -> sc
135+ { scBuilder = scBuilder sc <> fromText x
136+ , scColumn = scColumn sc + T.length x
113 traceR "spit_after" Nothing
138 traceR "spit_after" Nothing
116− { scBuilder = scBuilder sc <> fromText x
117− , scColumn = scColumn sc + T.length x
120−-- | Output a newline.
140+-- | Output a newline. The 'modNewline' function can be used to alter what
141+-- will be inserted. This is used to output comments following an element of
142+-- AST because we cannot output comments immediately, e.g. because we need
143+-- to close parentheses first, etc.
145+-- 'newline' auto-resets its modifications so the changes introduced with
146+-- 'modNewline' only have effect once.
122newline :: R ()
148newline :: R ()
123newline = do
149newline = do
150+ n <- R (gets scNewline)
151+ R . modify $ \sc -> sc
152+ { scNewline = newlineRaw
156+-- | Low-level newline primitive. This one always just inserts a newline, no
157+-- hooks can be attached.
124 traceR "newline_before" (Just "\n")
161 traceR "newline_before" (Just "\n")
125− R (modify modSC)
162+ R . modify $ \sc -> sc
163+ { scBuilder = scBuilder sc <> "\n"
126 traceR "newline_after" Nothing
166 traceR "newline_after" Nothing
128− modSC sc = sc
168+-- | Modify how next newline will be output. The argument of call-back is
129− { scBuilder = scBuilder sc <> "\n"
169+-- the version of 'newline' built so far.
171+modNextline :: (R () -> R ()) -> R ()
172+modNextline f = R $ do
173+ old <- gets scNewline
133-- | Ensure that indentation level is satisfied. Insert correct number of
178-- | Ensure that indentation level is satisfied. Insert correct number of
134-- spaces if it isn't.
179-- spaces if it isn't.
@@ -148,12 +193,11 @@ inci :: R () -> R ()
148inci m' = do
193inci m' = do
149 traceR "inci_before" Nothing
194 traceR "inci_before" Nothing
150 let R m = traceR "inci_inside" Nothing >> m'
195 let R m = traceR "inci_inside" Nothing >> m'
197+ { rcIndent = rcIndent rc + indentStep
151 R (local modRC m)
199 R (local modRC m)
152 traceR "inci_ended" Nothing
200 traceR "inci_ended" Nothing
155− { rcIndent = rcIndent x + indentStep
158-- | Set indentation level for the inner computation equal to current
202-- | Set indentation level for the inner computation equal to current
159-- column. This makes sure that the entire inner block is uniformly
203-- column. This makes sure that the entire inner block is uniformly
@@ -165,10 +209,10 @@ sitcc m' = do
165 traceR "sitcc_before" Nothing
209 traceR "sitcc_before" Nothing
166 i <- R (asks rcIndent)
210 i <- R (asks rcIndent)
167 c <- R (gets scColumn)
211 c <- R (gets scColumn)
212+ let R m = traceR "sitcc_inside" Nothing >> m'
168− let modRC x = x
213+ modRC rc = rc
169 { rcIndent = max i c
214 { rcIndent = max i c
171− R m = traceR "sitcc_inside" Nothing >> m'
172 vlayout m' (R (local modRC m))
216 vlayout m' (R (local modRC m))
173 traceR "sitcc_ended" Nothing
217 traceR "sitcc_ended" Nothing
@@ -179,7 +223,7 @@ sitcc m' = do
179relaxComments :: R () -> R ()
223relaxComments :: R () -> R ()
180relaxComments (R m) = do
224relaxComments (R m) = do
181 traceR "relax_start" Nothing
225 traceR "relax_start" Nothing
182− let modRC x = x
226+ let modRC rc = rc
183 { rcRelaxedComments = True
227 { rcRelaxedComments = True
185 R (local modRC m)
229 R (local modRC m)
@@ -194,7 +238,7 @@ enterLayout l (R m) = do
194 SingleLine -> "single_line"
238 SingleLine -> "single_line"
195 MultiLine -> "multi_line"
239 MultiLine -> "multi_line"
196 traceR ("lstart_" ++ label) Nothing
240 traceR ("lstart_" ++ label) Nothing
197− let modRC x = x
241+ let modRC rc = rc
198 { rcLayout = l
242 { rcLayout = l
200 R (local modRC m)
244 R (local modRC m)
@@ -207,25 +251,91 @@ vlayout
207 -> R () -- ^ Multi line
251 -> R () -- ^ Multi line
209vlayout sline mline = do
253vlayout sline mline = do
210− l <- currentLayout
254+ l <- R (asks rcLayout)
211 case l of
255 case l of
212 SingleLine -> sline
256 SingleLine -> sline
213 MultiLine -> mline
257 MultiLine -> mline
215−-- | Return current layout.
217−currentLayout :: R Layout
218−currentLayout = R (asks rcLayout)
220-- | Check whether we're in a region with relaxed comments placement.
259-- | Check whether we're in a region with relaxed comments placement.
222relaxedComments :: R Bool
261relaxedComments :: R Bool
223relaxedComments = R (asks rcRelaxedComments)
262relaxedComments = R (asks rcRelaxedComments)
225−-- | Lookup an annotation.
264+----------------------------------------------------------------------------
265+-- Special helpers for comment placement
267+-- | Drop elements that begin before or at the same place as given
227−lookupAnn :: Data a => Located a -> R (Maybe Annotation)
270+trimSpanStream
228−lookupAnn l = M.lookup (mkAnnKey l) <$> R (asks rcAnns)
271+ :: RealSrcSpan -- ^ Reference span
273+trimSpanStream ref = do
274+ let leRef :: RealSrcSpan -> Bool
275+ leRef x = realSrcSpanStart x <= realSrcSpanStart ref
276+ R . modify $ \sc -> sc
277+ { scSpanStream = coerce (dropWhile leRef) (scSpanStream sc)
280+-- | Get location of next element in AST.
282+nextEltSpan :: R (Maybe RealSrcSpan)
283+nextEltSpan = listToMaybe . coerce <$> R (gets scSpanStream)
285+-- | Pop a 'Comment' from the 'CommentStream' if given predicate is
286+-- satisfied and there are comments in the stream.
289+ :: (RealLocated Comment -> Bool)
290+ -> R (Maybe (RealLocated Comment))
291+popComment f = R $ do
292+ CommentStream cstream <- gets scCommentStream
294+ [] -> return Nothing
297+ then Just x <$ modify (\sc -> sc
298+ { scCommentStream = CommentStream xs
302+-- | Return 'True' if there are more comments in the 'CommentStream'.
304+hasMoreComments :: R Bool
305+hasMoreComments = R $ do
306+ CommentStream cstream <- gets scCommentStream
307+ (return . not . null) cstream
309+-- | Current indentation level.
312+getIndent = R (asks rcIndent)
314+-- | Set indentation level.
316+setIndent :: Int -> R () -> R ()
318+ traceR "set_indent_before" Nothing
319+ let R m = traceR "set_indent_inside" Nothing >> m'
324+ traceR "set_indent_after" Nothing
326+-- | Get 'RealSrcSpan' of enclosing span, if any.
328+getEnclosingSpan :: R (Maybe RealSrcSpan)
329+getEnclosingSpan = R (asks rcEnclosingSpan)
331+-- | Set 'RealSrcSpan' of enclosing span for the given computation.
333+withEnclosingSpan :: RealSrcSpan -> R () -> R ()
334+withEnclosingSpan spn (R m) = do
336+ { rcEnclosingSpan = Just spn
230----------------------------------------------------------------------------
340----------------------------------------------------------------------------
231-- Debug helpers
341-- Debug helpers