* Display code outline
* Rename symbol
* Display type and documentation on hover
-* Inlay hints
+* Signature help
* Code action
* Formatting code
* Folding code
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>>>,
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 != ''
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
# 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> =
'textDocument/codeAction': ProcessCodeActionReply,
'textDocument/foldingRange': ProcessFoldingRangeReply,
'workspace/executeCommand': ProcessWorkspaceExecuteReply,
- 'workspace/symbol': ProcessWorkspaceSymbolReply,
}
if lsp_reply_handlers->has_key(req.method)
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
# 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.
completionTriggerChars: [],
signaturePopup: -1,
diagsMap: {},
- workspaceSymbolPopup: 0,
+ workspaceSymbolPopup: -1,
workspaceSymbolQuery: '',
callHierarchyType: '',
selection: {}
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
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 = {
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()
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:
:%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