--- /dev/null
+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<any>, actions: list<dict<any>>): void
+ if actions->empty()
+ # no action can be performed
+ util.WarnMsg('No code action is available')
+ return
+ endif
+
+ var prompt: list<string> = ['Code Actions:']
+ var act: dict<any>
+ 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
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
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,
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
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
outline.UpdateOutlineWindow(fname, symbolTypeTable, symbolLineTable)
enddef
-# 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<any>, b: dict<any>): 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<string>, A: list<number>, B: list<number>,
- new_lines: list<string>): list<string>
- 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<dict<any>>): 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<dict<any>> = []
- 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<string> = 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<number> = [e.A[0] - start_line, e.A[1]]
- var B: list<number> = [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<any>)
- 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<any>)
- 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<number> = 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<any>, req: dict<any>, reply: dict<any>)
# interface TextEdit
# Apply each of the text edit operations
var save_cursor: list<number> = getcurpos()
- s:applyTextEdits(bnr, reply.result)
+ textedit.ApplyTextEdits(bnr, reply.result)
save_cursor->setpos('.')
enddef
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<any>, cmd: dict<any>)
- 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<any>, req: dict<any>, reply: dict<any>)
- if reply.result->empty()
- # no action can be performed
- util.WarnMsg('No code action is available')
- return
- endif
-
- var actions: list<dict<any>> = reply.result
-
- var prompt: list<string> = ['Code Actions:']
- var act: dict<any>
- 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'
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
lspOptions[key] = opts[key]
endfor
enddef
+
+# vim: shiftwidth=2 softtabstop=2
lspserver.sendMessage(req)
enddef
+# Request the LSP server to execute a command
+# Request: workspace/executeCommand
+# Params: ExecuteCommandParams
+def s:executeCommand(lspserver: dict<any>, cmd: dict<any>)
+ var req = lspserver.createRequest('workspace/executeCommand')
+ req.params->extend(cmd)
+ lspserver.sendMessage(req)
+enddef
+
export def NewLspServer(path: string, args: list<string>): dict<any>
var lspserver: dict<any> = {
path: path,
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
prevWinID->win_gotoid()
enddef
+# vim: shiftwidth=2 softtabstop=2
echo 'Symbol: ' .. query
enddef
+# vim: shiftwidth=2 softtabstop=2
--- /dev/null
+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<any>, b: dict<any>): 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<string>, A: list<number>, B: list<number>,
+ new_lines: list<string>): list<string>
+ 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<dict<any>>): 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<dict<any>> = []
+ 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<string> = 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<number> = [e.A[0] - start_line, e.A[1]]
+ var B: list<number> = [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<any>)
+ 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<any>)
+ 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<number> = 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
*:LspSymbolSearch*
:LspSymbolSearch <sym> Perform a workspace wide search for the symbol <sym>.
- 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 <sym> 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:
:%bw!
enddef
+# Test for LSP diagnostics
def Test_lsp_diags()
:silent! edit Xtest.c
var lines: list<string> =<< trim END
:%bw!
enddef
+# Test for LSP code action to apply fixes
+def Test_lsp_codeaction()
+ var lines: list<string> =<< trim END
+ void testFunc()
+ {
+ int count;
+ count == 20;
+ }
+ END
+ writefile(lines, 'Xtest.c')
+ var args: list<any> = 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\<CR>")
+ buf->term_wait()
+ sleep 100m
+ buf->term_sendkeys("1")
+ sleep 100m
+ buf->term_sendkeys("\<CR>")
+ buf->term_wait()
+ sleep 100m
+ buf->term_sendkeys(":wq\<CR>")
+ 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