1
- {-# LANGUAGE CPP #-}
2
- {-# LANGUAGE DerivingStrategies #-}
3
- {-# LANGUAGE GADTs #-}
4
- {-# LANGUAGE OverloadedStrings #-}
5
- {-# LANGUAGE RankNTypes #-}
6
- {-# LANGUAGE TypeFamilies #-}
1
+ {-# LANGUAGE CPP #-}
2
+ {-# LANGUAGE DerivingVia #-}
3
+ {-# LANGUAGE GADTs #-}
4
+ {-# LANGUAGE RankNTypes #-}
5
+ {-# LANGUAGE TypeFamilies #-}
7
6
8
7
module Development.IDE.GHC.ExactPrint
9
8
( Graft (.. ),
10
9
graftDecls ,
11
10
graftDeclsWithM ,
12
11
annotate ,
12
+ annotateDecl ,
13
13
hoistGraft ,
14
14
graftWithM ,
15
- graftWithSmallestM ,
16
- graftSmallestDecls ,
15
+ genericGraftWithSmallestM ,
16
+ genericGraftWithLargestM ,
17
17
graftSmallestDeclsWithM ,
18
18
transform ,
19
19
transformM ,
@@ -27,6 +27,9 @@ module Development.IDE.GHC.ExactPrint
27
27
TransformT ,
28
28
Anns ,
29
29
Annotate ,
30
+ mkBindListT ,
31
+ setPrecedingLinesT ,
32
+ everywhereM' ,
30
33
)
31
34
where
32
35
@@ -63,7 +66,8 @@ import Parser (parseIdentifier)
63
66
import Data.Traversable (for )
64
67
import Data.Foldable (Foldable (fold ))
65
68
import Data.Bool (bool )
66
- import Data.Monoid (All (All ))
69
+ import Data.Monoid (All (All ), Any (Any ))
70
+ import Data.Functor.Compose (Compose (Compose ))
67
71
#if __GLASGOW_HASKELL__ == 808
68
72
import Control.Arrow
69
73
#endif
@@ -195,7 +199,7 @@ needsParensSpace ExplicitSum{} = (All False, All False)
195
199
needsParensSpace HsCase {} = (All False , All False )
196
200
needsParensSpace HsIf {} = (All False , All False )
197
201
needsParensSpace HsMultiIf {} = (All False , All False )
198
- needsParensSpace HsLet {} = (All False , All False )
202
+ needsParensSpace HsLet {} = (All False , All True )
199
203
needsParensSpace HsDo {} = (All False , All False )
200
204
needsParensSpace ExplicitList {} = (All False , All False )
201
205
needsParensSpace RecordCon {} = (All False , All False )
@@ -286,30 +290,56 @@ graftWithM dst trans = Graft $ \dflags a -> do
286
290
)
287
291
a
288
292
289
- graftWithSmallestM ::
290
- forall ast m a .
291
- (Fail. MonadFail m , Data a , ASTElement ast ) =>
293
+ -- | A generic query intended to be used for calling 'smallestM' and
294
+ -- 'largestM'. If the current node is a 'Located', returns whether or not the
295
+ -- given 'SrcSpan' is a subspan. For all other nodes, returns 'Nothing', which
296
+ -- indicates uncertainty. The search strategy in 'smallestM' et al. will
297
+ -- continue searching uncertain nodes.
298
+ genericIsSubspan ::
299
+ forall ast .
300
+ Typeable ast =>
301
+ -- | The type of nodes we'd like to consider.
302
+ Proxy (Located ast ) ->
292
303
SrcSpan ->
293
- (Located ast -> TransformT m (Maybe (Located ast ))) ->
304
+ GenericQ (Maybe Bool )
305
+ genericIsSubspan _ dst = mkQ Nothing $ \ case
306
+ (L span _ :: Located ast ) -> Just $ dst `isSubspanOf` span
307
+
308
+ -- | Run the given transformation only on the smallest node in the tree that
309
+ -- contains the 'SrcSpan'.
310
+ genericGraftWithSmallestM ::
311
+ forall m a ast .
312
+ (Monad m , Data a , Typeable ast ) =>
313
+ -- | The type of nodes we'd like to consider when finding the smallest.
314
+ Proxy (Located ast ) ->
315
+ SrcSpan ->
316
+ (DynFlags -> GenericM (TransformT m )) ->
294
317
Graft m a
295
- graftWithSmallestM dst trans = Graft $ \ dflags a -> do
296
- everywhereM'
297
- ( mkM $
298
- \ case
299
- val@ (L src _ :: Located ast )
300
- | dst `isSubspanOf` src -> do
301
- mval <- trans val
302
- case mval of
303
- Just val' -> do
304
- (anns, val'') <-
305
- hoistTransform (either Fail. fail pure ) $
306
- annotate dflags True $ maybeParensAST val'
307
- modifyAnnsT $ mappend anns
308
- pure val''
309
- Nothing -> pure val
310
- l -> pure l
311
- )
312
- a
318
+ genericGraftWithSmallestM proxy dst trans = Graft $ \ dflags ->
319
+ smallestM (genericIsSubspan proxy dst) (trans dflags)
320
+
321
+ -- | Run the given transformation only on the largest node in the tree that
322
+ -- contains the 'SrcSpan'.
323
+ genericGraftWithLargestM ::
324
+ forall m a ast .
325
+ (Monad m , Data a , Typeable ast ) =>
326
+ -- | The type of nodes we'd like to consider when finding the largest.
327
+ Proxy (Located ast ) ->
328
+ SrcSpan ->
329
+ (DynFlags -> GenericM (TransformT m )) ->
330
+ Graft m a
331
+ genericGraftWithLargestM proxy dst trans = Graft $ \ dflags ->
332
+ largestM (genericIsSubspan proxy dst) (trans dflags)
333
+
334
+
335
+ -- | Lift a function that replaces a value with several values into a generic
336
+ -- function. The result doesn't perform any searching, so should be driven via
337
+ -- 'everywhereM' or friends.
338
+ --
339
+ -- The 'Int' argument is the index in the list being bound.
340
+ mkBindListT :: forall b m . (Typeable b , Data b , Monad m ) => (Int -> b -> m [b ]) -> GenericM m
341
+ mkBindListT f = mkM $ fmap join . traverse (uncurry f) . zip [0 .. ]
342
+
313
343
314
344
graftDecls ::
315
345
forall a .
@@ -319,32 +349,13 @@ graftDecls ::
319
349
Graft (Either String ) a
320
350
graftDecls dst decs0 = Graft $ \ dflags a -> do
321
351
decs <- forM decs0 $ \ decl -> do
322
- (anns, decl') <- annotateDecl dflags decl
323
- modifyAnnsT $ mappend anns
324
- pure decl'
352
+ annotateDecl dflags decl
325
353
let go [] = DL. empty
326
354
go (L src e : rest)
327
355
| src == dst = DL. fromList decs <> DL. fromList rest
328
356
| otherwise = DL. singleton (L src e) <> go rest
329
357
modifyDeclsT (pure . DL. toList . go) a
330
358
331
- graftSmallestDecls ::
332
- forall a .
333
- (HasDecls a ) =>
334
- SrcSpan ->
335
- [LHsDecl GhcPs ] ->
336
- Graft (Either String ) a
337
- graftSmallestDecls dst decs0 = Graft $ \ dflags a -> do
338
- decs <- forM decs0 $ \ decl -> do
339
- (anns, decl') <- annotateDecl dflags decl
340
- modifyAnnsT $ mappend anns
341
- pure decl'
342
- let go [] = DL. empty
343
- go (L src e : rest)
344
- | dst `isSubspanOf` src = DL. fromList decs <> DL. fromList rest
345
- | otherwise = DL. singleton (L src e) <> go rest
346
- modifyDeclsT (pure . DL. toList . go) a
347
-
348
359
graftSmallestDeclsWithM ::
349
360
forall a .
350
361
(HasDecls a ) =>
@@ -356,11 +367,8 @@ graftSmallestDeclsWithM dst toDecls = Graft $ \dflags a -> do
356
367
go (e@ (L src _) : rest)
357
368
| dst `isSubspanOf` src = toDecls e >>= \ case
358
369
Just decs0 -> do
359
- decs <- forM decs0 $ \ decl -> do
360
- (anns, decl') <-
361
- annotateDecl dflags decl
362
- modifyAnnsT $ mappend anns
363
- pure decl'
370
+ decs <- forM decs0 $ \ decl ->
371
+ annotateDecl dflags decl
364
372
pure $ DL. fromList decs <> DL. fromList rest
365
373
Nothing -> (DL. singleton e <> ) <$> go rest
366
374
| otherwise = (DL. singleton e <> ) <$> go rest
@@ -377,12 +385,9 @@ graftDeclsWithM dst toDecls = Graft $ \dflags a -> do
377
385
go (e@ (L src _) : rest)
378
386
| src == dst = toDecls e >>= \ case
379
387
Just decs0 -> do
380
- decs <- forM decs0 $ \ decl -> do
381
- (anns, decl') <-
382
- hoistTransform (either Fail. fail pure ) $
383
- annotateDecl dflags decl
384
- modifyAnnsT $ mappend anns
385
- pure decl'
388
+ decs <- forM decs0 $ \ decl ->
389
+ hoistTransform (either Fail. fail pure ) $
390
+ annotateDecl dflags decl
386
391
pure $ DL. fromList decs <> DL. fromList rest
387
392
Nothing -> (DL. singleton e <> ) <$> go rest
388
393
| otherwise = (DL. singleton e <> ) <$> go rest
@@ -461,7 +466,7 @@ annotate dflags needs_space ast = do
461
466
pure (anns', expr')
462
467
463
468
-- | Given an 'LHsDecl', compute its exactprint annotations.
464
- annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String ) (Anns , LHsDecl GhcPs )
469
+ annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String ) (LHsDecl GhcPs )
465
470
-- The 'parseDecl' function fails to parse 'FunBind' 'ValD's which contain
466
471
-- multiple matches. To work around this, we split the single
467
472
-- 'FunBind'-of-multiple-'Match'es into multiple 'FunBind's-of-one-'Match',
@@ -474,24 +479,23 @@ annotateDecl dflags
474
479
let set_matches matches =
475
480
ValD ext fb { fun_matches = mg { mg_alts = L alt_src matches }}
476
481
477
- (anns', alts') <- fmap unzip $ for ( zip [ 0 .. ] alts) $ \ (ix :: Int , alt ) -> do
482
+ (anns', alts') <- fmap unzip $ for alts $ \ alt -> do
478
483
uniq <- show <$> uniqueSrcSpanT
479
484
let rendered = render dflags $ set_matches [alt]
480
485
lift (mapLeft show $ parseDecl dflags uniq rendered) >>= \ case
481
486
(ann, L _ (ValD _ FunBind { fun_matches = MG { mg_alts = L _ [alt']}}))
482
- -> pure (bool id ( setPrecedingLines alt' 1 0 ) (ix /= 0 ) ann, alt')
487
+ -> pure (setPrecedingLines alt' 1 0 ann, alt')
483
488
_ -> lift $ Left " annotateDecl: didn't parse a single FunBind match"
484
489
485
- let expr' = L src $ set_matches alts'
486
- anns'' = setPrecedingLines expr' 1 0 $ fold anns'
487
-
488
- pure (anns'', expr')
490
+ modifyAnnsT $ mappend $ fold anns'
491
+ pure $ L src $ set_matches alts'
489
492
annotateDecl dflags ast = do
490
493
uniq <- show <$> uniqueSrcSpanT
491
494
let rendered = render dflags ast
492
495
(anns, expr') <- lift $ mapLeft show $ parseDecl dflags uniq rendered
493
496
let anns' = setPrecedingLines expr' 1 0 anns
494
- pure (anns', expr')
497
+ modifyAnnsT $ mappend anns'
498
+ pure expr'
495
499
496
500
------------------------------------------------------------------------------
497
501
@@ -504,3 +508,77 @@ render dflags = showSDoc dflags . ppr
504
508
-- | Put parentheses around an expression if required.
505
509
parenthesize :: LHsExpr GhcPs -> LHsExpr GhcPs
506
510
parenthesize = parenthesizeHsExpr appPrec
511
+
512
+
513
+ ------------------------------------------------------------------------------
514
+ -- Custom SYB machinery
515
+ ------------------------------------------------------------------------------
516
+
517
+ -- | Generic monadic transformations that return side-channel data.
518
+ type GenericMQ r m = forall a . Data a = > a -> m (r , a )
519
+
520
+ ------------------------------------------------------------------------------
521
+ -- | Apply the given 'GenericM' at all every node whose children fail the
522
+ -- 'GenericQ', but which passes the query itself.
523
+ --
524
+ -- The query must be a monotonic function when it returns 'Just'. That is, if
525
+ -- @s@ is a subtree of @t@, @q t@ should return @Just True@ if @q s@ does. It
526
+ -- is the True-to-false edge of the query that triggers the transformation.
527
+ --
528
+ -- Why is the query a @Maybe Bool@? The GHC AST intersperses 'Located' nodes
529
+ -- with data nodes, so for any given node we can only definitely return an
530
+ -- answer if it's a 'Located'. See 'genericIsSubspan' for how this parameter is
531
+ -- used.
532
+ smallestM :: forall m . Monad m => GenericQ (Maybe Bool ) -> GenericM m -> GenericM m
533
+ smallestM q f = fmap snd . go
534
+ where
535
+ go :: GenericMQ Any m
536
+ go x = do
537
+ case q x of
538
+ Nothing -> gmapMQ go x
539
+ Just True -> do
540
+ it@ (r, x') <- gmapMQ go x
541
+ case r of
542
+ Any True -> pure it
543
+ Any False -> fmap (Any True ,) $ f x'
544
+ Just False -> pure (mempty , x)
545
+
546
+ ------------------------------------------------------------------------------
547
+ -- | Apply the given 'GenericM' at every node that passes the 'GenericQ', but
548
+ -- don't descend into children if the query matches. Because this traversal is
549
+ -- root-first, this policy will find the largest subtrees for which the query
550
+ -- holds true.
551
+ --
552
+ -- Why is the query a @Maybe Bool@? The GHC AST intersperses 'Located' nodes
553
+ -- with data nodes, so for any given node we can only definitely return an
554
+ -- answer if it's a 'Located'. See 'genericIsSubspan' for how this parameter is
555
+ -- used.
556
+ largestM :: forall m . Monad m => GenericQ (Maybe Bool ) -> GenericM m -> GenericM m
557
+ largestM q f = go
558
+ where
559
+ go :: GenericM m
560
+ go x = do
561
+ case q x of
562
+ Just True -> f x
563
+ Just False -> pure x
564
+ Nothing -> gmapM go x
565
+
566
+ newtype MonadicQuery r m a = MonadicQuery
567
+ { runMonadicQuery :: m (r , a )
568
+ }
569
+ deriving stock (Functor )
570
+ deriving Applicative via Compose m ((,) r )
571
+
572
+
573
+ ------------------------------------------------------------------------------
574
+ -- | Like 'gmapM', but also returns side-channel data.
575
+ gmapMQ ::
576
+ forall f r a . (Monoid r , Data a , Applicative f ) =>
577
+ (forall d . Data d => d -> f (r , d )) ->
578
+ a ->
579
+ f (r , a )
580
+ gmapMQ f = runMonadicQuery . gfoldl k pure
581
+ where
582
+ k :: Data d => MonadicQuery r f (d -> b ) -> d -> MonadicQuery r f b
583
+ k c x = c <*> MonadicQuery (f x)
584
+
0 commit comments