mirror of
https://github.com/neovim/neovim.git
synced 2025-11-21 17:46:30 +00:00
fix(lsp): send didOpen if name changes on write (#19583)
`:saveas newName` changes the name of an existing buffer. Due to the buffer re-use it skips the lsp attach phase and immediately sends a `didSave` notification to the server. Servers get confused about this, because they expect a `didOpen` notification first. Closes https://github.com/neovim/neovim/issues/18688
This commit is contained in:
committed by
GitHub
parent
711ef4eac9
commit
e99de3f12f
@@ -371,7 +371,9 @@ do
|
|||||||
state_by_client[client.id] = state
|
state_by_client[client.id] = state
|
||||||
end
|
end
|
||||||
if not state.buffers[bufnr] then
|
if not state.buffers[bufnr] then
|
||||||
local buf_state = {}
|
local buf_state = {
|
||||||
|
name = api.nvim_buf_get_name(bufnr),
|
||||||
|
}
|
||||||
state.buffers[bufnr] = buf_state
|
state.buffers[bufnr] = buf_state
|
||||||
if use_incremental_sync then
|
if use_incremental_sync then
|
||||||
buf_state.lines = nvim_buf_get_lines(bufnr, 0, -1, true)
|
buf_state.lines = nvim_buf_get_lines(bufnr, 0, -1, true)
|
||||||
@@ -381,6 +383,15 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
function changetracking._get_and_set_name(client, bufnr, name)
|
||||||
|
local state = state_by_client[client.id] or {}
|
||||||
|
local buf_state = (state.buffers or {})[bufnr]
|
||||||
|
local old_name = buf_state.name
|
||||||
|
buf_state.name = name
|
||||||
|
return old_name
|
||||||
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
function changetracking.reset_buf(client, bufnr)
|
function changetracking.reset_buf(client, bufnr)
|
||||||
changetracking.flush(client, bufnr)
|
changetracking.flush(client, bufnr)
|
||||||
@@ -1405,6 +1416,19 @@ local function text_document_did_save_handler(bufnr)
|
|||||||
local uri = vim.uri_from_bufnr(bufnr)
|
local uri = vim.uri_from_bufnr(bufnr)
|
||||||
local text = once(buf_get_full_text)
|
local text = once(buf_get_full_text)
|
||||||
for_each_buffer_client(bufnr, function(client)
|
for_each_buffer_client(bufnr, function(client)
|
||||||
|
local name = api.nvim_buf_get_name(bufnr)
|
||||||
|
local old_name = changetracking._get_and_set_name(client, bufnr, name)
|
||||||
|
if old_name and name ~= old_name then
|
||||||
|
client.notify('textDocument/didOpen', {
|
||||||
|
textDocument = {
|
||||||
|
version = 0,
|
||||||
|
uri = uri,
|
||||||
|
languageId = client.config.get_language_id(bufnr, vim.bo[bufnr].filetype),
|
||||||
|
text = buf_get_full_text(bufnr),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
util.buf_versions[bufnr] = 0
|
||||||
|
end
|
||||||
local save_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'save')
|
local save_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'save')
|
||||||
if save_capability then
|
if save_capability then
|
||||||
local included_text
|
local included_text
|
||||||
|
|||||||
@@ -67,10 +67,12 @@ local function expect_notification(method, params, ...)
|
|||||||
local message = read_message()
|
local message = read_message()
|
||||||
assert_eq(method, message.method,
|
assert_eq(method, message.method,
|
||||||
..., "expect_notification", "method")
|
..., "expect_notification", "method")
|
||||||
assert_eq(params, message.params,
|
if params then
|
||||||
..., "expect_notification", method, "params")
|
assert_eq(params, message.params,
|
||||||
assert_eq({jsonrpc = "2.0"; method=method, params=params}, message,
|
..., "expect_notification", method, "params")
|
||||||
..., "expect_notification", "message")
|
assert_eq({jsonrpc = "2.0"; method=method, params=params}, message,
|
||||||
|
..., "expect_notification", "message")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function expect_request(method, handler, ...)
|
local function expect_request(method, handler, ...)
|
||||||
@@ -257,6 +259,26 @@ function tests.basic_check_capabilities()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function tests.text_document_save_did_open()
|
||||||
|
skeleton {
|
||||||
|
on_init = function()
|
||||||
|
return {
|
||||||
|
capabilities = {
|
||||||
|
textDocumentSync = {
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end;
|
||||||
|
body = function()
|
||||||
|
notify('start')
|
||||||
|
expect_notification('textDocument/didOpen')
|
||||||
|
expect_notification('textDocument/didSave')
|
||||||
|
notify('shutdown')
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
function tests.text_document_sync_save_bool()
|
function tests.text_document_sync_save_bool()
|
||||||
skeleton {
|
skeleton {
|
||||||
on_init = function()
|
on_init = function()
|
||||||
|
|||||||
@@ -535,6 +535,46 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('saveas sends didOpen if filename changed', function()
|
||||||
|
local expected_handlers = {
|
||||||
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
|
{ NIL, {}, { method = 'start', client_id = 1 } },
|
||||||
|
}
|
||||||
|
local client
|
||||||
|
test_rpc_server({
|
||||||
|
test_name = 'text_document_save_did_open',
|
||||||
|
on_init = function(c)
|
||||||
|
client = c
|
||||||
|
end,
|
||||||
|
on_exit = function(code, signal)
|
||||||
|
eq(0, code, 'exit code')
|
||||||
|
eq(0, signal, 'exit signal')
|
||||||
|
end,
|
||||||
|
on_handler = function(err, result, ctx)
|
||||||
|
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
|
||||||
|
if ctx.method == 'start' then
|
||||||
|
local tmpfile_old = helpers.tmpname()
|
||||||
|
local tmpfile_new = helpers.tmpname()
|
||||||
|
os.remove(tmpfile_new)
|
||||||
|
exec_lua(
|
||||||
|
[=[
|
||||||
|
local oldname, newname = ...
|
||||||
|
BUFFER = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_buf_set_name(BUFFER, oldname)
|
||||||
|
vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"})
|
||||||
|
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
|
||||||
|
vim.api.nvim_buf_call(BUFFER, function() vim.cmd('saveas ' .. newname) end)
|
||||||
|
]=],
|
||||||
|
tmpfile_old,
|
||||||
|
tmpfile_new
|
||||||
|
)
|
||||||
|
else
|
||||||
|
client.stop()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
it('BufWritePost sends didSave including text if server capability is set', function()
|
it('BufWritePost sends didSave including text if server capability is set', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{NIL, {}, {method="shutdown", client_id=1}};
|
{NIL, {}, {method="shutdown", client_id=1}};
|
||||||
|
|||||||
Reference in New Issue
Block a user