From d043d7c8ce62cf07c730af28998b4e2f4dfce3f7 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Fri, 14 Jan 2022 07:22:27 -0800 Subject: [PATCH] Move the text edit and code action functions to a separate file --- autoload/codeaction.vim | 58 ++++++++ autoload/handlers.vim | 287 +++----------------------------------- autoload/lspoptions.vim | 2 + autoload/lspserver.vim | 12 +- autoload/outline.vim | 1 + autoload/symbolsearch.vim | 1 + autoload/textedit.vim | 247 ++++++++++++++++++++++++++++++++ doc/lsp.txt | 11 +- test/test_lsp.vim | 35 +++++ 9 files changed, 378 insertions(+), 276 deletions(-) create mode 100644 autoload/codeaction.vim create mode 100644 autoload/textedit.vim diff --git a/autoload/codeaction.vim b/autoload/codeaction.vim new file mode 100644 index 0000000..28fec07 --- /dev/null +++ b/autoload/codeaction.vim @@ -0,0 +1,58 @@ +vim9script + +var util = {} +var textedit = {} + +if has('patch-8.2.4019') + import './util.vim' as util_import + import './textedit.vim' as textedit_import + + util.WarnMsg = util_import.WarnMsg + textedit.ApplyWorkspaceEdit = textedit_import.ApplyWorkspaceEdit +else + import WarnMsg from './util.vim' + import ApplyWorkspaceEdit from './textedit.vim' + + util.WarnMsg = WarnMsg + textedit.ApplyWorkspaceEdit = ApplyWorkspaceEdit +endif + +export def ApplyCodeAction(lspserver: dict, actions: list>): void + if actions->empty() + # no action can be performed + util.WarnMsg('No code action is available') + return + endif + + var prompt: list = ['Code Actions:'] + var act: dict + for i in range(actions->len()) + act = actions[i] + var t: string = act.title->substitute('\r\n', '\\r\\n', 'g') + t = t->substitute('\n', '\\n', 'g') + prompt->add(printf("%d. %s", i + 1, t)) + endfor + var choice = inputlist(prompt) + if choice < 1 || choice > prompt->len() + return + endif + + var selAction = actions[choice - 1] + + # textDocument/codeAction can return either Command[] or CodeAction[]. + # If it is a CodeAction, it can have either an edit, a command or both. + # Edits should be executed first. + if selAction->has_key('edit') || selAction->has_key('command') + if selAction->has_key('edit') + # apply edit first + textedit.ApplyWorkspaceEdit(selAction.edit) + endif + if selAction->has_key('command') + lspserver.executeCommand(selAction) + endif + else + lspserver.executeCommand(selAction) + endif +enddef + +# vim: shiftwidth=2 softtabstop=2 diff --git a/autoload/handlers.vim b/autoload/handlers.vim index 6b32d33..8f16e9f 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -8,12 +8,16 @@ var opt = {} var util = {} var diag = {} var outline = {} +var textedit = {} +var codeaction = {} if has('patch-8.2.4019') import './lspoptions.vim' as opt_import import './util.vim' as util_import import './diag.vim' as diag_import import './outline.vim' as outline_import + import './textedit.vim' as textedit_import + import './codeaction.vim' as codeaction_import opt.lspOptions = opt_import.lspOptions util.WarnMsg = util_import.WarnMsg @@ -23,6 +27,9 @@ if has('patch-8.2.4019') util.GetLineByteFromPos = util_import.GetLineByteFromPos diag.DiagNotification = diag_import.DiagNotification outline.UpdateOutlineWindow = outline_import.UpdateOutlineWindow + textedit.ApplyTextEdits = textedit_import.ApplyTextEdits + textedit.ApplyWorkspaceEdit = textedit_import.ApplyWorkspaceEdit + codeaction.ApplyCodeAction = codeaction_import.ApplyCodeAction else import lspOptions from './lspoptions.vim' import {WarnMsg, @@ -32,6 +39,8 @@ else GetLineByteFromPos} from './util.vim' import DiagNotification from './diag.vim' import UpdateOutlineWindow from './outline.vim' + import {ApplyTextEdits, ApplyWorkspaceEdit} from './textedit.vim' + import ApplyCodeAction from './codeaction.vim' opt.lspOptions = lspOptions util.WarnMsg = WarnMsg @@ -41,6 +50,9 @@ else util.GetLineByteFromPos = GetLineByteFromPos diag.DiagNotification = DiagNotification outline.UpdateOutlineWindow = UpdateOutlineWindow + textedit.ApplyTextEdits = ApplyTextEdits + textedit.ApplyWorkspaceEdit = ApplyWorkspaceEdit + codeaction.ApplyCodeAction = ApplyCodeAction endif # process the 'initialize' method reply from the LSP server @@ -550,228 +562,6 @@ def s:processDocSymbolReply(lspserver: dict, req: dict, reply: dict, b: dict): number - # line number - if a.A[0] != b.A[0] - return b.A[0] - a.A[0] - endif - # column number - if a.A[1] != b.A[1] - return b.A[1] - a.A[1] - endif - - return 0 -enddef - -# Replaces text in a range with new text. -# -# CAUTION: Changes in-place! -# -# 'lines': Original list of strings -# 'A': Start position; [line, col] -# 'B': End position [line, col] -# 'new_lines' A list of strings to replace the original -# -# returns the modified 'lines' -def s:set_lines(lines: list, A: list, B: list, - new_lines: list): list - var i_0: number = A[0] - - # If it extends past the end, truncate it to the end. This is because the - # way the LSP describes the range including the last newline is by - # specifying a line number after what we would call the last line. - var numlines: number = lines->len() - var i_n = [B[0], numlines - 1]->min() - - if i_0 < 0 || i_0 >= numlines || i_n < 0 || i_n >= numlines - #util.WarnMsg("set_lines: Invalid range, A = " .. A->string() - # .. ", B = " .. B->string() .. ", numlines = " .. numlines - # .. ", new lines = " .. new_lines->string()) - var msg = "set_lines: Invalid range, A = " .. A->string() - msg ..= ", B = " .. B->string() .. ", numlines = " .. numlines - msg ..= ", new lines = " .. new_lines->string() - util.WarnMsg(msg) - return lines - endif - - # save the prefix and suffix text before doing the replacements - var prefix: string = '' - var suffix: string = lines[i_n][B[1] :] - if A[1] > 0 - prefix = lines[i_0][0 : A[1] - 1] - endif - - var new_lines_len: number = new_lines->len() - - #echomsg 'i_0 = ' .. i_0 .. ', i_n = ' .. i_n .. ', new_lines = ' .. string(new_lines) - var n: number = i_n - i_0 + 1 - if n != new_lines_len - if n > new_lines_len - # remove the deleted lines - lines->remove(i_0, i_0 + n - new_lines_len - 1) - else - # add empty lines for newly the added lines (will be replaced with the - # actual lines below) - lines->extend(repeat([''], new_lines_len - n), i_0) - endif - endif - #echomsg "lines(1) = " .. string(lines) - - # replace the previous lines with the new lines - for i in range(new_lines_len) - lines[i_0 + i] = new_lines[i] - endfor - #echomsg "lines(2) = " .. string(lines) - - # append the suffix (if any) to the last line - if suffix != '' - var i = i_0 + new_lines_len - 1 - lines[i] = lines[i] .. suffix - endif - #echomsg "lines(3) = " .. string(lines) - - # prepend the prefix (if any) to the first line - if prefix != '' - lines[i_0] = prefix .. lines[i_0] - endif - #echomsg "lines(4) = " .. string(lines) - - return lines -enddef - -# Apply set of text edits to the specified buffer -# The text edit logic is ported from the Neovim lua implementation -def s:applyTextEdits(bnr: number, text_edits: list>): void - if text_edits->empty() - return - endif - - # if the buffer is not loaded, load it and make it a listed buffer - if !bnr->bufloaded() - bnr->bufload() - endif - setbufvar(bnr, '&buflisted', true) - - var start_line: number = 4294967295 # 2 ^ 32 - var finish_line: number = -1 - var updated_edits: list> = [] - var start_row: number - var start_col: number - var end_row: number - var end_col: number - - # create a list of buffer positions where the edits have to be applied. - for e in text_edits - # Adjust the start and end columns for multibyte characters - start_row = e.range.start.line - start_col = util.GetLineByteFromPos(bnr, e.range.start) - end_row = e.range.end.line - end_col = util.GetLineByteFromPos(bnr, e.range.end) - start_line = [e.range.start.line, start_line]->min() - finish_line = [e.range.end.line, finish_line]->max() - - updated_edits->add({A: [start_row, start_col], - B: [end_row, end_col], - lines: e.newText->split("\n", true)}) - endfor - - # Reverse sort the edit operations by descending line and column numbers so - # that they can be applied without interfering with each other. - updated_edits->sort('s:edit_sort_func') - - var lines: list = bnr->getbufline(start_line + 1, finish_line + 1) - var fix_eol: bool = bnr->getbufvar('&fixeol') - var set_eol = fix_eol && bnr->getbufinfo()[0].linecount <= finish_line + 1 - if set_eol && lines[-1]->len() != 0 - lines->add('') - endif - - #echomsg 'lines(1) = ' .. string(lines) - #echomsg updated_edits - - for e in updated_edits - var A: list = [e.A[0] - start_line, e.A[1]] - var B: list = [e.B[0] - start_line, e.B[1]] - lines = s:set_lines(lines, A, B, e.lines) - endfor - - #echomsg 'lines(2) = ' .. string(lines) - - # If the last line is empty and we need to set EOL, then remove it. - if set_eol && lines[-1]->len() == 0 - lines->remove(-1) - endif - - #echomsg 'applyTextEdits: start_line = ' .. start_line .. ', finish_line = ' .. finish_line - #echomsg 'lines = ' .. string(lines) - - # Delete all the lines that need to be modified - bnr->deletebufline(start_line + 1, finish_line + 1) - - # if the buffer is empty, appending lines before the first line adds an - # extra empty line at the end. Delete the empty line after appending the - # lines. - var dellastline: bool = false - if start_line == 0 && bnr->getbufinfo()[0].linecount == 1 && - bnr->getbufline(1)[0] == '' - dellastline = true - endif - - # Append the updated lines - appendbufline(bnr, start_line, lines) - - if dellastline - bnr->deletebufline(bnr->getbufinfo()[0].linecount) - endif -enddef - -# interface TextDocumentEdit -def s:applyTextDocumentEdit(textDocEdit: dict) - var bnr: number = bufnr(util.LspUriToFile(textDocEdit.textDocument.uri)) - if bnr == -1 - util.ErrMsg('Error: Text Document edit, buffer ' .. textDocEdit.textDocument.uri .. ' is not found') - return - endif - s:applyTextEdits(bnr, textDocEdit.edits) -enddef - -# interface WorkspaceEdit -def s:applyWorkspaceEdit(workspaceEdit: dict) - if workspaceEdit->has_key('documentChanges') - for change in workspaceEdit.documentChanges - if change->has_key('kind') - util.ErrMsg('Error: Unsupported change in workspace edit [' .. change.kind .. ']') - else - s:applyTextDocumentEdit(change) - endif - endfor - return - endif - - if !workspaceEdit->has_key('changes') - return - endif - - var save_cursor: list = getcurpos() - for [uri, changes] in workspaceEdit.changes->items() - var fname: string = util.LspUriToFile(uri) - var bnr: number = fname->bufnr() - if bnr == -1 - # file is already removed - continue - endif - - # interface TextEdit - s:applyTextEdits(bnr, changes) - endfor - # Restore the cursor to the location before the edit - save_cursor->setpos('.') -enddef - # process the 'textDocument/formatting' reply from the LSP server # Result: TextEdit[] | null def s:processFormatReply(lspserver: dict, req: dict, reply: dict) @@ -792,7 +582,7 @@ def s:processFormatReply(lspserver: dict, req: dict, reply: dict) # interface TextEdit # Apply each of the text edit operations var save_cursor: list = getcurpos() - s:applyTextEdits(bnr, reply.result) + textedit.ApplyTextEdits(bnr, reply.result) save_cursor->setpos('.') enddef @@ -806,58 +596,13 @@ def s:processRenameReply(lspserver: dict, req: dict, reply: dict) endif # result: WorkspaceEdit - s:applyWorkspaceEdit(reply.result) -enddef - -# Request the LSP server to execute a command -# Request: workspace/executeCommand -# Params: ExecuteCommandParams -def s:executeCommand(lspserver: dict, cmd: dict) - var req = lspserver.createRequest('workspace/executeCommand') - req.params->extend(cmd) - lspserver.sendMessage(req) + textedit.ApplyWorkspaceEdit(reply.result) enddef # process the 'textDocument/codeAction' reply from the LSP server # Result: (Command | CodeAction)[] | null def s:processCodeActionReply(lspserver: dict, req: dict, reply: dict) - if reply.result->empty() - # no action can be performed - util.WarnMsg('No code action is available') - return - endif - - var actions: list> = reply.result - - var prompt: list = ['Code Actions:'] - var act: dict - for i in range(actions->len()) - act = actions[i] - var t: string = act.title->substitute('\r\n', '\\r\\n', 'g') - t = t->substitute('\n', '\\n', 'g') - prompt->add(printf("%d. %s", i + 1, t)) - endfor - var choice = inputlist(prompt) - if choice < 1 || choice > prompt->len() - return - endif - - var selAction = actions[choice - 1] - - # textDocument/codeAction can return either Command[] or CodeAction[]. - # If it is a CodeAction, it can have either an edit, a command or both. - # Edits should be executed first. - if selAction->has_key('edit') || selAction->has_key('command') - if selAction->has_key('edit') - # apply edit first - s:applyWorkspaceEdit(selAction.edit) - endif - if selAction->has_key('command') - s:executeCommand(lspserver, selAction) - endif - else - s:executeCommand(lspserver, selAction) - endif + codeaction.ApplyCodeAction(lspserver, reply.result) enddef # Reply: 'textDocument/selectionRange' @@ -1109,7 +854,7 @@ def s:processApplyEditReq(lspserver: dict, request: dict) if workspaceEditParams->has_key('label') :echomsg "Workspace edit" .. workspaceEditParams.label endif - s:applyWorkspaceEdit(workspaceEditParams.edit) + textedit.ApplyWorkspaceEdit(workspaceEditParams.edit) # TODO: Need to return the proper result of the edit operation lspserver.sendResponse(request, {applied: true}, {}) enddef diff --git a/autoload/lspoptions.vim b/autoload/lspoptions.vim index 8298436..33e73bd 100644 --- a/autoload/lspoptions.vim +++ b/autoload/lspoptions.vim @@ -25,3 +25,5 @@ export def LspOptionsSet(opts: dict) lspOptions[key] = opts[key] endfor enddef + +# vim: shiftwidth=2 softtabstop=2 diff --git a/autoload/lspserver.vim b/autoload/lspserver.vim index cef6987..a1dff8d 100644 --- a/autoload/lspserver.vim +++ b/autoload/lspserver.vim @@ -818,6 +818,15 @@ def s:foldRange(lspserver: dict, fname: string) lspserver.sendMessage(req) enddef +# Request the LSP server to execute a command +# Request: workspace/executeCommand +# Params: ExecuteCommandParams +def s:executeCommand(lspserver: dict, cmd: dict) + var req = lspserver.createRequest('workspace/executeCommand') + req.params->extend(cmd) + lspserver.sendMessage(req) +enddef + export def NewLspServer(path: string, args: list): dict var lspserver: dict = { path: path, @@ -876,7 +885,8 @@ export def NewLspServer(path: string, args: list): dict addWorkspaceFolder: function('s:addWorkspaceFolder', [lspserver]), removeWorkspaceFolder: function('s:removeWorkspaceFolder', [lspserver]), selectionRange: function('s:selectionRange', [lspserver]), - foldRange: function('s:foldRange', [lspserver]) + foldRange: function('s:foldRange', [lspserver]), + executeCommand: function('s:executeCommand', [lspserver]) }) return lspserver diff --git a/autoload/outline.vim b/autoload/outline.vim index a8cf918..53021ee 100644 --- a/autoload/outline.vim +++ b/autoload/outline.vim @@ -276,3 +276,4 @@ export def OpenOutlineWindow() prevWinID->win_gotoid() enddef +# vim: shiftwidth=2 softtabstop=2 diff --git a/autoload/symbolsearch.vim b/autoload/symbolsearch.vim index 3d46354..cee7266 100644 --- a/autoload/symbolsearch.vim +++ b/autoload/symbolsearch.vim @@ -142,3 +142,4 @@ export def ShowSymbolMenu(lspserver: dict, query: string) echo 'Symbol: ' .. query enddef +# vim: shiftwidth=2 softtabstop=2 diff --git a/autoload/textedit.vim b/autoload/textedit.vim new file mode 100644 index 0000000..abddb4e --- /dev/null +++ b/autoload/textedit.vim @@ -0,0 +1,247 @@ +vim9script + +var util = {} + +if has('patch-8.2.4019') + import './util.vim' as util_import + + util.WarnMsg = util_import.WarnMsg + util.ErrMsg = util_import.ErrMsg + util.LspUriToFile = util_import.LspUriToFile + util.GetLineByteFromPos = util_import.GetLineByteFromPos +else + import {WarnMsg, + ErrMsg, + TraceLog, + LspUriToFile, + GetLineByteFromPos} from './util.vim' + + util.WarnMsg = WarnMsg + util.ErrMsg = ErrMsg + util.LspUriToFile = LspUriToFile + util.GetLineByteFromPos = GetLineByteFromPos +endif + +# sort the list of edit operations in the descending order of line and column +# numbers. +# 'a': {'A': [lnum, col], 'B': [lnum, col]} +# 'b': {'A': [lnum, col], 'B': [lnum, col]} +def s:edit_sort_func(a: dict, b: dict): number + # line number + if a.A[0] != b.A[0] + return b.A[0] - a.A[0] + endif + # column number + if a.A[1] != b.A[1] + return b.A[1] - a.A[1] + endif + + return 0 +enddef + +# Replaces text in a range with new text. +# +# CAUTION: Changes in-place! +# +# 'lines': Original list of strings +# 'A': Start position; [line, col] +# 'B': End position [line, col] +# 'new_lines' A list of strings to replace the original +# +# returns the modified 'lines' +def s:set_lines(lines: list, A: list, B: list, + new_lines: list): list + var i_0: number = A[0] + + # If it extends past the end, truncate it to the end. This is because the + # way the LSP describes the range including the last newline is by + # specifying a line number after what we would call the last line. + var numlines: number = lines->len() + var i_n = [B[0], numlines - 1]->min() + + if i_0 < 0 || i_0 >= numlines || i_n < 0 || i_n >= numlines + #util.WarnMsg("set_lines: Invalid range, A = " .. A->string() + # .. ", B = " .. B->string() .. ", numlines = " .. numlines + # .. ", new lines = " .. new_lines->string()) + var msg = "set_lines: Invalid range, A = " .. A->string() + msg ..= ", B = " .. B->string() .. ", numlines = " .. numlines + msg ..= ", new lines = " .. new_lines->string() + util.WarnMsg(msg) + return lines + endif + + # save the prefix and suffix text before doing the replacements + var prefix: string = '' + var suffix: string = lines[i_n][B[1] :] + if A[1] > 0 + prefix = lines[i_0][0 : A[1] - 1] + endif + + var new_lines_len: number = new_lines->len() + + #echomsg 'i_0 = ' .. i_0 .. ', i_n = ' .. i_n .. ', new_lines = ' .. string(new_lines) + var n: number = i_n - i_0 + 1 + if n != new_lines_len + if n > new_lines_len + # remove the deleted lines + lines->remove(i_0, i_0 + n - new_lines_len - 1) + else + # add empty lines for newly the added lines (will be replaced with the + # actual lines below) + lines->extend(repeat([''], new_lines_len - n), i_0) + endif + endif + #echomsg "lines(1) = " .. string(lines) + + # replace the previous lines with the new lines + for i in range(new_lines_len) + lines[i_0 + i] = new_lines[i] + endfor + #echomsg "lines(2) = " .. string(lines) + + # append the suffix (if any) to the last line + if suffix != '' + var i = i_0 + new_lines_len - 1 + lines[i] = lines[i] .. suffix + endif + #echomsg "lines(3) = " .. string(lines) + + # prepend the prefix (if any) to the first line + if prefix != '' + lines[i_0] = prefix .. lines[i_0] + endif + #echomsg "lines(4) = " .. string(lines) + + return lines +enddef + +# Apply set of text edits to the specified buffer +# The text edit logic is ported from the Neovim lua implementation +export def ApplyTextEdits(bnr: number, text_edits: list>): void + if text_edits->empty() + return + endif + + # if the buffer is not loaded, load it and make it a listed buffer + if !bnr->bufloaded() + bnr->bufload() + endif + setbufvar(bnr, '&buflisted', true) + + var start_line: number = 4294967295 # 2 ^ 32 + var finish_line: number = -1 + var updated_edits: list> = [] + var start_row: number + var start_col: number + var end_row: number + var end_col: number + + # create a list of buffer positions where the edits have to be applied. + for e in text_edits + # Adjust the start and end columns for multibyte characters + start_row = e.range.start.line + start_col = util.GetLineByteFromPos(bnr, e.range.start) + end_row = e.range.end.line + end_col = util.GetLineByteFromPos(bnr, e.range.end) + start_line = [e.range.start.line, start_line]->min() + finish_line = [e.range.end.line, finish_line]->max() + + updated_edits->add({A: [start_row, start_col], + B: [end_row, end_col], + lines: e.newText->split("\n", true)}) + endfor + + # Reverse sort the edit operations by descending line and column numbers so + # that they can be applied without interfering with each other. + updated_edits->sort('s:edit_sort_func') + + var lines: list = bnr->getbufline(start_line + 1, finish_line + 1) + var fix_eol: bool = bnr->getbufvar('&fixeol') + var set_eol = fix_eol && bnr->getbufinfo()[0].linecount <= finish_line + 1 + if set_eol && lines[-1]->len() != 0 + lines->add('') + endif + + #echomsg 'lines(1) = ' .. string(lines) + #echomsg updated_edits + + for e in updated_edits + var A: list = [e.A[0] - start_line, e.A[1]] + var B: list = [e.B[0] - start_line, e.B[1]] + lines = s:set_lines(lines, A, B, e.lines) + endfor + + #echomsg 'lines(2) = ' .. string(lines) + + # If the last line is empty and we need to set EOL, then remove it. + if set_eol && lines[-1]->len() == 0 + lines->remove(-1) + endif + + #echomsg 'ApplyTextEdits: start_line = ' .. start_line .. ', finish_line = ' .. finish_line + #echomsg 'lines = ' .. string(lines) + + # Delete all the lines that need to be modified + bnr->deletebufline(start_line + 1, finish_line + 1) + + # if the buffer is empty, appending lines before the first line adds an + # extra empty line at the end. Delete the empty line after appending the + # lines. + var dellastline: bool = false + if start_line == 0 && bnr->getbufinfo()[0].linecount == 1 && + bnr->getbufline(1)[0] == '' + dellastline = true + endif + + # Append the updated lines + appendbufline(bnr, start_line, lines) + + if dellastline + bnr->deletebufline(bnr->getbufinfo()[0].linecount) + endif +enddef + +# interface TextDocumentEdit +def s:applyTextDocumentEdit(textDocEdit: dict) + var bnr: number = bufnr(util.LspUriToFile(textDocEdit.textDocument.uri)) + if bnr == -1 + util.ErrMsg('Error: Text Document edit, buffer ' .. textDocEdit.textDocument.uri .. ' is not found') + return + endif + ApplyTextEdits(bnr, textDocEdit.edits) +enddef + +# interface WorkspaceEdit +export def ApplyWorkspaceEdit(workspaceEdit: dict) + if workspaceEdit->has_key('documentChanges') + for change in workspaceEdit.documentChanges + if change->has_key('kind') + util.ErrMsg('Error: Unsupported change in workspace edit [' .. change.kind .. ']') + else + s:applyTextDocumentEdit(change) + endif + endfor + return + endif + + if !workspaceEdit->has_key('changes') + return + endif + + var save_cursor: list = getcurpos() + for [uri, changes] in workspaceEdit.changes->items() + var fname: string = util.LspUriToFile(uri) + var bnr: number = fname->bufnr() + if bnr == -1 + # file is already removed + continue + endif + + # interface TextEdit + ApplyTextEdits(bnr, changes) + endfor + # Restore the cursor to the location before the edit + save_cursor->setpos('.') +enddef + +# vim: shiftwidth=2 softtabstop=2 diff --git a/doc/lsp.txt b/doc/lsp.txt index 1422d4e..2976c7b 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -334,10 +334,13 @@ diagnostic messages, you can add the following line to your .vimrc file: *:LspSymbolSearch* :LspSymbolSearch Perform a workspace wide search for the symbol . - 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. + If is not supplied, then you will be prompted to + 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. In the popup menu, the following keys can be used: diff --git a/test/test_lsp.vim b/test/test_lsp.vim index b71abb9..df0c8e3 100644 --- a/test/test_lsp.vim +++ b/test/test_lsp.vim @@ -138,6 +138,7 @@ def Test_lsp_show_references() :%bw! enddef +# Test for LSP diagnostics def Test_lsp_diags() :silent! edit Xtest.c var lines: list =<< trim END @@ -190,6 +191,40 @@ def Test_lsp_diags() :%bw! enddef +# Test for LSP code action to apply fixes +def Test_lsp_codeaction() + var lines: list =<< trim END + void testFunc() + { + int count; + count == 20; + } + END + writefile(lines, 'Xtest.c') + var args: list = v:argv + args->add('Xtest.c') + var buf = term_start(args, {term_finish: 'close'}) + buf->term_wait() + sleep 100m + buf->term_sendkeys('4G') + buf->term_wait() + buf->term_sendkeys(":LspCodeAction\") + buf->term_wait() + sleep 100m + buf->term_sendkeys("1") + sleep 100m + buf->term_sendkeys("\") + buf->term_wait() + sleep 100m + buf->term_sendkeys(":wq\") + buf->term_wait() + sleep 100m + var l = readfile('Xtest.c') + assert_equal("\tcount = 20;", l[3]) + delete('Xtest.c') + :%bw! +enddef + def LspRunTests() # Edit a dummy C file to start the LSP server :edit Xtest.c -- 2.48.1