From d4b7ba8dae00e5874ff709012b5ea698dcd3c529 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 18 Jan 2021 09:51:59 -0800 Subject: [PATCH] Add new test. Use the method syntax in more places --- autoload/buf.vim | 2 +- autoload/handlers.vim | 52 +++++++++++++++++++++--------------------- autoload/lsp.vim | 52 +++++++++++++++++++++++------------------- autoload/lspserver.vim | 19 ++++++++------- autoload/util.vim | 5 ++++ doc/lsp.txt | 48 +++++++++++++++++++++++++++----------- test/test_lsp.vim | 45 +++++++++++++++++++++++++++++++++++- 7 files changed, 148 insertions(+), 75 deletions(-) diff --git a/autoload/buf.vim b/autoload/buf.vim index fcbc51f..e46823b 100644 --- a/autoload/buf.vim +++ b/autoload/buf.vim @@ -24,7 +24,7 @@ export def LspDiagsUpdated(lspserver: dict, bnr: number) endif var signs: list> = [] - for [lnum, diag] in items(lspserver.diagsMap[bnr]) + for [lnum, diag] in lspserver.diagsMap[bnr]->items() signs->add({id: 0, buffer: str2nr(bnr), group: 'LSPDiag', lnum: str2nr(lnum), name: s:lspDiagSevToSignName(diag.severity)}) diff --git a/autoload/handlers.vim b/autoload/handlers.vim index 7699cbc..ec266c7 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -67,7 +67,7 @@ def s:processDefDeclReply(lspserver: dict, req: dict, reply: dict var file = LspUriToFile(result.uri) var wid = file->bufwinid() if wid != -1 - win_gotoid(wid) + wid->win_gotoid() else exe 'split ' .. file endif @@ -103,10 +103,10 @@ def s:processSignaturehelpReply(lspserver: dict, req: dict, reply: dic startcol = text->stridx(label) endif endif - var popupID = popup_atcursor(text, {}) - prop_type_add('signature', {bufnr: winbufnr(popupID), highlight: 'Title'}) + var popupID = text->popup_atcursor({}) + prop_type_add('signature', {bufnr: popupID->winbufnr(), highlight: 'Title'}) if hllen > 0 - prop_add(1, startcol + 1, {bufnr: winbufnr(popupID), length: hllen, type: 'signature'}) + prop_add(1, startcol + 1, {bufnr: popupID->winbufnr(), length: hllen, type: 'signature'}) endif enddef @@ -153,7 +153,7 @@ def s:processCompletionReply(lspserver: dict, req: dict, reply: dict> - if type(reply.result) == v:t_list + if reply.result->type() == v:t_list items = reply.result else items = reply.result.items @@ -200,7 +200,7 @@ def s:processHoverReply(lspserver: dict, req: dict, reply: dict): var hoverText: list - if type(reply.result.contents) == v:t_dict + if reply.result.contents->type() == v:t_dict if reply.result.contents->has_key('kind') # MarkupContent if reply.result.contents.kind == 'plaintext' @@ -218,16 +218,16 @@ def s:processHoverReply(lspserver: dict, req: dict, reply: dict): ErrMsg('Error: Unsupported hover contents (' .. reply.result.contents .. ')') return endif - elseif type(reply.result.contents) == v:t_list + elseif reply.result.contents->type() == v:t_list # interface MarkedString[] for e in reply.result.contents - if type(e) == v:t_string + if e->type() == v:t_string hoverText->extend(e->split("\n")) else hoverText->extend(e.value->split("\n")) endif endfor - elseif type(reply.result.contents) == v:t_string + elseif reply.result.contents->type() == v:t_string if reply.result.contents->empty() return endif @@ -269,7 +269,7 @@ def s:processReferencesReply(lspserver: dict, req: dict, reply: dictwin_gotoid() enddef # process the 'textDocument/documentHighlight' reply from the LSP server @@ -280,7 +280,7 @@ def s:processDocHighlightReply(lspserver: dict, req: dict, reply: dict endif var fname: string = LspUriToFile(req.params.textDocument.uri) - var bnr = bufnr(fname) + var bnr = fname->bufnr() for docHL in reply.result var kind: number = docHL->get('kind', 1) @@ -448,9 +448,9 @@ def s:set_lines(lines: list, A: list, B: list, var i_n = [B[0], numlines - 1]->min() if i_0 < 0 || i_0 >= numlines || i_n < 0 || i_n >= numlines - WarnMsg("set_lines: Invalid range, A = " .. string(A) - .. ", B = " .. string(B) .. ", numlines = " .. numlines - .. ", new lines = " .. string(new_lines)) + WarnMsg("set_lines: Invalid range, A = " .. A->string() + .. ", B = " .. B->string() .. ", numlines = " .. numlines + .. ", new lines = " .. new_lines->string()) return lines endif @@ -613,9 +613,9 @@ def s:applyWorkspaceEdit(workspaceEdit: dict) endif var save_cursor: list = getcurpos() - for [uri, changes] in items(workspaceEdit.changes) + for [uri, changes] in workspaceEdit.changes->items() var fname: string = LspUriToFile(uri) - var bnr: number = bufnr(fname) + var bnr: number = fname->bufnr() if bnr == -1 # file is already removed continue @@ -639,7 +639,7 @@ def s:processFormatReply(lspserver: dict, req: dict, reply: dict) # result: TextEdit[] var fname: string = LspUriToFile(req.params.textDocument.uri) - var bnr: number = bufnr(fname) + var bnr: number = fname->bufnr() if bnr == -1 # file is already removed return @@ -859,7 +859,7 @@ export def ProcessReply(lspserver: dict, req: dict, reply: dict): if lsp_reply_handlers->has_key(req.method) lsp_reply_handlers[req.method](lspserver, req, reply) else - ErrMsg("Error: Unsupported reply received from LSP server: " .. string(reply)) + ErrMsg("Error: Unsupported reply received from LSP server: " .. reply->string()) endif enddef @@ -868,7 +868,7 @@ enddef # Param: PublishDiagnosticsParams def s:processDiagNotif(lspserver: dict, reply: dict): void var fname: string = LspUriToFile(reply.params.uri) - var bnr: number = bufnr(fname) + var bnr: number = fname->bufnr() if bnr == -1 # Is this condition possible? return @@ -928,7 +928,7 @@ enddef # process unsupported notification messages def s:processUnsupportedNotif(lspserver: dict, reply: dict) - ErrMsg('Error: Unsupported notification message received from the LSP server (' .. lspserver.path .. '), message = ' .. string(reply)) + ErrMsg('Error: Unsupported notification message received from the LSP server (' .. lspserver.path .. '), message = ' .. reply->string()) enddef # process notification messages from the LSP server @@ -945,7 +945,7 @@ export def ProcessNotif(lspserver: dict, reply: dict): void if lsp_notif_handlers->has_key(reply.method) lsp_notif_handlers[reply.method](lspserver, reply) else - ErrMsg('Error: Unsupported notification received from LSP server ' .. string(reply)) + ErrMsg('Error: Unsupported notification received from LSP server ' .. reply->string()) endif enddef @@ -967,7 +967,7 @@ def s:processApplyEditReq(lspserver: dict, request: dict) enddef def s:processUnsupportedReq(lspserver: dict, request: dict) - ErrMsg('Error: Unsupported request message received from the LSP server (' .. lspserver.path .. '), message = ' .. string(request)) + ErrMsg('Error: Unsupported request message received from the LSP server (' .. lspserver.path .. '), message = ' .. request->string()) enddef # process a request message from the server @@ -988,7 +988,7 @@ export def ProcessRequest(lspserver: dict, request: dict) lspRequestHandlers[request.method](lspserver, request) else ErrMsg('Error: Unsupported request received from LSP server ' .. - string(request)) + request->string()) endif enddef @@ -1026,9 +1026,9 @@ export def ProcessMessages(lspserver: dict): void if msg->has_key('result') || msg->has_key('error') # response message from the server - var req = lspserver.requests->get(string(msg.id)) + var req = lspserver.requests->get(msg.id->string()) # Remove the corresponding stored request message - lspserver.requests->remove(string(msg.id)) + lspserver.requests->remove(msg.id->string()) if msg->has_key('result') lspserver.processReply(req, msg) @@ -1037,7 +1037,7 @@ export def ProcessMessages(lspserver: dict): void var emsg: string = msg.error.message emsg ..= ', code = ' .. msg.error.code if msg.error->has_key('data') - emsg = emsg .. ', data = ' .. string(msg.error.data) + emsg = emsg .. ', data = ' .. msg.error.data->string() endif ErrMsg("Error: request " .. req.method .. " failed (" .. emsg .. ")") endif diff --git a/autoload/lsp.vim b/autoload/lsp.vim index bb3c8a5..2b6d621 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -6,6 +6,7 @@ import NewLspServer from './lspserver.vim' import {WarnMsg, ErrMsg, lsp_server_trace, + ClearTraceLogs, GetLineByteFromPos} from './util.vim' # Needs Vim 8.2.2342 and higher @@ -54,12 +55,13 @@ def s:lspAddServer(ftype: string, lspserver: dict) enddef def lsp#enableServerTrace() + ClearTraceLogs() lsp_server_trace = v:true enddef # Show information about all the LSP servers def lsp#showServers() - for [ftype, lspserver] in items(ftypeServerMap) + for [ftype, lspserver] in ftypeServerMap->items() var msg = ftype .. " " if lspserver.running msg ..= 'running' @@ -284,7 +286,7 @@ def lsp#removeFile(bnr: number): void return endif - var fname: string = bufname(bnr) + var fname: string = bnr->bufname() var ftype: string = bnr->getbufvar('&filetype') if fname == '' || ftype == '' return @@ -297,7 +299,7 @@ def lsp#removeFile(bnr: number): void if lspserver.diagsMap->has_key(bnr) lspserver.diagsMap->remove(bnr) endif - remove(bufnrToServer, bnr) + bufnrToServer->remove(bnr) enddef # Stop all the LSP servers @@ -317,26 +319,26 @@ def lsp#addServer(serverList: list>) continue endif - if !file_readable(server.path) + if !server.path->filereadable() ErrMsg('Error: LSP server ' .. server.path .. ' is not found') return endif - if type(server.args) != v:t_list + if server.args->type() != v:t_list ErrMsg('Error: Arguments for LSP server ' .. server.path .. ' is not a List') return endif var lspserver: dict = NewLspServer(server.path, server.args) - if type(server.filetype) == v:t_string + if server.filetype->type() == v:t_string s:lspAddServer(server.filetype, lspserver) - elseif type(server.filetype) == v:t_list + elseif server.filetype->type() == v:t_list for ftype in server.filetype s:lspAddServer(ftype, lspserver) endfor else ErrMsg('Error: Unsupported file type information "' .. - string(server.filetype) .. '" in LSP server registration') + server.filetype->string() .. '" in LSP server registration') continue endif endfor @@ -411,7 +413,7 @@ def lsp#showDiagnostics(): void var qflist: list> = [] var text: string - for [lnum, diag] in items(lspserver.diagsMap[bnr]) + for [lnum, diag] in lspserver.diagsMap[bnr]->items() text = diag.message->substitute("\n\\+", "\n", 'g') qflist->add({'filename': fname, 'lnum': diag.range.start.line + 1, @@ -452,11 +454,13 @@ enddef # sort the diaganostics messages for a buffer by line number def s:getSortedDiagLines(lspserver: dict, bnr: number): list + # create a list of line numbers from the diag map keys var lnums: list = - lspserver.diagsMap[bnr]->keys()->mapnew((_, v) => str2nr(v)) + lspserver.diagsMap[bnr]->keys()->mapnew((_, v) => v->str2nr()) return lnums->sort((a, b) => a - b) enddef +# jump to the next/previous/first diagnostic message in the current buffer def lsp#jumpToDiag(which: string): void var ftype = &filetype if ftype == '' @@ -494,7 +498,7 @@ def lsp#jumpToDiag(which: string): void # Find the entry just before the current line (binary search) var curlnum: number = line('.') - for lnum in (which == 'next') ? sortedDiags : reverse(sortedDiags) + for lnum in (which == 'next') ? sortedDiags : sortedDiags->reverse() if (which == 'next' && lnum > curlnum) || (which == 'prev' && lnum < curlnum) cursor(lnum, 1) @@ -645,7 +649,7 @@ def s:outlineJumpToSymbol() # Highlight the selected symbol prop_remove({type: 'LspOutlineHighlight'}) - var col: number = match(getline('.'), '\S') + 1 + var col: number = getline('.')->match('\S') + 1 prop_add(line('.'), col, {type: 'LspOutlineHighlight', length: w:lspSymbols.lnumTable[lnum].name->len()}) @@ -688,7 +692,7 @@ def s:addSymbolText(bnr: number, lnumMap: list>, children: bool) var prefix: string = pfx .. ' ' - for [symType, symbols] in items(symbolTypeTable) + for [symType, symbols] in symbolTypeTable->items() if !children # Add an empty line for the top level symbol types. For types in the # children symbols, don't add the empty line. @@ -729,11 +733,11 @@ def lsp#updateOutlineWindow(fname: string, skipOutlineRefresh = true var prevWinID: number = win_getid() - win_gotoid(wid) + wid->win_gotoid() # if the file displayed in the outline window is same as the new file, then # save and restore the cursor position - var symbols = getwinvar(wid, 'lspSymbols', {}) + var symbols = wid->getwinvar('lspSymbols', {}) var saveCursor: list = [] if !symbols->empty() && symbols.filename == fname saveCursor = getcurpos() @@ -742,8 +746,8 @@ def lsp#updateOutlineWindow(fname: string, :setlocal modifiable :silent! :%d _ setline(1, ['# LSP Outline View', - '# ' .. fnamemodify(fname, ':t') .. ' (' - .. fnamemodify(fname, ':h') .. ')']) + '# ' .. fname->fnamemodify(':t') .. ' (' + .. fname->fnamemodify(':h') .. ')']) # First two lines in the buffer display comment information var lnumMap: list> = [{}, {}] @@ -755,10 +759,10 @@ def lsp#updateOutlineWindow(fname: string, :setlocal nomodifiable if !saveCursor->empty() - setpos('.', saveCursor) + saveCursor->setpos('.') endif - win_gotoid(prevWinID) + prevWinID->win_gotoid() # Highlight the current symbol s:outlineHighlightCurrentSymbol() @@ -768,7 +772,7 @@ def lsp#updateOutlineWindow(fname: string, enddef def s:outlineHighlightCurrentSymbol() - var fname: string = fnamemodify(expand('%'), ':p') + var fname: string = expand('%')->fnamemodify(':p') if fname == '' || &filetype == '' return endif @@ -780,7 +784,7 @@ def s:outlineHighlightCurrentSymbol() # Check whether the symbols for this file are displayed in the outline # window - var lspSymbols = getwinvar(wid, 'lspSymbols', {}) + var lspSymbols = wid->getwinvar('lspSymbols', {}) if lspSymbols->empty() || lspSymbols.filename != fname return endif @@ -890,7 +894,7 @@ def s:openOutlineWindow() autocmd CursorHold * call s:outlineHighlightCurrentSymbol() augroup END - win_gotoid(prevWinID) + prevWinID->win_gotoid() enddef def s:requestDocSymbols() @@ -1059,7 +1063,7 @@ def s:filterSymbols(lspserver: dict, popupID: number, key: string): bool || key == "\" # scroll the popup window var cmd: string = 'normal! ' .. (key == "\" ? 'j' : key == "\" ? 'k' : key) - cmd->win_execute(popupID) + win_execute(popupID, cmd) key_handled = true elseif key == "\" || key == "\" # Use native Vim handling for these keys @@ -1208,7 +1212,7 @@ def lsp#listWorkspaceFolders() return endif - echomsg 'Workspace Folders: ' .. string(lspserver.workspaceFolders) + echomsg 'Workspace Folders: ' .. lspserver.workspaceFolders->string() enddef # Add a workspace folder. Default is to use the current folder. diff --git a/autoload/lspserver.vim b/autoload/lspserver.vim index fc344c7..0c198b3 100644 --- a/autoload/lspserver.vim +++ b/autoload/lspserver.vim @@ -10,9 +10,9 @@ import {ProcessReply, ProcessMessages} from './handlers.vim' import {WarnMsg, ErrMsg, - ClearTraceLogs, TraceLog, LspUriToFile, + LspBufnrToUri, LspFileToUri} from './util.vim' # LSP server standard output handler @@ -53,7 +53,6 @@ def s:startServer(lspserver: dict): number err_cb: function('s:error_cb', [lspserver]), exit_cb: function('s:exit_cb', [lspserver])} - ClearTraceLogs() lspserver.data = '' lspserver.caps = {} lspserver.nextID = 1 @@ -115,7 +114,7 @@ def s:initServer(lspserver: dict) initparams.processId = getpid() initparams.clientInfo = { name: 'Vim', - version: string(v:versionlong), + version: v:versionlong->string(), } var curdir: string = getcwd() initparams.rootPath = curdir @@ -196,7 +195,7 @@ def s:createRequest(lspserver: dict, method: string): dict req.params = {} # Save the request, so that the corresponding response can be processed - lspserver.requests->extend({[string(req.id)]: req}) + lspserver.requests->extend({[req.id->string()]: req}) return req enddef @@ -223,7 +222,7 @@ enddef # send a response message to the server def s:sendResponse(lspserver: dict, request: dict, result: dict, error: dict) var resp: dict = lspserver.createResponse(request.id) - if type(result) != v:t_none + if result->type() != v:t_none resp->extend({result: result}) else resp->extend({error: error}) @@ -248,7 +247,7 @@ def s:textdocDidOpen(lspserver: dict, bnr: number, ftype: string): void # interface DidOpenTextDocumentParams # interface TextDocumentItem var tdi = {} - tdi.uri = LspFileToUri(bufname(bnr)) + tdi.uri = LspBufnrToUri(bnr) tdi.languageId = ftype tdi.version = 1 tdi.text = getbufline(bnr, 1, '$')->join("\n") .. "\n" @@ -264,7 +263,7 @@ def s:textdocDidClose(lspserver: dict, bnr: number): void # interface DidCloseTextDocumentParams # interface TextDocumentIdentifier var tdid = {} - tdid.uri = LspFileToUri(bufname(bnr)) + tdid.uri = LspBufnrToUri(bnr) notif.params->extend({textDocument: tdid}) lspserver.sendMessage(notif) @@ -280,7 +279,7 @@ def s:textdocDidChange(lspserver: dict, bnr: number, start: number, # interface DidChangeTextDocumentParams # interface VersionedTextDocumentIdentifier var vtdid: dict = {} - vtdid.uri = LspFileToUri(bufname(bnr)) + vtdid.uri = LspBufnrToUri(bnr) # Use Vim 'changedtick' as the LSP document version number vtdid.version = bnr->getbufvar('changedtick') notif.params->extend({textDocument: vtdid}) @@ -492,7 +491,7 @@ def s:didSaveFile(lspserver: dict, bnr: number): void var notif: dict = lspserver.createNotification('textDocument/didSave') # interface: DidSaveTextDocumentParams - notif.params->extend({textDocument: {uri: LspFileToUri(bufname(bnr))}}) + notif.params->extend({textDocument: {uri: LspBufnrToUri(bnr)}}) lspserver.sendMessage(notif) enddef @@ -656,7 +655,7 @@ def s:codeAction(lspserver: dict, fname_arg: string) # interface CodeActionParams var fname: string = fnamemodify(fname_arg, ':p') - var bnr: number = bufnr(fname_arg) + var bnr: number = fname_arg->bufnr() req.params->extend({textDocument: {uri: LspFileToUri(fname)}}) var r: dict> = { start: {line: line('.') - 1, character: charcol('.') - 1}, diff --git a/autoload/util.vim b/autoload/util.vim index 0b2bef2..d930fd3 100644 --- a/autoload/util.vim +++ b/autoload/util.vim @@ -89,6 +89,11 @@ export def LspFileToUri(fname: string): string return uri enddef +# Convert a Vim buffer number to an LSP URI (file://) +export def LspBufnrToUri(bnr: number): string + return LspFileToUri(bnr->bufname()) +enddef + # Returns the byte number of the specified LSP position in buffer 'bnr'. # LSP's line and characters are 0-indexed. # Vim's line and columns are 1-indexed. diff --git a/doc/lsp.txt b/doc/lsp.txt index 0f46a50..24acb68 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -111,30 +111,52 @@ The following commands are provided: ============================================================================== 4. Configuration *lsp-configuration* -To register a LSP server, add the following lines to your .vimrc file: -> +To register one or more LSP servers, use the lsp#addServer() function with a +list of LSP server details in the .vimrc file. + +For example, to add the LSP servers for the Javascript, Typescript and Python +file types, add the following commands to the .vimrc file: > + let lspServers = [ - \ { - \ 'filetype': ['c', 'cpp'], - \ 'path': '/usr/local/bin/clangd', - \ 'args': ['--background-index'] - \ }, \ { \ 'filetype': ['javascript', 'typescript'], \ 'path': '/usr/local/bin/typescript-language-server', \ 'args': ['--stdio'] \ } \ { - \ 'filetype': 'sh', - \ 'path': '/usr/local/bin/bash-language-server', - \ 'args': ['start'] - \ }, + \ 'filetype': 'python', + \ 'path': '/usr/local/bin/pyls', + \ 'args': ['--check-parent-process', '-v'] + \ } \ ] call lsp#addServer(lspServers) < -The above lines add the LSP servers for C, C++, Javascript, Typescript and -Shell script file types. +Depending on the location of the typescript and python pyls language servers +installed in your system, update the 'path' in the above snippet +appripriately. + +Another example, for adding the LSP servers for the C, C++, Shell script and +Vim file types: > + let lspServers = [ + \ #{ + \ filetype: ['c', 'cpp'], + \ path: '/usr/local/bin/clangd', + \ args: ['--background-index'] + \ }, + \ #{ + \ filetype: 'sh', + \ path: '/usr/local/bin/bash-language-server', + \ args: ['start'] + \ }, + \ #{ + \ filetype: ['vim'], + \ path: '/usr/local/bin/vim-language-server', + \ args: ['--stdio'] + \ } + \ ] + call lsp#addServer(lspServers) +< To add a LSP server, the following information is needed: filetype One or more file types supported by the LSP server. diff --git a/test/test_lsp.vim b/test/test_lsp.vim index 2a5374c..d18bfbd 100644 --- a/test/test_lsp.vim +++ b/test/test_lsp.vim @@ -96,12 +96,55 @@ def Test_lsp_formatting() :%bw! enddef +# Test for showing all the references of a symbol in a file using LSP +def Test_lsp_show_references() + :silent! edit Xtest.c + var lines: list =<< trim END + int count; + void redFunc() + { + int count, i; + count = 10; + i = count; + } + void blueFunc() + { + int count, j; + count = 20; + j = count; + } + END + setline(1, lines) + :redraw! + cursor(5, 2) + var bnr: number = bufnr() + :LspShowReferences + :sleep 1 + var qfl: list> = getqflist() + assert_equal('quickfix', getwinvar(winnr('$'), '&buftype')) + assert_equal(bnr, qfl[0].bufnr) + assert_equal(3, qfl->len()) + assert_equal([4, 6], [qfl[0].lnum, qfl[0].col]) + assert_equal([5, 2], [qfl[1].lnum, qfl[1].col]) + assert_equal([6, 6], [qfl[2].lnum, qfl[2].col]) + :only + cursor(1, 5) + :LspShowReferences + :sleep 1 + qfl = getqflist() + assert_equal(1, qfl->len()) + assert_equal([1, 5], [qfl[0].lnum, qfl[0].col]) + + :%bw! +enddef + def LspRunTests() # Edit a dummy C file to start the LSP server :edit Xtest.c :sleep 1 :%bw! + # Get the list of test functions in this file and call them var fns: list = execute('function /Test_') ->split("\n") ->map("v:val->substitute('^def \\d\\+_', '', '')") @@ -116,7 +159,7 @@ def LspRunTests() endif endfor - echomsg "Success: All LSP tests have passed" + echomsg "Success: All the LSP tests have passed" enddef LspRunTests() -- 2.50.0