]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add a function to use with the 'tagfunc' option to jump to a symbol definition
authorYegappan Lakshmanan <yegappan@yahoo.com>
Wed, 26 Oct 2022 15:35:26 +0000 (08:35 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Wed, 26 Oct 2022 15:35:26 +0000 (08:35 -0700)
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/symbol.vim
doc/lsp.txt
test/unit_tests.vim

index 5d5d6dc952c5b39e41aa38d233d54afb358a62aa..d7b3cd2d476ff7d6bef1987679e24fa17b75356b 100644 (file)
@@ -201,7 +201,7 @@ enddef
 # display in a balloon
 var lspDiagPopupID: number = 0
 var lspDiagPopupInfo: dict<any> = {}
-def g:LspDiagExpr(): string
+def g:LspDiagExpr(): any
   var lspserver: dict<any> = buf.BufLspServerGet(v:beval_bufnr)
   if lspserver->empty() || !lspserver.running
     return ''
@@ -209,10 +209,8 @@ def g:LspDiagExpr(): string
 
   # Display the diagnostic message only if the mouse is over the gutter for
   # the signs.
-  if opt.lspOptions.noDiagHoverOnLine
-    if v:beval_col >= 2
-      return ''
-    endif
+  if opt.lspOptions.noDiagHoverOnLine && v:beval_col >= 2
+    return ''
   endif
 
   var diagInfo: dict<any> = lspserver.getDiagByLine(v:beval_bufnr,
@@ -222,7 +220,7 @@ def g:LspDiagExpr(): string
     return ''
   endif
 
-  return diagInfo.message
+  return diagInfo.message->split("\n")
 enddef
 
 # Called after leaving insert mode. Used to process diag messages (if any)
@@ -924,4 +922,14 @@ export def ShowServerCapabilities()
   lspserver.showCapabilities()
 enddef
 
+# Function to use with the 'tagfunc' option.
+export def TagFunc(pat: string, flags: string, info: dict<any>): any
+  var lspserver: dict<any> = CurbufGetServerChecked()
+  if lspserver->empty()
+    return v:null
+  endif
+
+  return lspserver.tagFunc(pat, flags, info)
+enddef
+
 # vim: shiftwidth=2 softtabstop=2
index b562a2e5b06958f7cd0b18a443c8b36db12f6733..f53ab3c9f9bc3c61f8296602f8875b96689d9768 100644 (file)
@@ -1114,6 +1114,35 @@ def ShowCapabilities(lspserver: dict<any>)
   endfor
 enddef
 
+# Send a 'textDocument/definition' request to the LSP server to get the
+# location where the symbol under the cursor is defined and return a list of
+# Dicts in a format accepted by the 'tagfunc' option.
+# Returns null if the LSP server doesn't support getting the location of a
+# symbol definition or the symbol is not defined.
+def TagFunc(lspserver: dict<any>, pat: string, flags: string, info: dict<any>): any
+  # Check whether LSP server supports getting the location of a definition
+  if !lspserver.caps->has_key('definitionProvider')
+                               || !lspserver.caps.definitionProvider
+    return null
+  endif
+
+  # interface DefinitionParams
+  #   interface TextDocumentPositionParams
+  var reply = lspserver.rpc('textDocument/definition', GetLspTextDocPosition())
+  if reply->empty() || reply.result->empty()
+    return null
+  endif
+
+  var taglocations: list<dict<any>>
+  if reply.result->type() == v:t_list
+    taglocations = reply.result
+  else
+    taglocations = [reply.result]
+  endif
+
+  return symbol.TagFunc(lspserver, taglocations, pat)
+enddef
+
 export def NewLspServer(path: string, args: list<string>, isSync: bool, initializationOptions: dict<any>): dict<any>
   var lspserver: dict<any> = {
     path: path,
@@ -1164,10 +1193,11 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     getCompletion: function(GetCompletion, [lspserver]),
     resolveCompletion: function(ResolveCompletion, [lspserver]),
     gotoDefinition: function(GotoDefinition, [lspserver]),
-    switchSourceHeader: function(SwitchSourceHeader, [lspserver]),
     gotoDeclaration: function(GotoDeclaration, [lspserver]),
     gotoTypeDef: function(GotoTypeDef, [lspserver]),
     gotoImplementation: function(GotoImplementation, [lspserver]),
+    tagFunc: function(TagFunc, [lspserver]),
+    switchSourceHeader: function(SwitchSourceHeader, [lspserver]),
     showSignature: function(ShowSignature, [lspserver]),
     didSaveFile: function(DidSaveFile, [lspserver]),
     hover: function(ShowHoverInfo, [lspserver]),
index ae293dc0d6369548dc8cf6034789faa90ec651d7..9177b0083183cd1c3755b0479f4d0b9f3678e756 100644 (file)
@@ -323,4 +323,22 @@ export def GotoSymbol(lspserver: dict<any>, location: dict<any>, peekSymbol: boo
   redraw!
 enddef
 
+# Process the LSP server reply message for a 'textDocument/definition' request
+# and return a list of Dicts in a format accepted by the 'tagfunc' option.
+export def TagFunc(lspserver: dict<any>,
+                       taglocations: list<dict<any>>,
+                       pat: string): list<dict<any>>
+  var retval: list<dict<any>>
+
+  for tagloc in taglocations
+    var tagitem = {}
+    tagitem.name = pat
+    tagitem.filename = util.LspUriToFile(tagloc.uri)
+    tagitem.cmd = (tagloc.range.start.line + 1)->string()
+    retval->add(tagitem)
+  endfor
+
+  return retval
+enddef
+
 # vim: shiftwidth=2 softtabstop=2
index 751c77ad13d1025f63d1ba6af7c3bbcdfd2623c5..78874bc3970c9f5c37352b394f1b4acbacca95af 100644 (file)
@@ -678,7 +678,23 @@ message area instead, you can set the 'showDiagInPopup' option to false.  By
 default this is set to true.
 
 ==============================================================================
-8. Autocommands                                                *lsp-autocmds*
+8. Tag Function
+
+The |:LspGotoDefinition| command can be used jump to the location where a
+symbol is defined.  To jump to the symbol definition using the Vim
+|tag-commands|, you can set the 'tagfunc' option to the 'lsp#lsp#TagFunc'
+function: >
+
+       setlocal tagfunc=lsp#lsp#TagFunc
+<
+After setting the above option, you can use |Ctrl-]| and other tag related
+commands to jump to the symbol definition.
+
+Note that most of the language servers return only one symbol location even if
+the symbol is defined in multiple places in the code.
+
+==============================================================================
+9. Autocommands                                                *lsp-autocmds*
 
                                                        *LspAttached*
 LspAttached                    A |User| autocommand fired when the LSP client
index 73f103ec16fdd8a2f46845f921eca9d4c82bf3da..5185de4e7feb19065a2122737c8f13279a10a00d 100644 (file)
@@ -272,6 +272,7 @@ def Test_LspDiag()
   assert_equal([5, 2, 'W'], [qfl[1].lnum, qfl[1].col, qfl[1].type])
   assert_equal([7, 2, 'W'], [qfl[2].lnum, qfl[2].col, qfl[2].type])
   close
+  g:LspOptionsSet({showDiagInPopup: false})
   normal gg
   var output = execute('LspDiagCurrent')->split("\n")
   assert_equal('No diagnostic messages found for current line', output[0])
@@ -295,6 +296,7 @@ def Test_LspDiag()
   WaitForDiags(0)
   output = execute('LspDiagShow')->split("\n")
   assert_match('No diagnostic messages found for', output[0])
+  g:LspOptionsSet({showDiagInPopup: true})
 
   :%bw!
 enddef
@@ -780,6 +782,38 @@ def Test_LspOutline()
   :%bw!
 enddef
 
+# Test for setting the 'tagfunc'
+def Test_LspTagFunc()
+  var lines: list<string> =<< trim END
+    void aFunc(void)
+    {
+      xFunc();
+    }
+
+    void bFunc(void)
+    {
+      xFunc();
+    }
+
+    void xFunc(void)
+    {
+    }
+  END
+  writefile(lines, 'Xtest.c')
+  :silent! edit Xtest.c
+  :sleep 1
+  :setlocal tagfunc=lsp#lsp#TagFunc
+  cursor(3, 4)
+  :exe "normal \<C-]>"
+  assert_equal([11, 1], [line('.'), col('.')])
+  cursor(1, 1)
+  assert_fails('exe "normal \<C-]>"', 'E433: No tags file')
+
+  :set tagfunc&
+  :%bw!
+  delete('Xtest.c')
+enddef
+
 def LspRunTests()
   :set nomore
   :set debug=beep