Skip to content

Commit d2d8633

Browse files
author
Cassandra Comar
committed
allow either code lens or inlay hints for local binds
1 parent 330cbe9 commit d2d8633

File tree

1 file changed

+84
-51
lines changed

1 file changed

+84
-51
lines changed

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

Lines changed: 84 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Control.Concurrent.STM.Stats (atomically)
1818
import Control.DeepSeq (rwhnf)
1919
import Control.Lens ((?~), (^.))
2020
import Control.Monad (mzero)
21+
import Control.Monad.Except (ExceptT)
2122
import Control.Monad.Extra (whenMaybe)
2223
import Control.Monad.IO.Class (MonadIO (liftIO))
2324
import Control.Monad.Trans.Class (MonadTrans (lift))
@@ -26,6 +27,7 @@ import qualified Data.Aeson.Types as A
2627
import Data.Generics (GenericQ, everything,
2728
extQ, mkQ, something)
2829
import Data.List (find)
30+
import qualified Data.Map as M
2931
import qualified Data.Map as Map
3032
import Data.Maybe (catMaybes, fromMaybe,
3133
mapMaybe, maybeToList)
@@ -79,15 +81,14 @@ import Language.LSP.Protocol.Message (Method (..),
7981
SMethod (..))
8082
import Language.LSP.Protocol.Types (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams),
8183
CodeLens (..),
82-
CodeLensParams (CodeLensParams, _textDocument),
84+
CodeLensParams (..),
8385
Command, Diagnostic (..),
8486
InlayHint (..),
8587
InlayHintParams (InlayHintParams),
8688
Null (Null),
8789
TextDocumentIdentifier (TextDocumentIdentifier),
8890
TextEdit (TextEdit),
8991
WorkspaceEdit (WorkspaceEdit),
90-
isSubrangeOf,
9192
type (|?) (..))
9293
import Text.Regex.TDFA ((=~))
9394

@@ -107,6 +108,7 @@ descriptor recorder plId =
107108
{ pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeLens codeLensProvider
108109
<> mkResolveHandler SMethod_CodeLensResolve codeLensResolveProvider
109110
<> mkPluginHandler SMethod_TextDocumentInlayHint localBindingInlayHints
111+
<> mkPluginHandler SMethod_TextDocumentCodeLens localBindingCodeLens
110112
, pluginCommands = [PluginCommand typeLensCommandId "adds a signature" commandHandler]
111113
, pluginRules = globalBindingRules recorder *> localBindingRules recorder
112114
, pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}
@@ -512,54 +514,85 @@ findBindingsQ = something (mkQ Nothing findBindings)
512514
findSigIds (unLoc -> (TypeSig _ names _)) = map unLoc names
513515
findSigIds _ = []
514516

515-
-- | Provide code lens for local bindings.
516-
localBindingInlayHints :: PluginMethodHandler IdeState Method_TextDocumentInlayHint
517-
localBindingInlayHints state plId (InlayHintParams _ (TextDocumentIdentifier uri) visibleRange) = do
518-
enabled <- liftIO $ runAction "inlayHint.config" state $ usePropertyAction #localBindingInlayHintOn plId properties
519-
if not enabled then pure $ InL [] else do
517+
type LocalBindingHintRenderer a = Id -> T.Text -> Range -> Int -> a
518+
519+
generateWhereInlayHints :: LocalBindingHintRenderer InlayHint
520+
generateWhereInlayHints name ty range offset =
521+
let edit = makeEdit range ((T.pack $ printName (idName name)) <> " :: " <> ty) offset
522+
in InlayHint { _textEdits = Just [edit]
523+
, _paddingRight = Nothing
524+
, _paddingLeft = Just True
525+
, _tooltip = Nothing
526+
, _position = _end range
527+
, _kind = Nothing
528+
, _label = InL $ ":: " <> ty
529+
, _data_ = Nothing
530+
}
531+
where
532+
makeEdit :: Range -> T.Text -> Int -> TextEdit
533+
makeEdit range text offset =
534+
let startPos = range ^. L.start
535+
-- Subtract the offset to align with the whole binding expression
536+
insertChar = _character startPos - fromIntegral offset
537+
startPos' = startPos { _character = insertChar }
538+
insertRange = Range startPos' startPos'
539+
in TextEdit insertRange (text <> "\n" <> T.replicate (fromIntegral insertChar) " ")
540+
541+
generateWhereLens :: PluginId -> Uri -> Id -> T.Text -> Range -> Int -> CodeLens
542+
generateWhereLens plId uri _ title range _ = CodeLens range (Just cmd) Nothing
543+
where
544+
cmd = mkLspCommand plId typeLensCommandId title (Just [A.toJSON (makeEdit range title)])
545+
makeEdit :: Range -> T.Text -> WorkspaceEdit
546+
makeEdit range text =
547+
let startPos = range ^. L.start
548+
insertChar = startPos ^. L.character
549+
insertRange = Range startPos startPos
550+
in WorkspaceEdit
551+
(Just $ M.fromList [(uri, [TextEdit insertRange (text <> "\n" <> T.replicate (fromIntegral insertChar) " ")])])
552+
Nothing
553+
Nothing
554+
555+
556+
bindingToHints :: LocalBindingHintRenderer a -> Id -> Maybe String -> Range -> Int -> Maybe a
557+
bindingToHints render id (Just sig) range offset = Just $ render id (T.pack sig) range offset
558+
bindingToHints _ _ Nothing _ _ = Nothing
559+
560+
renderLocalHints :: MonadIO m => LocalBindingHintRenderer a -> Uri -> IdeState -> ExceptT PluginError m ([a] |? b)
561+
renderLocalHints render uri state = do
520562
nfp <- getNormalizedFilePathE uri
521563
(LocalBindingTypeSigsResult (localBindings, sigMap), pm)
522-
<- runActionE "InlayHint.GetWhereBindingTypeSigs" state $ useWithStaleE GetLocalBindingTypeSigs nfp
523-
let bindingToInlayHints :: Id -> Maybe String -> Range -> Int -> Maybe InlayHint
524-
bindingToInlayHints id (Just sig) range offset =
525-
Just $ generateWhereInlayHints (T.pack $ printName (idName id)) (T.pack sig) range offset
526-
bindingToInlayHints _ Nothing _ _ = Nothing
527-
528-
-- | Note there may multi ids for one binding,
529-
-- like @(a, b) = (42, True)@, there are `a` and `b`
530-
-- in one binding.
531-
inlayHints = catMaybes
532-
[ bindingToInlayHints bindingId bindingSig bindingRange offset
533-
| LocalBindings{..} <- localBindings
534-
, let sigSpans = getSrcSpan <$> existingSigNames
535-
, LocalBinding{..} <- bindings
536-
, let bindingSpan = getSrcSpan (idName bindingId)
537-
, let bindingSig = Map.lookup bindingId sigMap
538-
, bindingSpan `notElem` sigSpans
539-
, Just bindingRange <- maybeToList $ toCurrentRange pm <$> srcSpanToRange bindingLoc
540-
-- Show inlay hints only within visible range
541-
, isSubrangeOf bindingRange visibleRange
542-
]
543-
pure $ InL inlayHints
544-
where
545-
generateWhereInlayHints :: T.Text -> T.Text -> Range -> Int -> InlayHint
546-
generateWhereInlayHints name ty range offset =
547-
let edit = makeEdit range (name <> " :: " <> ty) offset
548-
in InlayHint { _textEdits = Just [edit]
549-
, _paddingRight = Nothing
550-
, _paddingLeft = Just True
551-
, _tooltip = Nothing
552-
, _position = _end range
553-
, _kind = Nothing
554-
, _label = InL $ ":: " <> ty
555-
, _data_ = Nothing
556-
}
557-
558-
makeEdit :: Range -> T.Text -> Int -> TextEdit
559-
makeEdit range text offset =
560-
let startPos = range ^. L.start
561-
-- Subtract the offset to align with the whole binding expression
562-
insertChar = _character startPos - fromIntegral offset
563-
startPos' = startPos { _character = insertChar }
564-
insertRange = Range startPos' startPos'
565-
in TextEdit insertRange (text <> "\n" <> T.replicate (fromIntegral insertChar) " ")
564+
<- runActionE "InlayHint.GetWhereBindingTypeSigs" state $ useWithStaleE GetLocalBindingTypeSigs nfp
565+
566+
-- | Note there may multi ids for one binding,
567+
-- like @(a, b) = (42, True)@, there are `a` and `b`
568+
-- in one binding.
569+
let hints = catMaybes
570+
[ bindingToHints render bindingId bindingSig bindingRange offset
571+
| LocalBindings{..} <- localBindings
572+
, let sigSpans = getSrcSpan <$> existingSigNames
573+
, LocalBinding{..} <- bindings
574+
, let bindingSpan = getSrcSpan (idName bindingId)
575+
, let bindingSig = Map.lookup bindingId sigMap
576+
, bindingSpan `notElem` sigSpans
577+
, Just bindingRange <- maybeToList $ toCurrentRange pm <$> srcSpanToRange bindingLoc
578+
-- Show inlay hints only within visible range
579+
-- TODO: there's no "visibleRange" on CodeLens'
580+
-- , isSubrangeOf bindingRange visibleRange
581+
]
582+
pure $ InL hints
583+
584+
localBindingCodeLens :: PluginMethodHandler IdeState Method_TextDocumentCodeLens
585+
localBindingCodeLens state plId (CodeLensParams{..}) = do
586+
enabled <- liftIO $ runAction "inlayHint.config" state $ usePropertyAction #localBindingInlayHintOn plId properties
587+
let uri = _textDocument ^. L.uri
588+
if enabled
589+
then pure $ InL []
590+
else renderLocalHints (generateWhereLens plId uri) uri state
591+
592+
-- | Provide inlay hints for local bindings
593+
localBindingInlayHints :: PluginMethodHandler IdeState Method_TextDocumentInlayHint
594+
localBindingInlayHints state plId (InlayHintParams _ (TextDocumentIdentifier uri) _) = do
595+
enabled <- liftIO $ runAction "inlayHint.config" state $ usePropertyAction #localBindingInlayHintOn plId properties
596+
if not enabled
597+
then pure $ InL []
598+
else renderLocalHints generateWhereInlayHints uri state

0 commit comments

Comments
 (0)