@@ -18,6 +18,7 @@ import Control.Concurrent.STM.Stats (atomically)
1818import Control.DeepSeq (rwhnf )
1919import Control.Lens ((?~) , (^.) )
2020import Control.Monad (mzero )
21+ import Control.Monad.Except (ExceptT )
2122import Control.Monad.Extra (whenMaybe )
2223import Control.Monad.IO.Class (MonadIO (liftIO ))
2324import Control.Monad.Trans.Class (MonadTrans (lift ))
@@ -26,6 +27,7 @@ import qualified Data.Aeson.Types as A
2627import Data.Generics (GenericQ , everything ,
2728 extQ , mkQ , something )
2829import Data.List (find )
30+ import qualified Data.Map as M
2931import qualified Data.Map as Map
3032import Data.Maybe (catMaybes , fromMaybe ,
3133 mapMaybe , maybeToList )
@@ -79,15 +81,14 @@ import Language.LSP.Protocol.Message (Method (..),
7981 SMethod (.. ))
8082import 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 (|? ) (.. ))
9293import 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