]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add support for enabling/disabling inlay hints
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 8 Jul 2023 02:33:58 +0000 (19:33 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 8 Jul 2023 02:33:58 +0000 (19:33 -0700)
autoload/lsp/buffer.vim
autoload/lsp/diag.vim
autoload/lsp/inlayhints.vim
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/options.vim
doc/lsp.txt
plugin/lsp.vim
test/clangd_tests.vim

index 85a07ca2fc0e009a18ef57c746c65dfeca2b2499..d2ccc8f477643d75c67a0b4335fe51b5afa6016d 100644 (file)
@@ -48,7 +48,8 @@ var SupportedCheckFns = {
   foldingRange: (lspserver) => lspserver.isFoldingRangeProvider,
   hover: (lspserver) => lspserver.isHoverProvider,
   implementation: (lspserver) => lspserver.isImplementationProvider,
-  inlayHint: (lspserver) => lspserver.isInlayHintProvider,
+  inlayHint: (lspserver) => lspserver.isInlayHintProvider ||
+                                       lspserver.isClangdInlayHintsProvider,
   references: (lspserver) => lspserver.isReferencesProvider,
   rename: (lspserver) => lspserver.isRenameProvider,
   selectionRange: (lspserver) => lspserver.isSelectionRangeProvider,
index 81e9936ed854167940d705c74001bcd8bdac5da1..583c020664513b0e77a2592db5f7605fe59eb41a 100644 (file)
@@ -782,7 +782,7 @@ export def DiagsHighlightDisable()
   # turn off all diags highlight
   opt.lspOptions.autoHighlightDiags = false
   for binfo in getbufinfo({bufloaded: true})
-      RemoveDiagVisualsForBuffer(binfo.bufnr)
+    RemoveDiagVisualsForBuffer(binfo.bufnr)
   endfor
 enddef
 
index 37efdc4eef43ae0d83cb770e0e7f709eda8f0508..bff0e93ea34e741b45ceefd3db689d97ec1fdf91 100644 (file)
@@ -15,28 +15,32 @@ export def InitOnce()
   ])
   prop_type_add('LspInlayHintsType', {highlight: 'LspInlayHintsType'})
   prop_type_add('LspInlayHintsParam', {highlight: 'LspInlayHintsParam'})
+
+  autocmd_add([{group: 'LspOptionsChanged',
+               event: 'User',
+               pattern: '*',
+               cmd: 'LspInlayHintsOptionsChanged()'}])
 enddef
 
 # Clear all the inlay hints text properties in the current buffer
-def InlayHintsClear(lspserver: dict<any>)
-  prop_remove({type: 'LspInlayHintsType', bufnr: bufnr('%'), all: true})
-  prop_remove({type: 'LspInlayHintsParam', bufnr: bufnr('%'), all: true})
+def InlayHintsClear(bnr: number)
+  prop_remove({type: 'LspInlayHintsType', bufnr: bnr, all: true})
+  prop_remove({type: 'LspInlayHintsParam', bufnr: bnr, all: true})
 enddef
 
 # LSP inlay hints reply message handler
-export def InlayHintsReply(lspserver: dict<any>, inlayHints: any)
+export def InlayHintsReply(lspserver: dict<any>, bnr: number, inlayHints: any)
   if inlayHints->empty()
     return
   endif
 
-  InlayHintsClear(lspserver)
+  InlayHintsClear(bnr)
 
   if mode() != 'n'
     # Update inlay hints only in normal mode
     return
   endif
 
-  var bnr = bufnr('%')
   for hint in inlayHints
     var label = ''
     if hint.label->type() == v:t_list
@@ -65,56 +69,61 @@ export def InlayHintsReply(lspserver: dict<any>, inlayHints: any)
 enddef
 
 # Timer callback to display the inlay hints.
-def InlayHintsCallback(lspserver: dict<any>, timerid: number)
-  lspserver.inlayHintsShow()
-  b:LspInlayHintsNeedsUpdate = false
+def InlayHintsTimerCb(lspserver: dict<any>, bnr: number, timerid: number)
+  lspserver.inlayHintsShow(bnr)
+  setbufvar(bnr, 'LspInlayHintsNeedsUpdate', false)
 enddef
 
 # Update all the inlay hints.  A timer is used to throttle the updates.
-def LspInlayHintsUpdate()
-  if !get(b:, 'LspInlayHintsNeedsUpdate', true)
+def LspInlayHintsUpdate(bnr: number)
+  if !getbufvar(bnr, 'LspInlayHintsNeedsUpdate', true)
     return
   endif
 
-  var timerid = get(b:, 'LspInlayHintsTimer', -1)
+  var timerid = getbufvar(bnr, 'LspInlayHintsTimer', -1)
   if timerid != -1
     timerid->timer_stop()
-    b:LspInlayHintsTimer = -1
+    setbufvar(bnr, 'LspInlayHintsTimer', -1)
   endif
 
-  var lspserver: dict<any> = buf.CurbufGetServerChecked('inlayHint')
+  var lspserver: dict<any> = buf.BufLspServerGet(bnr, 'inlayHint')
   if lspserver->empty()
     return
   endif
 
-  timerid = timer_start(300, function('InlayHintsCallback', [lspserver]))
-  b:LspInlayHintsTimer = timerid
+  if get(g:, 'LSPTest')
+    # When running tests, update the inlay hints immediately
+    InlayHintsTimerCb(lspserver, bnr, -1)
+  else
+    timerid = timer_start(300, function('InlayHintsTimerCb', [lspserver, bnr]))
+    setbufvar(bnr, 'LspInlayHintsTimer', timerid)
+  endif
 enddef
 
 # Text is modified. Need to update the inlay hints.
-def LspInlayHintsChanged()
-  b:LspInlayHintsNeedsUpdate = true
+def LspInlayHintsChanged(bnr: number)
+  setbufvar(bnr, 'LspInlayHintsNeedsUpdate', true)
 enddef
 
 # Trigger an update of the inlay hints in the current buffer.
-export def LspInlayHintsUpdateNow()
-  b:LspInlayHintsNeedsUpdate = true
-  LspInlayHintsUpdate()
+export def LspInlayHintsUpdateNow(bnr: number)
+  setbufvar(bnr, 'LspInlayHintsNeedsUpdate', true)
+  LspInlayHintsUpdate(bnr)
 enddef
 
 # Stop updating the inlay hints.
-def LspInlayHintsUpdateStop()
-  var timerid = get(b:, 'LspInlayHintsTimer', -1)
+def LspInlayHintsUpdateStop(bnr: number)
+  var timerid = getbufvar(bnr, 'LspInlayHintsTimer', -1)
   if timerid != -1
     timerid->timer_stop()
-    b:LspInlayHintsTimer = -1
+    setbufvar(bnr, 'LspInlayHintsTimer', -1)
   endif
 enddef
 
 # Do buffer-local initialization for displaying inlay hints
 export def BufferInit(lspserver: dict<any>, bnr: number)
   if !lspserver.isInlayHintProvider && !lspserver.isClangdInlayHintsProvider
-    # no support for inley hints
+    # no support for inlay hints
     return
   endif
 
@@ -129,26 +138,75 @@ export def BufferInit(lspserver: dict<any>, bnr: number)
   # time.
   acmds->add({bufnr: bnr,
                event: ['CursorHold'],
-               group: 'LSPBufferAutocmds',
-               cmd: 'LspInlayHintsUpdate()'})
+               group: 'LspInlayHints',
+               cmd: $'LspInlayHintsUpdate({bnr})'})
   # After the text in the current buffer is modified, the inlay hints need to
   # be updated.
   acmds->add({bufnr: bnr,
                event: ['TextChanged'],
-               group: 'LSPBufferAutocmds',
-               cmd: 'LspInlayHintsChanged()'})
+               group: 'LspInlayHints',
+               cmd: $'LspInlayHintsChanged({bnr})'})
   # Editing a file should trigger an inlay hint update.
   acmds->add({bufnr: bnr,
                event: ['BufReadPost'],
-               group: 'LSPBufferAutocmds',
-               cmd: 'LspInlayHintsUpdateNow()'})
+               group: 'LspInlayHints',
+               cmd: $'LspInlayHintsUpdateNow({bnr})'})
   # Inlay hints need not be updated if a buffer is no longer active.
   acmds->add({bufnr: bnr,
                event: ['BufLeave'],
-               group: 'LSPBufferAutocmds',
-               cmd: 'LspInlayHintsUpdateStop()'})
+               group: 'LspInlayHints',
+               cmd: $'LspInlayHintsUpdateStop({bnr})'})
 
   autocmd_add(acmds)
 enddef
 
+var inlayHintsEnabled = opt.lspOptions.showInlayHints
+
+# Enable inlay hints.  For all the buffers with an attached language server
+# that supports inlay hints, refresh the inlay hints.
+export def InlayHintsEnable()
+  opt.lspOptions.showInlayHints = true
+  for binfo in getbufinfo()
+    var lspservers: list<dict<any>> = buf.BufLspServersGet(binfo.bufnr)
+    if lspservers->empty()
+      continue
+    endif
+    for lspserver in lspservers
+      if !lspserver.isInlayHintProvider &&
+         !lspserver.isClangdInlayHintsProvider
+       continue
+      endif
+      BufferInit(lspserver, binfo.bufnr)
+      LspInlayHintsUpdateNow(binfo.bufnr)
+    endfor
+  endfor
+  inlayHintsEnabled = true
+enddef
+
+# Disable inlay hints for the current Vim session.  Clear the inlay hints in
+# all the buffers.
+export def InlayHintsDisable()
+  opt.lspOptions.showInlayHints = false
+  for binfo in getbufinfo()
+    var lspserver: dict<any> = buf.BufLspServerGet(binfo.bufnr, 'inlayHint')
+    if lspserver->empty()
+      continue
+    endif
+    LspInlayHintsUpdateStop(binfo.bufnr)
+    :silent! autocmd_delete([{bufnr: binfo.bufnr, group: 'LspInlayHints'}])
+    InlayHintsClear(binfo.bufnr)
+  endfor
+  inlayHintsEnabled = false
+enddef
+
+# Some options are changed.  If 'showInlayHints' option is changed, then
+# either enable or disable inlay hints.
+export def LspInlayHintsOptionsChanged()
+  if inlayHintsEnabled && !opt.lspOptions.showInlayHints
+    InlayHintsDisable()
+  elseif !inlayHintsEnabled && opt.lspOptions.showInlayHints
+    InlayHintsEnable()
+  endif
+enddef
+
 # vim: tabstop=8 shiftwidth=2 softtabstop=2
index adc8e7cb5c0a4f1a649ce44b414ab7c6a008cf7c..7b9ed6808a4182ab0b197e259a1cd2940ccc9e9c 100644 (file)
@@ -799,6 +799,23 @@ export def Hover(cmdmods: string)
   lspserver.hover(cmdmods)
 enddef
 
+# Enable or disable inlay hints
+export def InlayHints(ctl: string)
+  if ctl == 'enable'
+    inlayhints.InlayHintsEnable()
+  elseif ctl == 'disable'
+    inlayhints.InlayHintsDisable()
+  else
+    util.ErrMsg($'LspInlayHints - Unsupported argument "{ctl}"')
+  endif
+enddef
+
+# Command-line completion for the ":LspInlayHints" command
+export def LspInlayHintsComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
+  var l = ['enable', 'disable']
+  return filter(l, (_, val) => val =~ $'^{arglead}')
+enddef
+
 # show symbol references
 export def ShowReferences(peek: bool)
   var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
index 70976748ee2ef15190c81cf0cb03c573f1889b2b..4366d7334d95520d6b21d06640b5b29d48bc83af 100644 (file)
@@ -151,7 +151,7 @@ def ServerInitReply(lspserver: dict<any>, initResult: dict<any>): void
   # Update the inlay hints (if enabled)
   if opt.lspOptions.showInlayHints && (lspserver.isInlayHintProvider
                                    || lspserver.isClangdInlayHintsProvider)
-    inlayhints.LspInlayHintsUpdateNow()
+    inlayhints.LspInlayHintsUpdateNow(bufnr())
   endif
 enddef
 
@@ -1197,24 +1197,33 @@ enddef
 
 # Request: "textDocument/inlayHint"
 # Inlay hints.
-def InlayHintsShow(lspserver: dict<any>)
+def InlayHintsShow(lspserver: dict<any>, bnr: number)
   # Check whether LSP server supports type hierarchy
   if !lspserver.isInlayHintProvider && !lspserver.isClangdInlayHintsProvider
     util.ErrMsg('LSP server does not support inlay hint')
     return
   endif
 
-  var lastlnum = line('$')
+  var binfo = bnr->getbufinfo()
+  if binfo->empty()
+    return
+  endif
+  var lastlnum = binfo[0].linecount
+  var lastline = bnr->getbufline('$')
+  var lastcol = 1
+  if !lastline->empty() && !lastline[0]->empty()
+    lastcol = lastline[0]->strchars()
+  endif
   var param = {
-      textDocument: {uri: util.LspFileToUri(@%)},
+      textDocument: {uri: util.LspBufnrToUri(bnr)},
       range:
       {
        start: {line: 0, character: 0},
-       end: {line: lastlnum - 1, character: charcol([lastlnum, '$']) - 1}
+       end: {line: lastlnum - 1, character: lastcol - 1}
       }
   }
 
-  lspserver.encodeRange(bufnr(), param.range)
+  lspserver.encodeRange(bnr, param.range)
 
   var msg: string
   if lspserver.isClangdInlayHintsProvider
@@ -1223,7 +1232,9 @@ def InlayHintsShow(lspserver: dict<any>)
   else
     msg = 'textDocument/inlayHint'
   endif
-  var reply = lspserver.rpc_a(msg, param, inlayhints.InlayHintsReply)
+  var reply = lspserver.rpc_a(msg, param, (_, reply) => {
+    inlayhints.InlayHintsReply(lspserver, bnr, reply)
+  })
 enddef
 
 def DecodeTypeHierarchy(lspserver: dict<any>, isSuper: bool, typeHier: dict<any>)
index 5a787e8206387e7412a4302dce6fb20d6ef49043..754a6fb9f656a0091b7c45f783455fa90097c91d 100644 (file)
@@ -113,6 +113,11 @@ export def OptionsSet(opts: dict<any>)
   else
     lspOptions.completionMatcherValue = COMPLETIONMATCHER_CASE
   endif
+
+  # Apply the changed options
+  if exists('#LspOptionsChanged#User')
+    :doautocmd <nomodeline> LspOptionsChanged User
+  endif
 enddef
 
 # return a copy of the LSP plugin options
index cec1a2366259622d175c448e1e9b614001cec0cf..b530f5b03b93e413d6637849cec2572a320ede38 100644 (file)
@@ -107,6 +107,7 @@ The following commands are provided:
                        in a popup window.
 :LspIncomingCalls      Display the list of symbols calling the current symbol
                        in a window.
+:LspInlayHints         Enable or disable inlay hints.
 :LspOutgoingCalls      Display the list of symbols called by the current
                        symbol in a window.
 :LspOutline            Show the list of symbols defined in the current file
@@ -871,6 +872,13 @@ can map these commands to keys and make it easier to invoke them.
                        |lsp-call-hierarchy| for more information.  Note that
                        not all the language servers support this feature.
 
+                                               *:LspInlayHints*
+:LspInlayHints         Enable or disable inlay hints.  Supports the "enable"
+                       and "disable" arguments.  When "enable" is specified,
+                       enables the inlay hints for all the buffers with a
+                       language server that supports inlay hints.  When
+                       "disable" is specified, disables the inlay hints.
+
                                                *:LspOutoingCalls*
 :LspOutoingCalls       Display a hierarchy of symbols called by the symbol
                        under the cursor in a window.  See
index d89f3ae779d2df2388af5e6ed51c13ac3f07b79d..2de7df5b1bcd39d0171d92e3cec8433f860c3ffd 100644 (file)
@@ -77,6 +77,7 @@ command! -nargs=0 -bar -count LspGotoTypeDef lsp.GotoTypedef(v:false, <q-mods>,
 command! -nargs=0 -bar LspHighlight call LspDocHighlight(<q-mods>)
 command! -nargs=0 -bar LspHighlightClear call LspDocHighlightClear()
 command! -nargs=? -bar LspHover lsp.Hover(<q-mods>)
+command! -nargs=1 -bar -complete=customlist,lsp.LspInlayHintsComplete LspInlayHints lsp.InlayHints(<q-args>)
 command! -nargs=0 -bar LspIncomingCalls lsp.IncomingCalls()
 command! -nargs=0 -bar LspOutgoingCalls lsp.OutgoingCalls()
 command! -nargs=0 -bar -count LspOutline lsp.Outline(<q-mods>, <count>)
index 2ee69e040b4a31bb76a7a68614b4538710235b3b..b32f06bc2b7950d57ed7d955d77b24fbefd2777d 100644 (file)
@@ -1499,6 +1499,60 @@ def g:Test_OmniComplete_Struct()
   :%bw!
 enddef
 
+# Test for inlay hints
+def g:Test_InlayHints()
+  :silent! edit XinlayHints.c
+  sleep 200m
+  var lines: list<string> =<< trim END
+    void func1(int a, int b)
+    {
+    }
+
+    void func2()
+    {
+      func1(10, 20);
+    }
+  END
+  setline(1, lines)
+  g:WaitForServerFileLoad(0)
+  redraw!
+
+  :LspInlayHints enable
+  assert_equal([{id: -1, col: 9, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1},
+               {id: -2, col: 13, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1}],
+               prop_list(7))
+
+  :LspInlayHints disable
+  assert_equal([], prop_list(7))
+
+  g:LspOptionsSet({showInlayHints: true})
+  assert_equal([{id: -1, col: 9, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1},
+               {id: -2, col: 13, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1}],
+               prop_list(7))
+
+  g:LspOptionsSet({showInlayHints: false})
+  assert_equal([], prop_list(7))
+
+  :hide enew
+  :LspInlayHints enable
+  :bprev
+  assert_equal([{id: -1, col: 9, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1},
+               {id: -2, col: 13, type_bufnr: 0, end: 1,
+                type: 'LspInlayHintsParam', length: 1, start: 1}],
+               prop_list(7))
+  :hide enew
+  :LspInlayHints disable
+  :bprev
+  assert_equal([], prop_list(7))
+
+  :%bw!
+enddef
+
 # Test for the :LspServer command.
 def g:Test_LspServer()
   new a.raku