]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Use sync RPC for workspace symbol search
authorYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 18 Oct 2022 04:26:44 +0000 (21:26 -0700)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 18 Oct 2022 04:26:44 +0000 (21:26 -0700)
README.md
autoload/lsp/handlers.vim
autoload/lsp/lsp.vim
autoload/lsp/lspserver.vim
autoload/lsp/symbol.vim
doc/lsp.txt
test/unit_tests.vim

index 8a9446862f5a861ed24f560f15ec654c63d5686b..edc5ac114f1a3b69f447f261ddb24073a1e5191d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The following language server protocol (LSP) features are supported:
 * Display code outline
 * Rename symbol
 * Display type and documentation on hover
-* Inlay hints
+* Signature help
 * Code action
 * Formatting code
 * Folding code
index bdb2a5be55305a954a0e5ab957d44258d1d5aa7d..48cf696435bab09449cb1d5e7aa79d7acb8c5be1 100644 (file)
@@ -284,19 +284,6 @@ def ProcessDocHighlightReply(lspserver: dict<any>, req: dict<any>, reply: dict<a
   endfor
 enddef
 
-# map the LSP symbol kind number to string
-def LspSymbolKindToName(symkind: number): string
-  var symbolMap: list<string> = ['', 'File', 'Module', 'Namespace', 'Package',
-       'Class', 'Method', 'Property', 'Field', 'Constructor', 'Enum',
-       'Interface', 'Function', 'Variable', 'Constant', 'String', 'Number',
-       'Boolean', 'Array', 'Object', 'Key', 'Null', 'EnumMember', 'Struct',
-       'Event', 'Operator', 'TypeParameter']
-  if symkind > 26
-    return ''
-  endif
-  return symbolMap[symkind]
-enddef
-
 # process SymbolInformation[]
 def ProcessSymbolInfoTable(symbolInfoTable: list<dict<any>>,
                                symbolTypeTable: dict<list<dict<any>>>,
@@ -309,7 +296,7 @@ def ProcessSymbolInfoTable(symbolInfoTable: list<dict<any>>,
 
   for symbol in symbolInfoTable
     fname = util.LspUriToFile(symbol.location.uri)
-    symbolType = LspSymbolKindToName(symbol.kind)
+    symbolType = symbol.SymbolKindToName(symbol.kind)
     name = symbol.name
     if symbol->has_key('containerName')
       if symbol.containerName != ''
@@ -340,7 +327,7 @@ def ProcessDocSymbolTable(docSymbolTable: list<dict<any>>,
 
   for symbol in docSymbolTable
     name = symbol.name
-    symbolType = LspSymbolKindToName(symbol.kind)
+    symbolType = symbol.SymbolKindToName(symbol.kind)
     r = symbol.range
     if symbol->has_key('detail')
       symbolDetail = symbol.detail
@@ -438,68 +425,6 @@ def ProcessWorkspaceExecuteReply(lspserver: dict<any>, req: dict<any>, reply: di
   # Nothing to do for the reply
 enddef
 
-# Convert a file name <filename> (<dirname>) format.
-# Make sure the popup does't occupy the entire screen by reducing the width.
-def MakeMenuName(popupWidth: number, fname: string): string
-  var filename: string = fname->fnamemodify(':t')
-  var flen: number = filename->len()
-  var dirname: string = fname->fnamemodify(':h')
-
-  if fname->len() > popupWidth && flen < popupWidth
-    # keep the full file name and reduce directory name length
-    # keep some characters at the beginning and end (equally).
-    # 6 spaces are used for "..." and " ()"
-    var dirsz = (popupWidth - flen - 6) / 2
-    dirname = dirname[: dirsz] .. '...' .. dirname[-dirsz : ]
-  endif
-  var str: string = filename
-  if dirname != '.'
-    str ..= $' ({dirname}/)'
-  endif
-  return str
-enddef
-
-# process the 'workspace/symbol' reply from the LSP server
-# Result: SymbolInformation[] | null
-def ProcessWorkspaceSymbolReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
-  var symbols: list<dict<any>> = []
-  var symbolType: string
-  var fileName: string
-  var r: dict<dict<number>>
-  var symName: string
-
-  if reply.result->empty()
-    return
-  endif
-
-  for symbol in reply.result
-    if !symbol->has_key('location')
-      # ignore entries without location information
-      continue
-    endif
-
-    # interface SymbolInformation
-    fileName = util.LspUriToFile(symbol.location.uri)
-    r = symbol.location.range
-
-    symName = symbol.name
-    if symbol->has_key('containerName') && symbol.containerName != ''
-      symName = $'{symbol.containerName}::{symName}'
-    endif
-    symName ..= $' [{LspSymbolKindToName(symbol.kind)}]'
-    symName ..= ' ' .. MakeMenuName(
-               lspserver.workspaceSymbolPopup->popup_getpos().core_width,
-               fileName)
-
-    symbols->add({name: symName,
-                       file: fileName,
-                       pos: r.start})
-  endfor
-  symbols->setwinvar(lspserver.workspaceSymbolPopup, 'LspSymbolTable')
-  lspserver.workspaceSymbolPopup->popup_settext(
-                               symbols->copy()->mapnew('v:val.name'))
-enddef
-
 # Process various reply messages from the LSP server
 export def ProcessReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
   var lsp_reply_handlers: dict<func> =
@@ -513,7 +438,6 @@ export def ProcessReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>):
       'textDocument/codeAction': ProcessCodeActionReply,
       'textDocument/foldingRange': ProcessFoldingRangeReply,
       'workspace/executeCommand': ProcessWorkspaceExecuteReply,
-      'workspace/symbol': ProcessWorkspaceSymbolReply,
     }
 
   if lsp_reply_handlers->has_key(req.method)
index 199a915711160574143f72e8846936937c4928fd..6fd28e260ee4218948aad965e230e63b892830f3 100644 (file)
@@ -764,11 +764,7 @@ export def SymbolSearch(queryArg: string)
   endif
   redraw!
 
-  symbol.ShowSymbolMenu(lspserver, query)
-
-  if !lspserver.workspaceQuery(query)
-    lspserver.workspaceSymbolPopup->popup_close()
-  endif
+  lspserver.workspaceQuery(query)
 enddef
 
 # Display the list of workspace folders
index 82a9e7f056b3d9f876fd4cc9d3f0c49a8710d05e..e4fc26701bb096ae2f0f750099629d229aea1017 100644 (file)
@@ -917,19 +917,24 @@ enddef
 # List project-wide symbols matching query string
 # Request: "workspace/symbol"
 # Param: WorkspaceSymbolParams
-def WorkspaceQuerySymbols(lspserver: dict<any>, query: string): bool
+def WorkspaceQuerySymbols(lspserver: dict<any>, query: string)
   # Check whether the LSP server supports listing workspace symbols
   if !lspserver.caps->has_key('workspaceSymbolProvider')
                                || !lspserver.caps.workspaceSymbolProvider
     util.ErrMsg("Error: LSP server does not support listing workspace symbols")
-    return false
+    return
   endif
 
-  var req = lspserver.createRequest('workspace/symbol')
-  req.params->extend({query: query})
-  lspserver.sendMessage(req)
+  # Param: WorkspaceSymbolParams
+  var param = {}
+  param.query = query
+  var reply = lspserver.rpc('workspace/symbol', param)
+  if reply->empty() || reply.result->empty()
+    util.WarnMsg($'Error: Symbol "{query}" is not found')
+    return
+  endif
 
-  return true
+  symbol.WorkspaceSymbolPopup(lspserver, query, reply.result)
 enddef
 
 # Add a workspace folder to the LSP server.
@@ -1099,7 +1104,7 @@ export def NewLspServer(path: string, args: list<string>, isSync: bool, initiali
     completionTriggerChars: [],
     signaturePopup: -1,
     diagsMap: {},
-    workspaceSymbolPopup: 0,
+    workspaceSymbolPopup: -1,
     workspaceSymbolQuery: '',
     callHierarchyType: '',
     selection: {}
index 57e98092fdd8c22532701e061c676c0a9d3ad734..ae293dc0d6369548dc8cf6034789faa90ec651d7 100644 (file)
@@ -103,6 +103,9 @@ def JumpToWorkspaceSymbol(popupID: number, result: number): void
     else
       winList[0]->win_gotoid()
     endif
+    # Set the previous cursor location mark. Instead of using setpos(), m' is
+    # used so that the current location is added to the jump list.
+    normal m'
     setcursorcharpos(symTbl[result - 1].pos.line + 1,
                        symTbl[result - 1].pos.character + 1)
   catch
@@ -111,7 +114,7 @@ def JumpToWorkspaceSymbol(popupID: number, result: number): void
 enddef
 
 # display a list of symbols from the workspace
-export def ShowSymbolMenu(lspserver: dict<any>, query: string)
+def ShowSymbolMenu(lspserver: dict<any>, query: string)
   # Create the popup menu
   var lnum = &lines - &cmdheight - 2 - 10
   var popupAttr = {
@@ -138,6 +141,83 @@ export def ShowSymbolMenu(lspserver: dict<any>, query: string)
   echo $'Symbol: {query}'
 enddef
 
+# Convert a file name to <filename> (<dirname>) format.
+# Make sure the popup does't occupy the entire screen by reducing the width.
+def MakeMenuName(popupWidth: number, fname: string): string
+  var filename: string = fname->fnamemodify(':t')
+  var flen: number = filename->len()
+  var dirname: string = fname->fnamemodify(':h')
+
+  if fname->len() > popupWidth && flen < popupWidth
+    # keep the full file name and reduce directory name length
+    # keep some characters at the beginning and end (equally).
+    # 6 spaces are used for "..." and " ()"
+    var dirsz = (popupWidth - flen - 6) / 2
+    dirname = dirname[: dirsz] .. '...' .. dirname[-dirsz : ]
+  endif
+  var str: string = filename
+  if dirname != '.'
+    str ..= $' ({dirname}/)'
+  endif
+  return str
+enddef
+
+# process the 'workspace/symbol' reply from the LSP server
+# Result: SymbolInformation[] | null
+export def WorkspaceSymbolPopup(lspserver: dict<any>, query: string,
+                               symInfo: list<dict<any>>)
+  var symbols: list<dict<any>> = []
+  var symbolType: string
+  var fileName: string
+  var r: dict<dict<number>>
+  var symName: string
+
+  # Create a symbol popup menu if it is not present
+  if lspserver.workspaceSymbolPopup->winbufnr() == -1
+    ShowSymbolMenu(lspserver, query)
+  endif
+
+  for symbol in symInfo
+    if !symbol->has_key('location')
+      # ignore entries without location information
+      continue
+    endif
+
+    # interface SymbolInformation
+    fileName = util.LspUriToFile(symbol.location.uri)
+    r = symbol.location.range
+
+    symName = symbol.name
+    if symbol->has_key('containerName') && symbol.containerName != ''
+      symName = $'{symbol.containerName}::{symName}'
+    endif
+    symName ..= $' [{SymbolKindToName(symbol.kind)}]'
+    symName ..= ' ' .. MakeMenuName(
+               lspserver.workspaceSymbolPopup->popup_getpos().core_width,
+               fileName)
+
+    symbols->add({name: symName,
+                       file: fileName,
+                       pos: r.start})
+  endfor
+  symbols->setwinvar(lspserver.workspaceSymbolPopup, 'LspSymbolTable')
+  lspserver.workspaceSymbolPopup->popup_settext(
+                               symbols->copy()->mapnew('v:val.name'))
+enddef
+
+# map the LSP symbol kind number to string
+export def SymbolKindToName(symkind: number): string
+  var symbolMap: list<string> = ['', 'File', 'Module', 'Namespace', 'Package',
+       'Class', 'Method', 'Property', 'Field', 'Constructor', 'Enum',
+       'Interface', 'Function', 'Variable', 'Constant', 'String', 'Number',
+       'Boolean', 'Array', 'Object', 'Key', 'Null', 'EnumMember', 'Struct',
+       'Event', 'Operator', 'TypeParameter']
+  if symkind > 26
+    return ''
+  endif
+  return symbolMap[symkind]
+enddef
+
 # Display or peek symbol references in a location list
 export def ShowReferences(lspserver: dict<any>, refs: list<dict<any>>, peekSymbol: bool)
   if refs->empty()
index 3d07de71ce5261fcb33d84ba76f72d17f1c4cabf..22152a245861ec90fc845b07f958631491ab1f3f 100644 (file)
@@ -472,9 +472,11 @@ diagnostic messages, you can add the following line to your .vimrc file:
                        enter the symbol name (the keyword under the cursor is
                        used as the default).  A popup window is opened with
                        the list of matching symbols.  You can enter a few
-                       characters to narrow down the list of matches. You can
-                       close the popup menu by pressing the escape key or by
-                       pressing CTRL-C.
+                       characters to narrow down the list of matches. The
+                       displayed symbol name can be erased by pressing
+                       <Backspace> or <C-U> and a new symbol search pattern
+                       can be entered.  You can close the popup menu by
+                       pressing the escape key or by pressing CTRL-C.
 
                        In the popup menu, the following keys can be used:
 
index 3abc4e54a9315f4a76718cb70994191d82c55526..54cf016376a270296c914fe1a533b0e00a56a96d 100644 (file)
@@ -672,6 +672,44 @@ def Test_LspShowSignature()
   :%bw!
 enddef
 
+# Test for :LspSymbolSearch
+def Test_LspSymbolSearch()
+  silent! edit Xtest.c
+  sleep 200m
+  var lines: list<string> =<< trim END
+    void lsptest_funcA()
+    {
+    }
+
+    void lsptest_funcB()
+    {
+    }
+
+    void lsptest_funcC()
+    {
+    }
+  END
+  setline(1, lines)
+  :sleep 1
+
+  cursor(1, 1)
+  feedkeys(":LspSymbolSearch lsptest_funcB\<CR>\<CR>", "xt")
+  assert_equal([5, 6], [line('.'), col('.')])
+
+  cursor(1, 1)
+  feedkeys(":LspSymbolSearch lsptest_func\<CR>\<Down>\<Down>\<CR>", "xt")
+  assert_equal([9, 6], [line('.'), col('.')])
+
+  cursor(1, 1)
+  feedkeys(":LspSymbolSearch lsptest_funcA\<CR>\<BS>B\<CR>", "xt")
+  assert_equal([5, 6], [line('.'), col('.')])
+
+  var output = execute(':LspSymbolSearch lsptest_nonexist')->split("\n")
+  assert_equal(['Error: Symbol "lsptest_nonexist" is not found'], output)
+
+  :%bw!
+enddef
+
 # Test for :LspIncomingCalls
 def Test_LspIncomingCalls()
   silent! edit Xtest.c