autoload/lsp.vim | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++-- plugin/lsp.vim | 7 +++++-- diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 64f4f3b0c14c9f7eb911d8ae354196767f7417ba..50aab9a580fa1d93b22588c58208869b2b1aef0b 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -147,11 +147,39 @@ echomsg 'Error: Unsupported hover contents type (' .. reply.result.contents.kind .. ')' endif enddef +# process the 'textDocument/references' reply from the LSP server +def LSPprocessReferencesReply(ftype: string, reply: dict): void + if type(reply.result) == v:t_none || reply.result->len() == 0 + echomsg 'Error: No references found' + return + endif + + # create a quickfix list with the location of the references + var locations: list> = reply.result + var qflist: list> = [] + for loc in locations + var fname: string = loc.uri[7:] + var text: string = fname->getbufline(loc.range.start.line + 1)[0] + ->trim("\t ", 1) + qflist->add({'filename': fname, + 'lnum': loc.range.start.line + 1, + 'col': loc.range.start.character + 1, + 'text': text}) + endfor + call setqflist([], ' ', {'title': 'Language Server', 'items': qflist}) + var save_winid = win_getid() + copen + win_gotoid(save_winid) +enddef + # Process varous reply messages from the LSP server def lsp#process_reply(ftype: string, req: dict, reply: dict): void if req.method == 'initialize' LSPprocessInitializeReply(ftype, reply) - elseif req.method == 'textDocument/definition' || req.method == 'textDocument/declaration' + elseif req.method == 'textDocument/definition' + || req.method == 'textDocument/declaration' + || req.method == 'textDocument/typeDefinition' + || req.method == 'textDocument/implementation' LSPprocessDefDeclReply(reply) elseif req.method == 'textDocument/signatureHelp' LSPprocessSignaturehelpReply(reply) @@ -159,6 +187,8 @@ elseif req.method == 'textDocument/completion' LSPprocessCompletionReply(ftype, reply) elseif req.method == 'textDocument/hover' LSPprocessHoverReply(ftype, reply) + elseif req.method == 'textDocument/references' + LSPprocessReferencesReply(ftype, reply) else echomsg "Error: Unsupported reply received from LSP server: " .. string(reply) endif @@ -274,7 +304,7 @@ return req enddef # Send a "initialize" LSP request -def lsp#init_server(ftype: string): number +def lsp#init_server(ftype: string) var req = lsp#create_reqmsg(ftype, 'initialize') # interface 'InitializeParams' @@ -284,7 +314,6 @@ initparams.clientInfo = {'name': 'Vim', 'version': string(v:versionlong)} req.params->extend(initparams) LSPsendto_server(ftype, req) - return 1 enddef # Send a "initialized" LSP notification @@ -399,7 +428,7 @@ LSPsendto_server(ftype, notif) enddef # Goto a definition using "textDocument/definition" LSP request -def lsp#goto_definition(fname: string, ftype: string, lnum: number, col: number) +def lsp#gotoDefinition(fname: string, ftype: string, lnum: number, col: number) if fname == '' || ftype == '' return endif @@ -412,6 +441,13 @@ echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' return endif + # Check whether LSP server supports jumping to a definition + if !lsp_servers[ftype].caps->has_key('definitionProvider') + || !lsp_servers[ftype].caps.definitionProvider + echomsg "Error: LSP server does not support jumping to a definition" + return + endif + var req = lsp#create_reqmsg(ftype, 'textDocument/definition') # interface DefinitionParams @@ -425,7 +461,7 @@ LSPsendto_server(ftype, req) enddef # Goto a declaration using "textDocument/declaration" LSP request -def lsp#goto_declaration(fname: string, ftype: string, lnum: number, col: number) +def lsp#gotoDeclaration(fname: string, ftype: string, lnum: number, col: number) if fname == '' || ftype == '' return endif @@ -435,6 +471,13 @@ return endif if !lsp_servers[ftype].running echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' + return + endif + + # Check whether LSP server supports jumping to a declaration + if !lsp_servers[ftype].caps->has_key('declarationProvider') + || !lsp_servers[ftype].caps.declarationProvider + echomsg "Error: LSP server does not support jumping to a declaration" return endif @@ -450,6 +493,72 @@ LSPsendto_server(ftype, req) enddef +# Go to a type definition using "textDocument/typeDefinition" LSP request +def lsp#gotoTypedef(fname: string, ftype: string, lnum: number, col: number) + if fname == '' || ftype == '' + return + endif + if !lsp_servers->has_key(ftype) + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not found' + return + endif + if !lsp_servers[ftype].running + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' + return + endif + + # Check whether LSP server supports jumping to a type definition + if !lsp_servers[ftype].caps->has_key('typeDefinitionProvider') + || !lsp_servers[ftype].caps.typeDefinitionProvider + echomsg "Error: LSP server does not support jumping to a type definition" + return + endif + + var req = lsp#create_reqmsg(ftype, 'textDocument/typeDefinition') + + # interface TypeDefinitionParams + # interface TextDocumentPositionParams + # interface TextDocumentIdentifier + req.params->extend({'textDocument': {'uri': 'file://' .. fname}}) + # interface Position + req.params->extend({'position': {'line': lnum, 'character': col}}) + + LSPsendto_server(ftype, req) +enddef + +# Go to a implementation using "textDocument/implementation" LSP request +def lsp#gotoImplementation(fname: string, ftype: string, lnum: number, col: number) + if fname == '' || ftype == '' + return + endif + if !lsp_servers->has_key(ftype) + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not found' + return + endif + if !lsp_servers[ftype].running + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' + return + endif + + # Check whether LSP server supports jumping to a type definition + if !lsp_servers[ftype].caps->has_key('implementationProvider') + || !lsp_servers[ftype].caps.implementationProvider + echomsg "Error: LSP server does not support jumping to an implementation" + return + endif + + var req = lsp#create_reqmsg(ftype, 'textDocument/implementation') + + # interface ImplementationParams + # interface TextDocumentPositionParams + # interface TextDocumentIdentifier + req.params->extend({'textDocument': {'uri': 'file://' .. fname}}) + # interface Position + req.params->extend({'position': {'line': lnum, 'character': col}}) + + LSPsendto_server(ftype, req) +enddef + # Show the signature using "textDocument/signatureHelp" LSP method # Invoked from an insert-mode mapping, so return an empty string. def lsp#showSignature(): string @@ -617,7 +726,7 @@ for diag in lsp_servers[ftype].diags diag.message = diag.message->substitute("\n\\+", "\n", 'g') msgs->extend(split(diag.message, "\n")) endfor - setqflist([], ' ', {'lines': msgs}) + setqflist([], ' ', {'lines': msgs, 'title': 'Language Server'}) cwindow enddef @@ -710,6 +819,12 @@ echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' return endif + # Check whether LSP server supports getting hover information + if !lsp_servers[ftype].caps->has_key('hoverProvider') + || !lsp_servers[ftype].caps.hoverProvider + return + endif + var fname = expand('%:p') if fname == '' return @@ -724,6 +839,46 @@ # interface TextDocumentIdentifier req.params->extend({'textDocument': {'uri': 'file://' .. fname}}) # interface Position req.params->extend({'position': {'line': lnum, 'character': col}}) + + LSPsendto_server(ftype, req) +enddef + +def lsp#showReferences() + var ftype = &filetype + if ftype == '' + return + endif + + if !lsp_servers->has_key(ftype) + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not found' + return + endif + if !lsp_servers[ftype].running + echomsg 'Error: LSP server for "' .. ftype .. '" filetype is not running' + return + endif + + # Check whether LSP server supports getting reference information + if !lsp_servers[ftype].caps->has_key('referencesProvider') + || !lsp_servers[ftype].caps.referencesProvider + return + endif + + var fname = expand('%:p') + if fname == '' + return + endif + var lnum = line('.') - 1 + var col = col('.') - 1 + + var req = lsp#create_reqmsg(ftype, 'textDocument/references') + # interface ReferenceParams + # interface TextDocumentPositionParams + # interface TextDocumentIdentifier + req.params->extend({'textDocument': {'uri': 'file://' .. fname}}) + # interface Position + req.params->extend({'position': {'line': lnum, 'character': col}}) + req.params->extend({'context': {'includeDeclaration': v:true}}) LSPsendto_server(ftype, req) enddef diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 8c3d6bef31bcbf356c70d30a7c017f8a01eefb9f..cd1a823326bb6475c31bd4cde25b9f48bef07d36 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -13,8 +13,11 @@ " This takes some time. " autocmd VimLeavePre * call lsp#stop_all_servers() command! -nargs=0 LspShowServers call lsp#showServers() -command! -nargs=0 LspGotoDefinition call lsp#goto_definition(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) -command! -nargs=0 LspGotoDeclaration call lsp#goto_declaration(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) +command! -nargs=0 LspGotoDefinition call lsp#gotoDefinition(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) +command! -nargs=0 LspGotoDeclaration call lsp#gotoDeclaration(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) +command! -nargs=0 LspGotoTypeDef call lsp#gotoTypedef(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) +command! -nargs=0 LspGotoImpl call lsp#gotoImplementation(expand('%:p'), &filetype, line('.') - 1, col('.') - 1) command! -nargs=0 LspShowSignature call lsp#showSignature() command! -nargs=0 LspShowDiagnostics call lsp#showDiagnostics() +command! -nargs=0 LspShowReferences call lsp#showReferences()