fix(lsp): revert buf_versions deprecation/replacement (#29217)

* Revert "fix(lsp): account for changedtick version gap on modified reset (#29170)"

This reverts commit 2e6d295f79.

* Revert "refactor(lsp): replace util.buf_versions with changedtick (#28943)"

This reverts commit 5c33815448.
This commit is contained in:
Mathias Fußenegger
2024-06-07 11:36:46 +02:00
committed by GitHub
parent 6c7677e5d2
commit 6e45cd7f00
10 changed files with 69 additions and 67 deletions

View File

@ -22,9 +22,6 @@ API
LUA LUA
- vim.region() Use |getregionpos()| instead. - vim.region() Use |getregionpos()| instead.
LSP
- *vim.lsp.util.buf_versions* Use |b:changedtick| instead.
DIAGNOSTICS DIAGNOSTICS
- *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count = 1}` instead. - *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count = 1}` instead.
- *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count = -1}` instead. - *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count = -1}` instead.

View File

@ -484,6 +484,7 @@ local function text_document_did_save_handler(bufnr)
text = lsp._buf_get_full_text(bufnr), text = lsp._buf_get_full_text(bufnr),
}, },
}) })
util.buf_versions[bufnr] = 0
end 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
@ -519,6 +520,7 @@ local function buf_detach_client(bufnr, client)
end end
client.attached_buffers[bufnr] = nil client.attached_buffers[bufnr] = nil
util.buf_versions[bufnr] = nil
local namespace = lsp.diagnostic.get_namespace(client.id) local namespace = lsp.diagnostic.get_namespace(client.id)
vim.diagnostic.reset(namespace, bufnr) vim.diagnostic.reset(namespace, bufnr)
@ -574,11 +576,12 @@ local function buf_attach(bufnr)
}) })
-- First time, so attach and set up stuff. -- First time, so attach and set up stuff.
api.nvim_buf_attach(bufnr, false, { api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, _, _, firstline, lastline, new_lastline) on_lines = function(_, _, changedtick, firstline, lastline, new_lastline)
if #lsp.get_clients({ bufnr = bufnr }) == 0 then if #lsp.get_clients({ bufnr = bufnr }) == 0 then
-- detach if there are no clients -- detach if there are no clients
return #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 return #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0
end end
util.buf_versions[bufnr] = changedtick
changetracking.send_changes(bufnr, firstline, lastline, new_lastline) changetracking.send_changes(bufnr, firstline, lastline, new_lastline)
end, end,

View File

@ -1,5 +1,6 @@
local protocol = require('vim.lsp.protocol') local protocol = require('vim.lsp.protocol')
local sync = require('vim.lsp.sync') local sync = require('vim.lsp.sync')
local util = require('vim.lsp.util')
local api = vim.api local api = vim.api
local uv = vim.uv local uv = vim.uv
@ -276,7 +277,7 @@ local function send_changes(bufnr, sync_kind, state, buf_state)
client.notify(protocol.Methods.textDocument_didChange, { client.notify(protocol.Methods.textDocument_didChange, {
textDocument = { textDocument = {
uri = uri, uri = uri,
version = vim.b[bufnr].changedtick, version = util.buf_versions[bufnr],
}, },
contentChanges = changes, contentChanges = changes,
}) })

View File

@ -673,8 +673,8 @@ function Client:_request(method, params, handler, bufnr)
end end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
changetracking.flush(self, bufnr) changetracking.flush(self, bufnr)
local version = lsp.util.buf_versions[bufnr]
bufnr = resolve_bufnr(bufnr) bufnr = resolve_bufnr(bufnr)
local version = vim.b[bufnr].changedtick
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr) log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
local success, request_id = self.rpc.request(method, params, function(err, result) local success, request_id = self.rpc.request(method, params, function(err, result)
local context = { local context = {
@ -922,13 +922,14 @@ function Client:_text_document_did_open_handler(bufnr)
local params = { local params = {
textDocument = { textDocument = {
version = vim.b[bufnr].changedtick, version = 0,
uri = vim.uri_from_bufnr(bufnr), uri = vim.uri_from_bufnr(bufnr),
languageId = self.get_language_id(bufnr, filetype), languageId = self.get_language_id(bufnr, filetype),
text = lsp._buf_get_full_text(bufnr), text = lsp._buf_get_full_text(bufnr),
}, },
} }
self.notify(ms.textDocument_didOpen, params) self.notify(ms.textDocument_didOpen, params)
lsp.util.buf_versions[bufnr] = params.textDocument.version
-- Next chance we get, we should re-do the diagnostics -- Next chance we get, we should re-do the diagnostics
vim.schedule(function() vim.schedule(function()

View File

@ -43,7 +43,7 @@ function M.on_inlayhint(err, result, ctx, _)
return return
end end
local bufnr = assert(ctx.bufnr) local bufnr = assert(ctx.bufnr)
if vim.b[bufnr].changedtick ~= ctx.version then if util.buf_versions[bufnr] ~= ctx.version then
return return
end end
local client_id = ctx.client_id local client_id = ctx.client_id
@ -324,7 +324,7 @@ api.nvim_set_decoration_provider(namespace, {
return return
end end
if bufstate.version ~= vim.b[bufnr].changedtick then if bufstate.version ~= util.buf_versions[bufnr] then
return return
end end

View File

@ -116,7 +116,7 @@ local function tokens_to_ranges(data, bufnr, client, request)
if elapsed_ns > yield_interval_ns then if elapsed_ns > yield_interval_ns then
vim.schedule(function() vim.schedule(function()
coroutine.resume(co, vim.b[bufnr].changedtick) coroutine.resume(co, util.buf_versions[bufnr])
end) end)
if request.version ~= coroutine.yield() then if request.version ~= coroutine.yield() then
-- request became stale since the last time the coroutine ran. -- request became stale since the last time the coroutine ran.
@ -269,7 +269,7 @@ end
--- ---
---@package ---@package
function STHighlighter:send_request() function STHighlighter:send_request()
local version = vim.b[self.bufnr].changedtick local version = util.buf_versions[self.bufnr]
self:reset_timer() self:reset_timer()
@ -412,7 +412,7 @@ end
function STHighlighter:on_win(topline, botline) function STHighlighter:on_win(topline, botline)
for client_id, state in pairs(self.client_state) do for client_id, state in pairs(self.client_state) do
local current_result = state.current_result local current_result = state.current_result
if current_result.version and current_result.version == vim.b[self.bufnr].changedtick then if current_result.version and current_result.version == util.buf_versions[self.bufnr] then
if not current_result.namespace_cleared then if not current_result.namespace_cleared then
api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1)
current_result.namespace_cleared = true current_result.namespace_cleared = true

View File

@ -502,11 +502,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
should_check_version = false should_check_version = false
end end
-- changedtick increases on save but server only receives version updates
-- on line changes (via didChange)
-- This allows a gap of 1 to account for the servers outdated view
local version_offset = vim.b[bufnr].modified and 0 or 1
-- `VersionedTextDocumentIdentifier`s version may be null -- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
if if
@ -514,7 +509,8 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
and ( and (
text_document.version text_document.version
and text_document.version > 0 and text_document.version > 0
and vim.b[bufnr].changedtick > (text_document.version + version_offset) and M.buf_versions[bufnr]
and M.buf_versions[bufnr] > text_document.version
) )
then then
print('Buffer ', text_document.uri, ' newer than edits.') print('Buffer ', text_document.uri, ' newer than edits.')
@ -2206,16 +2202,9 @@ function M._refresh(method, opts)
end end
end end
---@nodoc
---@deprecated
---@type table<integer,integer>
M.buf_versions = setmetatable({}, {
__index = function(_, bufnr)
vim.deprecate('vim.lsp.util.buf_versions', 'vim.b.changedtick', '0.13')
return vim.b[bufnr].changedtick
end,
})
M._get_line_byte_from_position = get_line_byte_from_position M._get_line_byte_from_position = get_line_byte_from_position
---@nodoc
M.buf_versions = {} ---@type table<integer,integer>
return M return M

View File

@ -471,7 +471,7 @@ function tests.basic_check_buffer_open()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n') .. '\n', text = table.concat({ 'testing', '123' }, '\n') .. '\n',
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('finish') expect_notification('finish')
@ -498,7 +498,7 @@ function tests.basic_check_buffer_open_and_change()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n') .. '\n', text = table.concat({ 'testing', '123' }, '\n') .. '\n',
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {
@ -534,7 +534,7 @@ function tests.basic_check_buffer_open_and_change_noeol()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n'), text = table.concat({ 'testing', '123' }, '\n'),
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {
@ -569,7 +569,7 @@ function tests.basic_check_buffer_open_and_change_multi()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n') .. '\n', text = table.concat({ 'testing', '123' }, '\n') .. '\n',
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {
@ -614,7 +614,7 @@ function tests.basic_check_buffer_open_and_change_multi_and_close()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n') .. '\n', text = table.concat({ 'testing', '123' }, '\n') .. '\n',
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {
@ -672,7 +672,7 @@ function tests.basic_check_buffer_open_and_change_incremental()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n') .. '\n', text = table.concat({ 'testing', '123' }, '\n') .. '\n',
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {
@ -715,7 +715,7 @@ function tests.basic_check_buffer_open_and_change_incremental_editing()
languageId = '', languageId = '',
text = table.concat({ 'testing', '123' }, '\n'), text = table.concat({ 'testing', '123' }, '\n'),
uri = 'file://', uri = 'file://',
version = 2, version = 0,
}, },
}) })
expect_notification('textDocument/didChange', { expect_notification('textDocument/didChange', {

View File

@ -111,7 +111,6 @@ describe('semantic token highlighting', function()
end) end)
it('buffer is highlighted when attached', function() it('buffer is highlighted when attached', function()
insert(text)
exec_lua([[ exec_lua([[
bufnr = vim.api.nvim_get_current_buf() bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr) vim.api.nvim_win_set_buf(0, bufnr)
@ -119,6 +118,8 @@ describe('semantic token highlighting', function()
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
]]) ]])
insert(text)
screen:expect { screen:expect {
grid = [[ grid = [[
#include <iostream> | #include <iostream> |
@ -140,7 +141,6 @@ describe('semantic token highlighting', function()
end) end)
it('use LspTokenUpdate and highlight_token', function() it('use LspTokenUpdate and highlight_token', function()
insert(text)
exec_lua([[ exec_lua([[
vim.api.nvim_create_autocmd("LspTokenUpdate", { vim.api.nvim_create_autocmd("LspTokenUpdate", {
callback = function(args) callback = function(args)
@ -157,6 +157,8 @@ describe('semantic token highlighting', function()
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
]]) ]])
insert(text)
screen:expect { screen:expect {
grid = [[ grid = [[
#include <iostream> | #include <iostream> |
@ -178,17 +180,14 @@ describe('semantic token highlighting', function()
end) end)
it('buffer is unhighlighted when client is detached', function() it('buffer is unhighlighted when client is detached', function()
insert(text)
exec_lua([[ exec_lua([[
bufnr = vim.api.nvim_get_current_buf() bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr) vim.api.nvim_win_set_buf(0, bufnr)
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
vim.wait(1000, function()
return #server.messages > 1
end)
]]) ]])
insert(text)
exec_lua([[ exec_lua([[
vim.notify = function() end vim.notify = function() end
vim.lsp.buf_detach_client(bufnr, client_id) vim.lsp.buf_detach_client(bufnr, client_id)
@ -332,13 +331,14 @@ describe('semantic token highlighting', function()
end) end)
it('buffer is re-highlighted when force refreshed', function() it('buffer is re-highlighted when force refreshed', function()
insert(text)
exec_lua([[ exec_lua([[
bufnr = vim.api.nvim_get_current_buf() bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr) vim.api.nvim_win_set_buf(0, bufnr)
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
]]) ]])
insert(text)
screen:expect { screen:expect {
grid = [[ grid = [[
#include <iostream> | #include <iostream> |
@ -412,14 +412,13 @@ describe('semantic token highlighting', function()
end) end)
it('updates highlights with delta request on buffer change', function() it('updates highlights with delta request on buffer change', function()
insert(text)
exec_lua([[ exec_lua([[
bufnr = vim.api.nvim_get_current_buf() bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr) vim.api.nvim_win_set_buf(0, bufnr)
client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
]]) ]])
insert(text)
screen:expect { screen:expect {
grid = [[ grid = [[
#include <iostream> | #include <iostream> |
@ -598,7 +597,6 @@ describe('semantic token highlighting', function()
end) end)
it('does not send delta requests if not supported by server', function() it('does not send delta requests if not supported by server', function()
insert(text)
exec_lua( exec_lua(
[[ [[
local legend, response, edit_response = ... local legend, response, edit_response = ...
@ -627,6 +625,7 @@ describe('semantic token highlighting', function()
edit_response edit_response
) )
insert(text)
screen:expect { screen:expect {
grid = [[ grid = [[
#include <iostream> | #include <iostream> |
@ -1450,7 +1449,6 @@ int main()
}, },
}) do }) do
it(test.it, function() it(test.it, function()
insert(test.text1)
exec_lua(create_server_definition) exec_lua(create_server_definition)
exec_lua( exec_lua(
[[ [[
@ -1487,6 +1485,8 @@ int main()
test.response2 test.response2
) )
insert(test.text1)
test.expected_screen1() test.expected_screen1()
local highlights = exec_lua([[ local highlights = exec_lua([[

View File

@ -255,7 +255,7 @@ describe('LSP', function()
return return
end end
local expected_handlers = { local expected_handlers = {
{ NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1, version = 2 } }, { NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1 } },
{ NIL, {}, { method = 'test', client_id = 1 } }, { NIL, {}, { method = 'test', client_id = 1 } },
} }
test_rpc_server { test_rpc_server {
@ -948,11 +948,7 @@ describe('LSP', function()
it('should forward ContentModified to callback', function() it('should forward ContentModified to callback', function()
local expected_handlers = { local expected_handlers = {
{ NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } },
{ { { code = -32801 }, NIL, { method = 'error_code_test', bufnr = 1, client_id = 1 } },
{ code = -32801 },
NIL,
{ method = 'error_code_test', bufnr = 1, client_id = 1, version = 2 },
},
} }
local client --- @type vim.lsp.Client local client --- @type vim.lsp.Client
test_rpc_server { test_rpc_server {
@ -982,7 +978,7 @@ describe('LSP', function()
it('should track pending requests to the language server', function() it('should track pending requests to the language server', function()
local expected_handlers = { local expected_handlers = {
{ NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } },
{ NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 2 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } },
} }
local client --- @type vim.lsp.Client local client --- @type vim.lsp.Client
test_rpc_server { test_rpc_server {
@ -1049,7 +1045,7 @@ describe('LSP', function()
it('should clear pending and cancel requests on reply', function() it('should clear pending and cancel requests on reply', function()
local expected_handlers = { local expected_handlers = {
{ NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } },
{ NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 2 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } },
} }
local client --- @type vim.lsp.Client local client --- @type vim.lsp.Client
test_rpc_server { test_rpc_server {
@ -1088,7 +1084,7 @@ describe('LSP', function()
it('should trigger LspRequest autocmd when requests table changes', function() it('should trigger LspRequest autocmd when requests table changes', function()
local expected_handlers = { local expected_handlers = {
{ NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } },
{ NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 2 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } },
} }
local client --- @type vim.lsp.Client local client --- @type vim.lsp.Client
test_rpc_server { test_rpc_server {
@ -1368,7 +1364,6 @@ describe('LSP', function()
}, },
bufnr = 2, bufnr = 2,
client_id = 1, client_id = 1,
version = 2,
}, },
}, },
{ NIL, {}, { method = 'start', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } },
@ -2122,6 +2117,7 @@ describe('LSP', function()
local args = {...} local args = {...}
local bufnr = select(1, ...) local bufnr = select(1, ...)
local text_edit = select(2, ...) local text_edit = select(2, ...)
vim.lsp.util.buf_versions[bufnr] = 10
vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16') vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16')
]], ]],
target_bufnr, target_bufnr,
@ -2133,13 +2129,16 @@ describe('LSP', function()
}, buf_lines(target_bufnr)) }, buf_lines(target_bufnr))
end) end)
it('skips the edit if the version of the edit is behind the local buffer ', function() it('skips the edit if the version of the edit is behind the local buffer ', function()
local apply_edit_mocking_current_version = function(edit) local apply_edit_mocking_current_version = function(edit, versionedBuf)
exec_lua( exec_lua(
[[ [[
local args = {...} local args = {...}
local versionedBuf = args[2]
vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16') vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16')
]], ]],
edit edit,
versionedBuf
) )
end end
@ -2151,17 +2150,17 @@ describe('LSP', function()
eq(baseText, buf_lines(target_bufnr)) eq(baseText, buf_lines(target_bufnr))
-- Apply an edit for an old version, should skip -- Apply an edit for an old version, should skip
apply_edit_mocking_current_version(text_document_edit(1)) apply_edit_mocking_current_version(
text_document_edit(2),
{ currentVersion = 7, bufnr = target_bufnr }
)
eq(baseText, buf_lines(target_bufnr)) -- no change eq(baseText, buf_lines(target_bufnr)) -- no change
-- Sanity check that next version to current does apply change -- Sanity check that next version to current does apply change
apply_edit_mocking_current_version(text_document_edit(exec_lua( apply_edit_mocking_current_version(
[[ text_document_edit(8),
local bufnr = ... { currentVersion = 7, bufnr = target_bufnr }
return vim.b[bufnr].changedtick )
]],
target_bufnr
)))
eq({ eq({
'First ↥ 🤦 🦄 line of text', 'First ↥ 🤦 🦄 line of text',
'2nd line of 语text', '2nd line of 语text',
@ -2240,6 +2239,18 @@ describe('LSP', function()
} }
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) 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')} return {bufnr, vim.api.nvim_buf_get_var(bufnr, 'changedtick')}
]] ]]