# 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 s: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
def s:processWorkspaceSymbolReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
- WarnMsg('Error: Symbol not found')
return
endif
- s:showSymbols(reply.result, '')
+ var symbols: list<dict<any>> = []
+ var symbolType: string
+ var fileName: string
+ var r: dict<dict<number>>
+ var symName: string
+
+ for symbol in reply.result
+ if !symbol->has_key('location')
+ # ignore entries without location information
+ continue
+ endif
+
+ # interface SymbolInformation
+ fileName = 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 ..= ' ' .. s:makeMenuName(
+ lspserver.workspaceSymbolPopup->popup_getpos().core_width,
+ fileName)
+
+ symbols->add({name: symName,
+ file: fileName,
+ lnum: r.start.line + 1,
+ col: r.start.character + 1})
+ endfor
+ symbols->setwinvar(lspserver.workspaceSymbolPopup, 'LspSymbolTable')
+ lspserver.workspaceSymbolPopup->popup_settext(
+ symbols->copy()->map('v:val.name'))
enddef
# Process various reply messages from the LSP server
lspserver.codeAction(fname)
enddef
+# Handle keys pressed when the workspace symbol popup menu is displayed
+def LspFilterNames(lspserver: dict<any>, popupID: number, key: string): bool
+ var key_handled: bool = false
+ var update_popup: bool = false
+ var query: string = lspserver.workspaceSymbolQuery
+
+ if key == "\<BS>" || key == "\<C-H>"
+ # Erase one character from the filter text
+ if query->len() >= 1
+ query = query[: -2]
+ update_popup = true
+ endif
+ key_handled = true
+ elseif key == "\<C-U>"
+ # clear the filter text
+ query = ''
+ update_popup = true
+ key_handled = true
+ elseif key == "\<C-F>"
+ || key == "\<C-B>"
+ || key == "\<PageUp>"
+ || key == "\<PageDown>"
+ || key == "\<C-Home>"
+ || key == "\<C-End>"
+ || key == "\<C-N>"
+ || key == "\<C-P>"
+ # scroll the popup window
+ var cmd: string = 'normal! ' .. (key == "\<C-N>" ? 'j' : key == "\<C-P>" ? 'k' : key)
+ cmd->win_execute(popupID)
+ key_handled = true
+ elseif key == "\<Up>" || key == "\<Down>"
+ # Use native Vim handling for these keys
+ key_handled = false
+ elseif key =~ '^\f$' || key == "\<Space>"
+ # Filter the names based on the typed key and keys typed before
+ query ..= key
+ update_popup = true
+ key_handled = true
+ endif
+
+ if update_popup
+ # Update the popup with the new list of symbol names
+ popupID->popup_settext('')
+ if query != ''
+ lspserver.workspaceSymbols(query)
+ endif
+ echo 'Symbol: ' .. query
+ endif
+
+ # Update the workspace symbol query string
+ lspserver.workspaceSymbolQuery = query
+
+ if key_handled
+ return v:true
+ endif
+
+ return popupID->popup_filter_menu(key)
+enddef
+
+# Jump to the location of a symbol selected in the popup menu
+def LspJumpToSymbol(popupID: number, result: number): void
+ # clear the message displayed at the command-line
+ echo ''
+
+ if result <= 0
+ # popup is canceled
+ return
+ endif
+
+ var symTbl: list<dict<any>> = popupID->getwinvar('LspSymbolTable')
+ echomsg symTbl
+ try
+ # if the selected file is already present in a window, then jump to it
+ var fname: string = symTbl[result - 1].file
+ var winList: list<number> = fname->bufnr()->win_findbuf()
+ if winList->len() == 0
+ # Not present in any window
+ if &modified || &buftype != ''
+ # the current buffer is modified or is not a normal buffer, then open
+ # the file in a new window
+ exe "split " .. symTbl[result - 1].file
+ else
+ exe "confirm edit " .. symTbl[result - 1].file
+ endif
+ else
+ winList[0]->win_gotoid()
+ endif
+ cursor(symTbl[result - 1].lnum, symTbl[result - 1].col)
+ catch
+ # ignore exceptions
+ endtry
+enddef
+
+# display a list of symbols from the workspace
+def s:showSymbolMenu(lspserver: dict<any>, query: string)
+ # Create the popup menu
+ var lnum = &lines - &cmdheight - 2 - 10
+ var popupAttr = {
+ title: 'Workspace Symbol Search',
+ wrap: 0,
+ pos: 'topleft',
+ line: lnum,
+ col: 2,
+ minwidth: 60,
+ minheight: 10,
+ maxheight: 10,
+ maxwidth: 60,
+ mapping: false,
+ fixed: 1,
+ close: "button",
+ filter: function('s:LspFilterNames', [lspserver]),
+ callback: LspJumpToSymbol
+ }
+ lspserver.workspaceSymbolPopup = popup_menu([], popupAttr)
+ lspserver.workspaceSymbolQuery = query
+ prop_type_add('lspworkspacesymbol',
+ {bufnr: lspserver.workspaceSymbolPopup->winbufnr(),
+ highlight: 'Title'})
+ echo 'Symbol: ' .. query
+enddef
+
# Perform a workspace wide symbol lookup
# Uses LSP "workspace/symbol" request
-def lsp#showWorkspaceSymbols()
+def lsp#showWorkspaceSymbols(queryArg: string)
var ftype = &filetype
if ftype == ''
return
return
endif
- var sym: string = input("Lookup symbol: ", expand('<cword>'))
- if sym == ''
- return
+ var query: string = queryArg
+ if query == ''
+ query = input("Lookup symbol: ", expand('<cword>'))
+ if query == ''
+ return
+ endif
endif
+ redraw!
- lspserver.workspaceSymbols(sym)
+ s:showSymbolMenu(lspserver, query)
+
+ if !lspserver.workspaceSymbols(query)
+ lspserver.workspaceSymbolPopup->popup_close()
+ endif
enddef
# Display the list of workspace folders
},
completionItemKind: {valueSet: range(1, 25)}
},
+ hover: {
+ contentFormat: ['plaintext', 'markdown']
+ },
documentSymbol: {
hierarchicalDocumentSymbolSupport: v:true,
symbolKind: {valueSet: range(1, 25)}
},
- hover: {
- contentFormat: ['plaintext', 'markdown']
- }
},
window: {},
- general: {},
+ general: {}
}
# interface 'InitializeParams'
lspserver.sendMessage(req)
enddef
-def s:workspaceSymbols(lspserver: dict<any>, sym: string)
+def s:workspaceSymbols(lspserver: dict<any>, sym: string): bool
# Check whether the LSP server supports listing workspace symbols
if !lspserver.caps->has_key('workspaceSymbolProvider')
|| !lspserver.caps.workspaceSymbolProvider
ErrMsg("Error: LSP server does not support listing workspace symbols")
- return
+ return false
endif
var req = lspserver.createRequest('workspace/symbol')
req.params->extend({query: sym})
lspserver.sendMessage(req)
+
+ return true
enddef
def s:addWorkspaceFolder(lspserver: dict<any>, dirName: string): void
caps: {},
requests: {},
completePending: v:false,
- diagsMap: {}
+ diagsMap: {},
+ workspaceSymbolPopup: 0,
+ workspaceSymbolQuery: ''
}
# Add the LSP server functions
lspserver->extend({
command! -nargs=0 -bar LspShowReferences call lsp#showReferences()
command! -nargs=0 -bar LspHighlight call lsp#docHighlight()
command! -nargs=0 -bar LspHighlightClear call lsp#docHighlightClear()
-command! -nargs=0 -bar LspShowSymbols call lsp#showDocSymbols()
+command! -nargs=0 -bar LspShowDocSymbols call lsp#showDocSymbols()
command! -nargs=0 -bar -range=% LspFormat call lsp#textDocFormat(<range>, <line1>, <line2>)
command! -nargs=0 -bar LspCalledBy call lsp#incomingCalls()
command! -nargs=0 -bar LspCalling call lsp#outgoingCalls()
command! -nargs=0 -bar LspRename call lsp#rename()
command! -nargs=0 -bar LspCodeAction call lsp#codeAction()
-command! -nargs=0 -bar LspShowWorkspaceSymbols call lsp#showWorkspaceSymbols()
+command! -nargs=? -bar LspShowWorkspaceSymbols call lsp#showWorkspaceSymbols(<q-args>)
command! -nargs=0 -bar LspWorkspaceListFolders call lsp#listWorkspaceFolders()
command! -nargs=1 -bar -complete=dir LspWorkspaceAddFolder call lsp#addWorkspaceFolder(<q-args>)
command! -nargs=1 -bar -complete=dir LspWorkspaceRemoveFolder call lsp#removeWorkspaceFolder(<q-args>)