aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTJ DeVries <devries.timothyj@gmail.com>2021-01-11 11:39:11 -0500
committerGitHub <noreply@github.com>2021-01-11 11:39:11 -0500
commite0a4399adc4472b9b02a26c7f93b056c4725a6b9 (patch)
tree81980fd4f008fd4f404a913bd3d40d86c327b1de
parent0c0c3ee330c9e3aa7d6f054073b999c5f8001472 (diff)
downloadrneovim-e0a4399adc4472b9b02a26c7f93b056c4725a6b9.tar.gz
rneovim-e0a4399adc4472b9b02a26c7f93b056c4725a6b9.tar.bz2
rneovim-e0a4399adc4472b9b02a26c7f93b056c4725a6b9.zip
fix(lsp): Allow subsequent text document edits to pass (#13534)
* fix: Allow subsequent text document edits to pass * fixup: cleaner code * add tests
-rw-r--r--runtime/doc/lsp.txt10
-rw-r--r--runtime/lua/vim/lsp/util.lua24
-rw-r--r--test/functional/plugin/lsp_spec.lua105
3 files changed, 127 insertions, 12 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 6ca7b52fff..88e8275b44 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1400,9 +1400,15 @@ show_line_diagnostics({opts}, {bufnr}, {line_nr}, {client_id})
Lua module: vim.lsp.util *lsp-util*
*vim.lsp.util.apply_text_document_edit()*
-apply_text_document_edit({text_document_edit})
+apply_text_document_edit({text_document_edit}, {index})
+ Applies a `TextDocumentEdit` , which is a list of changes to a
+ single document.
+
Parameters: ~
- {text_document_edit} (table) a `TextDocumentEdit` object
+ {text_document_edit} table: a `TextDocumentEdit` object
+ {index} number: Optional index of the edit,
+ if from a list of edits (or nil, if
+ not from a list)
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index ecff95f61e..e33e0109b6 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -254,19 +254,27 @@ function M.extract_completion_items(result)
end
--- Applies a `TextDocumentEdit`, which is a list of changes to a single
--- document.
+--- document.
---
---@param text_document_edit (table) a `TextDocumentEdit` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
-function M.apply_text_document_edit(text_document_edit)
+---@param text_document_edit table: a `TextDocumentEdit` object
+---@param index number: Optional index of the edit, if from a list of edits (or nil, if not from a list)
+---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
+function M.apply_text_document_edit(text_document_edit, index)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
+ -- For lists of text document edits,
+ -- do not check the version after the first edit.
+ local should_check_version = true
+ if index and index > 1 then
+ should_check_version = false
+ end
+
-- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
- if text_document.version
+ if should_check_version and (text_document.version
and M.buf_versions[bufnr]
- and M.buf_versions[bufnr] > text_document.version then
+ and M.buf_versions[bufnr] > text_document.version) then
print("Buffer ", text_document.uri, " newer than edits.")
return
end
@@ -459,12 +467,12 @@ end
-- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
function M.apply_workspace_edit(workspace_edit)
if workspace_edit.documentChanges then
- for _, change in ipairs(workspace_edit.documentChanges) do
+ for idx, change in ipairs(workspace_edit.documentChanges) do
if change.kind then
-- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile
error(string.format("Unsupported change: %q", vim.inspect(change)))
else
- M.apply_text_document_edit(change)
+ M.apply_text_document_edit(change, idx)
end
end
return
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index ec06cb0639..e8caa05260 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1045,7 +1045,7 @@ describe('LSP', function()
return {
edits = {
make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
- },
+ },
textDocument = {
uri = "file://fake/uri";
version = editVersion
@@ -1086,7 +1086,7 @@ describe('LSP', function()
local args = {...}
local versionedBuf = args[2]
vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
- vim.lsp.util.apply_text_document_edit(...)
+ vim.lsp.util.apply_text_document_edit(args[1])
]], edit, versionedBuf)
end
@@ -1109,6 +1109,7 @@ describe('LSP', function()
}, buf_lines(target_bufnr))
end)
end)
+
describe('workspace_apply_edit', function()
it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function()
local expected = {
@@ -1124,6 +1125,106 @@ describe('LSP', function()
]])
end)
end)
+
+ describe('apply_workspace_edit', function()
+ local replace_line_edit = function(row, new_line, editVersion)
+ return {
+ edits = {
+ -- NOTE: This is a hack if you have a line longer than 1000 it won't replace it
+ make_edit(row, 0, row, 1000, new_line)
+ },
+ textDocument = {
+ uri = "file://fake/uri";
+ version = editVersion
+ }
+ }
+ end
+
+ -- Some servers send all the edits separately, but with the same version.
+ -- We should not stop applying the edits
+ local make_workspace_edit = function(changes)
+ return {
+ documentChanges = changes
+ }
+ end
+
+ local target_bufnr, changedtick = nil, nil
+
+ before_each(function()
+ local ret = exec_lua [[
+ local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ local lines = {
+ "Original Line #1",
+ "Original Line #2"
+ }
+
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
+
+ local update_changed_tick = function()
+ vim.lsp.util.buf_versions[bufnr] = vim.api.nvim_buf_get_var(bufnr, 'changedtick')
+ end
+
+ update_changed_tick()
+ vim.api.nvim_buf_attach(bufnr, false, {
+ on_changedtick = function()
+ update_changed_tick()
+ end
+ })
+
+ return {bufnr, vim.api.nvim_buf_get_var(bufnr, 'changedtick')}
+ ]]
+
+ target_bufnr = ret[1]
+ changedtick = ret[2]
+ end)
+
+ it('apply_workspace_edit applies a single edit', function()
+ local new_lines = {
+ "First Line",
+ }
+
+ local edits = {}
+ for row, line in ipairs(new_lines) do
+ table.insert(edits, replace_line_edit(row - 1, line, changedtick))
+ end
+
+ eq({
+ "First Line",
+ "Original Line #2",
+ }, exec_lua([[
+ local args = {...}
+ local workspace_edits = args[1]
+ local target_bufnr = args[2]
+
+ vim.lsp.util.apply_workspace_edit(workspace_edits)
+
+ return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
+ ]], make_workspace_edit(edits), target_bufnr))
+ end)
+
+ it('apply_workspace_edit applies multiple edits', function()
+ local new_lines = {
+ "First Line",
+ "Second Line",
+ }
+
+ local edits = {}
+ for row, line in ipairs(new_lines) do
+ table.insert(edits, replace_line_edit(row - 1, line, changedtick))
+ end
+
+ eq(new_lines, exec_lua([[
+ local args = {...}
+ local workspace_edits = args[1]
+ local target_bufnr = args[2]
+
+ vim.lsp.util.apply_workspace_edit(workspace_edits)
+
+ return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
+ ]], make_workspace_edit(edits), target_bufnr))
+ end)
+ end)
+
describe('completion_list_to_complete_items', function()
-- Completion option precedence:
-- textEdit.newText > insertText > label