Skip to content

Commit efc434b

Browse files
committed
feat: show local binding instead of just where clause
Now let and where clause will show binding type in inlay hints
1 parent 6d5f4af commit efc434b

File tree

7 files changed

+91
-77
lines changed

7 files changed

+91
-77
lines changed

ghcide/src/Development/IDE/Plugin/TypeLenses.hs

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
2424
import Data.Aeson.Types (toJSON)
2525
import qualified Data.Aeson.Types as A
2626
import Data.Generics (GenericQ, everything,
27-
mkQ, something)
27+
extQ, mkQ, something)
2828
import Data.List (find)
2929
import qualified Data.Map as Map
3030
import Data.Maybe (catMaybes, fromMaybe,
@@ -105,25 +105,25 @@ descriptor recorder plId =
105105
(defaultPluginDescriptor plId desc)
106106
{ pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeLens codeLensProvider
107107
<> mkResolveHandler SMethod_CodeLensResolve codeLensResolveProvider
108-
<> mkPluginHandler SMethod_TextDocumentInlayHint whereClauseInlayHints
108+
<> mkPluginHandler SMethod_TextDocumentInlayHint localBindingInlayHints
109109
, pluginCommands = [PluginCommand typeLensCommandId "adds a signature" commandHandler]
110-
, pluginRules = globalBindingRules recorder *> whereBindingRules recorder
110+
, pluginRules = globalBindingRules recorder *> localBindingRules recorder
111111
, pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}
112112
}
113113
where
114114
desc = "Provides code lenses type signatures"
115115

116116
properties :: Properties
117-
'[ 'PropertyKey "whereInlayHintOn" 'TBoolean,
117+
'[ 'PropertyKey "localBindingInlayHintOn" 'TBoolean,
118118
'PropertyKey "mode" ('TEnum Mode)]
119119
properties = emptyProperties
120120
& defineEnumProperty #mode "Control how type lenses are shown"
121121
[ (Always, "Always displays type lenses of global bindings")
122122
, (Exported, "Only display type lenses of exported global bindings")
123123
, (Diagnostics, "Follows error messages produced by GHC about missing signatures")
124124
] Always
125-
& defineBooleanProperty #whereInlayHintOn
126-
"Display type lenses of where bindings"
125+
& defineBooleanProperty #localBindingInlayHintOn
126+
"Display type lenses of local bindings"
127127
True
128128

129129
codeLensProvider :: PluginMethodHandler IdeState Method_TextDocumentCodeLens
@@ -376,23 +376,23 @@ pprPatSynTypeWithoutForalls p = pprPatSynType pWithoutTypeVariables
376376
-- --------------------------------------------------------------------------------
377377

378378
-- | A binding expression with its id and location.
379-
data WhereBinding = WhereBinding
379+
data LocalBinding = LocalBinding
380380
{ bindingId :: Id
381-
-- ^ Each WhereBinding represents an id in binding expression.
381+
-- ^ Each LocalBinding represents an id in binding expression.
382382
, bindingLoc :: SrcSpan
383383
-- ^ Location for an individual binding in a pattern.
384384
-- Here we use the 'bindingLoc' and offset to render the type signature at the proper place.
385385
, offset :: Int
386386
-- ^ Column offset between whole binding and individual binding in a pattern.
387387
--
388-
-- Example: For @(a, b) = (1, True)@, there will be two `WhereBinding`s:
389-
-- - `a`: WhereBinding id_a loc_a 0
390-
-- - `b`: WhereBinding id_b loc_b 4
388+
-- Example: For @(a, b) = (1, True)@, there will be two `LocalBinding`s:
389+
-- - `a`: LocalBinding id_a loc_a 0
390+
-- - `b`: LocalBinding id_b loc_b 4
391391
}
392392

393-
-- | Existing bindings in a where clause.
394-
data WhereBindings = WhereBindings
395-
{ bindings :: [WhereBinding]
393+
-- | Existing local bindings
394+
data LocalBindings = LocalBindings
395+
{ bindings :: [LocalBinding]
396396
, existingSigNames :: [Name]
397397
-- ^ Names of existing signatures.
398398
-- It is used to hide type lens for existing signatures.
@@ -409,69 +409,84 @@ data WhereBindings = WhereBindings
409409
-- the definition of `f`(second line).
410410
}
411411

412-
data GetWhereBindingTypeSigs = GetWhereBindingTypeSigs
412+
data GetLocalBindingTypeSigs = GetLocalBindingTypeSigs
413413
deriving (Generic, Show, Eq, Ord, Hashable, NFData)
414414

415415
type BindingSigMap = Map.Map Id String
416416

417-
newtype WhereBindingTypeSigsResult = WhereBindingTypeSigsResult ([WhereBindings], BindingSigMap)
417+
newtype LocalBindingTypeSigsResult = LocalBindingTypeSigsResult ([LocalBindings], BindingSigMap)
418418

419-
instance Show WhereBindingTypeSigsResult where
420-
show _ = "<GetTypeResult.where>"
419+
instance Show LocalBindingTypeSigsResult where
420+
show _ = "<GetTypeResult.local>"
421421

422-
instance NFData WhereBindingTypeSigsResult where
422+
instance NFData LocalBindingTypeSigsResult where
423423
rnf = rwhnf
424424

425-
type instance RuleResult GetWhereBindingTypeSigs = WhereBindingTypeSigsResult
425+
type instance RuleResult GetLocalBindingTypeSigs = LocalBindingTypeSigsResult
426426

427-
whereBindingRules :: Recorder (WithPriority Log) -> Rules ()
428-
whereBindingRules recorder = do
429-
define (cmapWithPrio LogShake recorder) $ \GetWhereBindingTypeSigs nfp -> do
427+
localBindingRules :: Recorder (WithPriority Log) -> Rules ()
428+
localBindingRules recorder = do
429+
define (cmapWithPrio LogShake recorder) $ \GetLocalBindingTypeSigs nfp -> do
430430
tmr <- use TypeCheck nfp
431431
-- we need session here for tidying types
432432
hsc <- use GhcSession nfp
433-
result <- liftIO $ whereBindingType (tmrTypechecked <$> tmr) (hscEnv <$> hsc)
433+
result <- liftIO $ localBindingType (tmrTypechecked <$> tmr) (hscEnv <$> hsc)
434434
pure ([], result)
435435

436-
whereBindingType :: Maybe TcGblEnv -> Maybe HscEnv -> IO (Maybe WhereBindingTypeSigsResult)
437-
whereBindingType (Just gblEnv) (Just hsc) = do
438-
let wheres = findWhereQ (tcg_binds gblEnv)
439-
localBindings = mapMaybe findBindingsQ wheres
436+
localBindingType :: Maybe TcGblEnv -> Maybe HscEnv -> IO (Maybe LocalBindingTypeSigsResult)
437+
localBindingType (Just gblEnv) (Just hsc) = do
438+
let locals = findLocalQ (tcg_binds gblEnv)
439+
localBindings = mapMaybe findBindingsQ locals
440440
bindToSig' = bindToSig hsc (tcg_rdr_env gblEnv)
441-
findSigs (WhereBindings bindings _) = fmap findSig bindings
442-
where findSig (WhereBinding bindingId _ _) = sequence (bindingId, bindToSig' bindingId)
441+
findSigs (LocalBindings bindings _) = fmap findSig bindings
442+
where findSig (LocalBinding bindingId _ _) = sequence (bindingId, bindToSig' bindingId)
443443
(_, Map.fromList . fromMaybe [] -> sigMap) <-
444444
initTcWithGbl hsc gblEnv ghostSpan $ sequence $ concatMap findSigs localBindings
445-
pure $ Just (WhereBindingTypeSigsResult (localBindings, sigMap))
446-
whereBindingType _ _ = pure Nothing
445+
pure $ Just (LocalBindingTypeSigsResult (localBindings, sigMap))
446+
localBindingType _ _ = pure Nothing
447447

448-
-- | All where clauses from type checked source.
449-
findWhereQ :: GenericQ [HsLocalBinds GhcTc]
450-
findWhereQ = everything (<>) $ mkQ [] (pure . findWhere)
448+
-- | All local bind expression from type checked source.
449+
findLocalQ :: GenericQ [HsLocalBinds GhcTc]
450+
findLocalQ = everything (<>) ([] `mkQ` (pure . findWhere) `extQ` findLet)
451451
where
452452
findWhere :: GRHSs GhcTc (LHsExpr GhcTc) -> HsLocalBinds GhcTc
453453
findWhere = grhssLocalBinds
454454

455-
-- | Find all bindings for **one** where clause.
456-
findBindingsQ :: GenericQ (Maybe WhereBindings)
455+
findLet :: LHsExpr GhcTc -> [HsLocalBinds GhcTc]
456+
findLet = findLetExpr . unLoc
457+
458+
findLetExpr :: HsExpr GhcTc -> [HsLocalBinds GhcTc]
459+
findLetExpr (HsLet _ _ binds _ _) = [binds]
460+
findLetExpr (HsDo _ _ (unLoc -> stmts)) = concatMap (findLetStmt . unLoc) stmts
461+
findLetExpr _ = []
462+
463+
findLetStmt :: ExprStmt GhcTc -> [HsLocalBinds GhcTc]
464+
findLetStmt (LetStmt _ binds) = [binds]
465+
-- TODO(jinser): why `foo <- expr` does not exist
466+
-- findLetStmt (BindStmt _ _ expr) = findLetExpr (unLoc expr)
467+
findLetStmt _ = []
468+
469+
-- | Find all bindings for **one** local bind expression.
470+
findBindingsQ :: GenericQ (Maybe LocalBindings)
457471
findBindingsQ = something (mkQ Nothing findBindings)
458472
where
459-
findBindings :: NHsValBindsLR GhcTc -> Maybe WhereBindings
473+
findBindings :: NHsValBindsLR GhcTc -> Maybe LocalBindings
460474
findBindings (NValBinds binds sigs) =
461-
Just $ WhereBindings
475+
Just $ LocalBindings
462476
{ bindings = concat $ mapMaybe (something (mkQ Nothing findBindingIds) . snd) binds
463477
, existingSigNames = concatMap findSigIds sigs
464478
}
465479

466-
findBindingIds :: LHsBindLR GhcTc GhcTc -> Maybe [WhereBinding]
480+
findBindingIds :: LHsBindLR GhcTc GhcTc -> Maybe [LocalBinding]
467481
findBindingIds bind = case unLoc bind of
468482
FunBind{..} ->
469-
let whereBinding = WhereBinding (unLoc fun_id) (getLoc fun_id)
483+
let localBinding = LocalBinding (unLoc fun_id) (getLoc fun_id)
470484
(col (getLoc fun_id) - col (getLoc bind))
471-
in Just $ pure whereBinding
472-
PatBind{..} -> Just $ (everything (<>) $ mkQ [] (fmap (uncurry wb) . maybeToList . findIdFromPat)) pat_lhs
485+
in Just $ pure localBinding
486+
PatBind{..} ->
487+
Just $ (everything (<>) $ mkQ [] (fmap (uncurry wb) . maybeToList . findIdFromPat)) pat_lhs
473488
where
474-
wb id srcSpan = WhereBinding id srcSpan (col srcSpan - col (getLoc pat_lhs))
489+
wb id srcSpan = LocalBinding id srcSpan (col srcSpan - col (getLoc pat_lhs))
475490
_ -> Nothing
476491
where
477492
col = srcSpanStartCol . realSrcSpan
@@ -485,29 +500,28 @@ findBindingsQ = something (mkQ Nothing findBindings)
485500
findSigIds (L _ (TypeSig _ names _)) = map unLoc names
486501
findSigIds _ = []
487502

488-
-- | Provide code lens for where bindings.
489-
whereClauseInlayHints :: PluginMethodHandler IdeState Method_TextDocumentInlayHint
490-
whereClauseInlayHints state plId (InlayHintParams _ (TextDocumentIdentifier uri) visibleRange) = do
491-
enabled <- liftIO $ runAction "inlayHint.config" state $ usePropertyAction #whereInlayHintOn plId properties
503+
-- | Provide code lens for local bindings.
504+
localBindingInlayHints :: PluginMethodHandler IdeState Method_TextDocumentInlayHint
505+
localBindingInlayHints state plId (InlayHintParams _ (TextDocumentIdentifier uri) visibleRange) = do
506+
enabled <- liftIO $ runAction "inlayHint.config" state $ usePropertyAction #localBindingInlayHintOn plId properties
492507
if not enabled then pure $ InL [] else do
493508
nfp <- getNormalizedFilePathE uri
494-
(WhereBindingTypeSigsResult (localBindings, sigMap), pm)
495-
<- runActionE "InlayHint.GetWhereBindingTypeSigs" state $ useWithStaleE GetWhereBindingTypeSigs nfp
509+
(LocalBindingTypeSigsResult (localBindings, sigMap), pm)
510+
<- runActionE "InlayHint.GetWhereBindingTypeSigs" state $ useWithStaleE GetLocalBindingTypeSigs nfp
496511
let bindingToInlayHints id sig = generateWhereInlayHints (T.pack $ printName (idName id)) (maybe "_" T.pack sig)
497512

498513
-- | Note there may multi ids for one binding,
499514
-- like @(a, b) = (42, True)@, there are `a` and `b`
500515
-- in one binding.
501516
inlayHints =
502517
[ bindingToInlayHints bindingId bindingSig bindingRange offset
503-
| WhereBindings{..} <- localBindings
518+
| LocalBindings{..} <- localBindings
504519
, let sigSpans = getSrcSpan <$> existingSigNames
505-
, WhereBinding{..} <- bindings
520+
, LocalBinding{..} <- bindings
506521
, let bindingSpan = getSrcSpan (idName bindingId)
507522
, let bindingSig = Map.lookup bindingId sigMap
508523
, bindingSpan `notElem` sigSpans
509524
, Just bindingRange <- maybeToList $ toCurrentRange pm <$> srcSpanToRange bindingLoc
510-
-- , Just bindingRange <- [srcSpanToRange bindingLoc]
511525
-- Show inlay hints only within visible range
512526
, isSubrangeOf bindingRange visibleRange
513527
]

test/testdata/schema/ghc94/default-config.golden.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
"ghcide-type-lenses": {
8686
"codeLensOn": true,
8787
"config": {
88-
"mode": "always",
89-
"whereInlayHintOn": true
88+
"localBindingInlayHintOn": true,
89+
"mode": "always"
9090
},
9191
"inlayHintsOn": true
9292
},

test/testdata/schema/ghc94/vscode-extension-schema.golden.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@
179179
"scope": "resource",
180180
"type": "boolean"
181181
},
182+
"haskell.plugin.ghcide-type-lenses.config.localBindingInlayHintOn": {
183+
"default": true,
184+
"markdownDescription": "Display type lenses of local bindings",
185+
"scope": "resource",
186+
"type": "boolean"
187+
},
182188
"haskell.plugin.ghcide-type-lenses.config.mode": {
183189
"default": "always",
184190
"description": "Control how type lenses are shown",
@@ -195,12 +201,6 @@
195201
"scope": "resource",
196202
"type": "string"
197203
},
198-
"haskell.plugin.ghcide-type-lenses.config.whereInlayHintOn": {
199-
"default": true,
200-
"markdownDescription": "Display type lenses of where bindings",
201-
"scope": "resource",
202-
"type": "boolean"
203-
},
204204
"haskell.plugin.ghcide-type-lenses.inlayHintsOn": {
205205
"default": true,
206206
"description": "Enables ghcide-type-lenses inlay hints",

test/testdata/schema/ghc96/default-config.golden.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
"ghcide-type-lenses": {
8686
"codeLensOn": true,
8787
"config": {
88-
"mode": "always",
89-
"whereInlayHintOn": true
88+
"localBindingInlayHintOn": true,
89+
"mode": "always"
9090
},
9191
"inlayHintsOn": true
9292
},

test/testdata/schema/ghc96/vscode-extension-schema.golden.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@
179179
"scope": "resource",
180180
"type": "boolean"
181181
},
182+
"haskell.plugin.ghcide-type-lenses.config.localBindingInlayHintOn": {
183+
"default": true,
184+
"markdownDescription": "Display type lenses of local bindings",
185+
"scope": "resource",
186+
"type": "boolean"
187+
},
182188
"haskell.plugin.ghcide-type-lenses.config.mode": {
183189
"default": "always",
184190
"description": "Control how type lenses are shown",
@@ -195,12 +201,6 @@
195201
"scope": "resource",
196202
"type": "string"
197203
},
198-
"haskell.plugin.ghcide-type-lenses.config.whereInlayHintOn": {
199-
"default": true,
200-
"markdownDescription": "Display type lenses of where bindings",
201-
"scope": "resource",
202-
"type": "boolean"
203-
},
204204
"haskell.plugin.ghcide-type-lenses.inlayHintsOn": {
205205
"default": true,
206206
"description": "Enables ghcide-type-lenses inlay hints",

test/testdata/schema/ghc98/default-config.golden.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
"ghcide-type-lenses": {
8686
"codeLensOn": true,
8787
"config": {
88-
"mode": "always",
89-
"whereInlayHintOn": true
88+
"localBindingInlayHintOn": true,
89+
"mode": "always"
9090
},
9191
"inlayHintsOn": true
9292
},

test/testdata/schema/ghc98/vscode-extension-schema.golden.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@
179179
"scope": "resource",
180180
"type": "boolean"
181181
},
182+
"haskell.plugin.ghcide-type-lenses.config.localBindingInlayHintOn": {
183+
"default": true,
184+
"markdownDescription": "Display type lenses of local bindings",
185+
"scope": "resource",
186+
"type": "boolean"
187+
},
182188
"haskell.plugin.ghcide-type-lenses.config.mode": {
183189
"default": "always",
184190
"description": "Control how type lenses are shown",
@@ -195,12 +201,6 @@
195201
"scope": "resource",
196202
"type": "string"
197203
},
198-
"haskell.plugin.ghcide-type-lenses.config.whereInlayHintOn": {
199-
"default": true,
200-
"markdownDescription": "Display type lenses of where bindings",
201-
"scope": "resource",
202-
"type": "boolean"
203-
},
204204
"haskell.plugin.ghcide-type-lenses.inlayHintsOn": {
205205
"default": true,
206206
"description": "Enables ghcide-type-lenses inlay hints",

0 commit comments

Comments
 (0)