| ... |
... |
@@ -140,9 +140,9 @@ defaultEPState = EPState |
|
140
|
140
|
|
|
141
|
141
|
-- | The R part of RWS. The environment. Updated via 'local' as we
|
|
142
|
142
|
-- enter a new AST element, having a different anchor point.
|
|
143
|
|
-data EPOptions m a = EPOptions
|
|
144
|
|
- { epTokenPrint :: String -> m a
|
|
145
|
|
- , epWhitespacePrint :: String -> m a
|
|
|
143
|
+data EPOptions m w = EPOptions
|
|
|
144
|
+ { epTokenPrint :: String -> m w
|
|
|
145
|
+ , epWhitespacePrint :: String -> m w
|
|
146
|
146
|
}
|
|
147
|
147
|
|
|
148
|
148
|
-- | Helper to create a 'EPOptions'
|
| ... |
... |
@@ -163,8 +163,7 @@ stringOptions = epOptions return return |
|
163
|
163
|
deltaOptions :: EPOptions Identity ()
|
|
164
|
164
|
deltaOptions = epOptions (\_ -> return ()) (\_ -> return ())
|
|
165
|
165
|
|
|
166
|
|
-data EPWriter a = EPWriter
|
|
167
|
|
- { output :: !a }
|
|
|
166
|
+data EPWriter w = EPWriter { output :: !w }
|
|
168
|
167
|
|
|
169
|
168
|
instance Monoid w => Semigroup (EPWriter w) where
|
|
170
|
169
|
(EPWriter a) <> (EPWriter b) = EPWriter (a <> b)
|
| ... |
... |
@@ -239,7 +238,220 @@ setAnchorEpaL (EpAnn _ an _) anc ts cs = EpAnn anc (setTrailing (an {al_anchor = |
|
239
|
238
|
-- | Key entry point. Switches to an independent AST element with its
|
|
240
|
239
|
-- own annotation, calculating new offsets, etc
|
|
241
|
240
|
markAnnotated :: (Monad m, Monoid w, ExactPrint a) => a -> EP w m a
|
|
242
|
|
-markAnnotated a = enterAnn (getAnnotationEntry a) a
|
|
|
241
|
+markAnnotated a = enterAnn (getAnnotationEntry a) a -- See Note [Exact print main loop]
|
|
|
242
|
+
|
|
|
243
|
+{-
|
|
|
244
|
+Note [Exact print main loop]
|
|
|
245
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
246
|
+
|
|
|
247
|
+Exact printing is the process of taking the ParsedSource, or some
|
|
|
248
|
+subtree from it, and rendering it to a string, which accurately
|
|
|
249
|
+reflects the original source that was parsed, except detail of
|
|
|
250
|
+whitespace (such as spaces vs tabs) is discarded.
|
|
|
251
|
+
|
|
|
252
|
+The ParsedSource or fragment from it must implement the ExactPrint
|
|
|
253
|
+class, and this file does that, as well as supporting machinery to
|
|
|
254
|
+drive the process. The ExactPrint class is described later in this
|
|
|
255
|
+note.
|
|
|
256
|
+
|
|
|
257
|
+The entry point for exact printing is the exactPrint function, defined
|
|
|
258
|
+as
|
|
|
259
|
+
|
|
|
260
|
+ exactPrint :: ExactPrint ast => ast -> String
|
|
|
261
|
+ exactPrint ast = snd $ runIdentity (runEP stringOptions (markAnnotated ast))
|
|
|
262
|
+
|
|
|
263
|
+ type EP w m a = RWST (EPOptions m w) (EPWriter w) EPState m a
|
|
|
264
|
+
|
|
|
265
|
+ runEP :: (Monad m) => EPOptions m w -> EP w m a -> m (a, w)
|
|
|
266
|
+ runEP epReader action = do
|
|
|
267
|
+ (ast, w) <- evalRWST action epReader defaultEPState
|
|
|
268
|
+ return (ast, output w)
|
|
|
269
|
+
|
|
|
270
|
+ data EPWriter w = EPWriter { output :: !w }
|
|
|
271
|
+
|
|
|
272
|
+ data EPOptions m w = EPOptions
|
|
|
273
|
+ { epTokenPrint :: String -> m w
|
|
|
274
|
+ , epWhitespacePrint :: String -> m w
|
|
|
275
|
+ }
|
|
|
276
|
+
|
|
|
277
|
+This offers some flexibility in how the output is produced, in that
|
|
|
278
|
+the EpWriter and EPOptions work together, defining an output type and
|
|
|
279
|
+a function to render whitespace and non-whitepace.
|
|
|
280
|
+
|
|
|
281
|
+In normal operation (via exactPrint) these are simply returning the
|
|
|
282
|
+string unchanged and accumulating a larger string. But it has also been
|
|
|
283
|
+used to render an HTML version of the source as it prints.
|
|
|
284
|
+
|
|
|
285
|
+The main reason exact print exists is to make it easier for tool
|
|
|
286
|
+writers to manipulate the ParsedSource, and render this back to the
|
|
|
287
|
+original source, except for the changed parts. One manipulation is to
|
|
|
288
|
+simply take say a FunBind with its signature from one place in the
|
|
|
289
|
+code to another. When doing this, the original parsed locations are no
|
|
|
290
|
+longer useful when rendering the source back to a string. To enable
|
|
|
291
|
+this, we have another entry point, for converting all absolute
|
|
|
292
|
+locations to relative ones, using DeltaPos, which captures the line
|
|
|
293
|
+and col offset relative to the prior output.
|
|
|
294
|
+
|
|
|
295
|
+The function to do this is
|
|
|
296
|
+
|
|
|
297
|
+ makeDeltaAst :: ExactPrint ast => ast -> ast
|
|
|
298
|
+ makeDeltaAst ast = fst $ runIdentity (runEP deltaOptions (markAnnotated ast))
|
|
|
299
|
+
|
|
|
300
|
+The implementation is practically identical for exactPrint and
|
|
|
301
|
+makeDeltaAst, they simply return the second or first part of the same
|
|
|
302
|
+calculation.
|
|
|
303
|
+
|
|
|
304
|
+This simplifies the implementation of the required ExactPrint
|
|
|
305
|
+implmentations, in that we only need one per ast item, which encodes
|
|
|
306
|
+how to print it, and this print process does bookkeeping to keep track
|
|
|
307
|
+of what spacing it used when doing the print, so it can return the
|
|
|
308
|
+updated item, with the original absolute SrcSpan's converted to
|
|
|
309
|
+DeltaPos values instead.
|
|
|
310
|
+
|
|
|
311
|
+This information is captured in the XRec/Anno instances used in GHC
|
|
|
312
|
+Hs, which are mostly LocatedA a, defined as
|
|
|
313
|
+
|
|
|
314
|
+ GenLocated (EpAnn [TrailingAnn]) a
|
|
|
315
|
+
|
|
|
316
|
+ data EpAnn ann
|
|
|
317
|
+ = EpAnn { entry :: !EpaLocation
|
|
|
318
|
+ , anns :: !ann
|
|
|
319
|
+ , comments :: !EpAnnComments
|
|
|
320
|
+ }
|
|
|
321
|
+
|
|
|
322
|
+ data EpaLocation = EpaSpan !SrcSpan
|
|
|
323
|
+ | EpaDelta !SrcSpan !DeltaPos [LEpaComment]
|
|
|
324
|
+
|
|
|
325
|
+When parsed, the entry EpaLocation captures an EpaSpan with the parsed
|
|
|
326
|
+absolute location. After makeDeltaAst, this instead uses EpaDelta,
|
|
|
327
|
+including the original SrcSpan, the relative spaceing as a DeltaPos,
|
|
|
328
|
+and any comments that may come before the item, e.g. comments before a
|
|
|
329
|
+semicolon.
|
|
|
330
|
+
|
|
|
331
|
+Both exactPrint and makeDeltaAst call markAnnotated on the item to be
|
|
|
332
|
+printed, inside the monad machinery.
|
|
|
333
|
+
|
|
|
334
|
+ markAnnotated :: (Monad m, Monoid w, ExactPrint a) => a -> EP w m a
|
|
|
335
|
+ markAnnotated a = enterAnn (getAnnotationEntry a) a
|
|
|
336
|
+
|
|
|
337
|
+This makes use of the ExactPrint instance for the ParsedSource and
|
|
|
338
|
+for every other part of the ParsedSource as a tree.
|
|
|
339
|
+
|
|
|
340
|
+ class (Typeable a) => ExactPrint a where
|
|
|
341
|
+ getAnnotationEntry :: a -> Entry
|
|
|
342
|
+ exact :: (Monad m, Monoid w) => a -> EP w m a
|
|
|
343
|
+ setAnnotationAnchor :: a -> EpaLocation -> [TrailingAnn] -> EpAnnComments -> a
|
|
|
344
|
+
|
|
|
345
|
+- getAnnotationEntry : returns the Entry information, normally derived
|
|
|
346
|
+ directly from the EpAnn in the LocatedA enclosing the item.
|
|
|
347
|
+ Entry contains
|
|
|
348
|
+ - The location of the first non-comment part of the item to be printed.
|
|
|
349
|
+ - Any [TrailingAnn] (semis, commas, etc) to follow the item.
|
|
|
350
|
+ - Any comments associated with the item, but not with enclosed items.
|
|
|
351
|
+ These comments may also precede or follow the entry location
|
|
|
352
|
+ - Some technical flags, relating to flushing remaining comments at the
|
|
|
353
|
+ last (topmost) item, and if the anchor can be updated (see below).
|
|
|
354
|
+
|
|
|
355
|
+- exact : Do the actual printing of the item, having been put in the right
|
|
|
356
|
+ place in terms of print location by the main loop, based on a call to
|
|
|
357
|
+ getAnnotationEntry for it. This printing normally comprises making nested
|
|
|
358
|
+ markAnnotated calls on the nested items in the current one, e.g.
|
|
|
359
|
+
|
|
|
360
|
+ exact (HsApp an e1 e2) = do
|
|
|
361
|
+ e1' <- markAnnotated e1
|
|
|
362
|
+ e2' <- markAnnotated e2
|
|
|
363
|
+ return (HsApp an e1' e2')
|
|
|
364
|
+
|
|
|
365
|
+- setAnnotationAnchor : the main loop printing does two things:
|
|
|
366
|
+ - produce output, reflecting the items being printed
|
|
|
367
|
+ - convert all the locations in the item (each an EpaLocation) from its
|
|
|
368
|
+ EpaSpan version to an EpaDelta one, which is independent of absolute
|
|
|
369
|
+ location, being the spacing from the previous print output to this
|
|
|
370
|
+ entry point
|
|
|
371
|
+ To allow these updated locations to be captured, the item implements
|
|
|
372
|
+ the setAnnotationAnchor method, which updates the fields of the Entry
|
|
|
373
|
+ returned by getAnnotationEntry
|
|
|
374
|
+
|
|
|
375
|
+You will recall
|
|
|
376
|
+
|
|
|
377
|
+ markAnnotated a = enterAnn (getAnnotationEntry a) a
|
|
|
378
|
+ enterAnn :: (Monad m, Monoid w, ExactPrint a) => Entry -> a -> EP w m a
|
|
|
379
|
+
|
|
|
380
|
+enterAnn is the part that is repeatedly called via markAnnotated, to
|
|
|
381
|
+manage the bookkeeping around printing and calculating the updated
|
|
|
382
|
+delta positions.
|
|
|
383
|
+
|
|
|
384
|
+It manages the printing state, adding the new comments from the Entry,
|
|
|
385
|
+positioning the print head to the entry point, takes account of whether
|
|
|
386
|
+it is an absolute span or a delta, and prints any comments that must come
|
|
|
387
|
+before this location.
|
|
|
388
|
+
|
|
|
389
|
+At this print location, it calls the `exact` method on the item. This
|
|
|
390
|
+is the only place `exact` can be called. Within `exact` implementations
|
|
|
391
|
+the `markAnnotated` function should be used instead, to ensure the state
|
|
|
392
|
+is managed properly for printing.
|
|
|
393
|
+
|
|
|
394
|
+Once the item is printed via its `exact` method, which normally
|
|
|
395
|
+recurses via `markAnnotated` into any nested items inside it, the
|
|
|
396
|
+`enterAnn` function prints any [TrailingAnn] from the entry and any
|
|
|
397
|
+trailing comments. It then wraps up by calling
|
|
|
398
|
+`setAnnotationAnchor`with the updated (now delta-based) values for the
|
|
|
399
|
+Entry
|
|
|
400
|
+
|
|
|
401
|
+Printing Complicated Items
|
|
|
402
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
403
|
+Sometimes we need to print a sub-item which does not map cleanly onto the
|
|
|
404
|
+ExactPrint class, usually because there are parts spread through the item
|
|
|
405
|
+that need to be managed together while printing.
|
|
|
406
|
+
|
|
|
407
|
+An example is in
|
|
|
408
|
+https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16321, where
|
|
|
409
|
+ConDeclGADT needs to print its inner_bindings, but the these in turn
|
|
|
410
|
+may have parens, e.g.
|
|
|
411
|
+
|
|
|
412
|
+ data V a where
|
|
|
413
|
+ MkV4 :: forall a. ((forall b. (Show a => a -> (b -> V a))))
|
|
|
414
|
+
|
|
|
415
|
+Here there are inner bindings for the first forall, the multiple
|
|
|
416
|
+parens around the second forall, and the second forall, wrapping the
|
|
|
417
|
+inner content within this.
|
|
|
418
|
+
|
|
|
419
|
+But the inner content `Show a => a -> (b -> V a)` had to be printed "inside"
|
|
|
420
|
+the inner_bindings, to correctly close the nested parens.
|
|
|
421
|
+
|
|
|
422
|
+The approach in !16321 modified `enterAnn`, to pass in alternate versions
|
|
|
423
|
+of the `exact` implementation. This works, but it makes an already
|
|
|
424
|
+complicated function even more complicated.
|
|
|
425
|
+
|
|
|
426
|
+An ExactPrint-idiomatic approach is to create a helper data structure
|
|
|
427
|
+to represent this nested organisation, and provide ExactPrint
|
|
|
428
|
+instances for that.
|
|
|
429
|
+
|
|
|
430
|
+This is done in
|
|
|
431
|
+https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16644, using
|
|
|
432
|
+
|
|
|
433
|
+ data InnerBindings
|
|
|
434
|
+ = InnerMore (LHsGadtTelescope GhcPs) InnerBindings
|
|
|
435
|
+ | InnerDone (Maybe (LHsContext GhcPs), HsConDeclGADTDetails GhcPs, LHsType GhcPs)
|
|
|
436
|
+
|
|
|
437
|
+The key part of the ExactPrint instance for this is
|
|
|
438
|
+
|
|
|
439
|
+ exact (InnerMore (L l (HsGadtPar (lp, rp))) xs) = do
|
|
|
440
|
+ lp' <- markEpToken lp
|
|
|
441
|
+ xs' <- markAnnotated xs
|
|
|
442
|
+ rp' <- markEpToken rp
|
|
|
443
|
+ return (InnerMore (L l (HsGadtPar (lp',rp'))) xs')
|
|
|
444
|
+
|
|
|
445
|
+This first prints the opening paren, prints the balance of the cons list, then
|
|
|
446
|
+prints the closing paren.
|
|
|
447
|
+
|
|
|
448
|
+Conversions to and from this structure populate the initial values from
|
|
|
449
|
+the ConDeclGADT, and retrieve the updated delta-based values after
|
|
|
450
|
+printing.
|
|
|
451
|
+
|
|
|
452
|
+Another use of this technique involves HsModuleImpDecls.
|
|
|
453
|
+
|
|
|
454
|
+-}
|
|
243
|
455
|
|
|
244
|
456
|
-- | For HsModule, because we do not have a proper SrcSpan, we must
|
|
245
|
457
|
-- indicate to flush trailing comments when done.
|
| ... |
... |
@@ -377,6 +589,7 @@ cua NoCanUpdateAnchor _ = return [] |
|
377
|
589
|
-- This is combination of the ghc=exactprint Delta.withAST and
|
|
378
|
590
|
-- Print.exactPC functions and effectively does the delta processing
|
|
379
|
591
|
-- immediately followed by the print processing. JIT ghc-exactprint.
|
|
|
592
|
+-- See Note [Exact print main loop]
|
|
380
|
593
|
enterAnn :: (Monad m, Monoid w, ExactPrint a) => Entry -> a -> EP w m a
|
|
381
|
594
|
enterAnn NoEntryVal a = do
|
|
382
|
595
|
p <- getPosP
|