mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
fix(lsp): prevent desync due to empty buffer (#29904)
Problem: Some language servers (e.g., rust-analyzer, texlab) are desynced when the user deletes the entire contents of the buffer. This is due to the discrepancy between how nvim computes diff and how nvim treats empty buffer. * diff: If the buffer became empty, then the diff includes the last line's eol. * empty buffer: Even if the buffer is empty, nvim regards it as having a single empty line with eol. Solution: Add special case for diff computation when the buffer becomes empty so that it does not include the eol of the last line.
This commit is contained in:
parent
4e90bc3023
commit
6bb40f3dbf
@ -212,7 +212,8 @@ end
|
|||||||
---@param lastline integer
|
---@param lastline integer
|
||||||
---@param new_lastline integer
|
---@param new_lastline integer
|
||||||
---@param offset_encoding string
|
---@param offset_encoding string
|
||||||
---@return vim.lsp.sync.Range, vim.lsp.sync.Range
|
---@return vim.lsp.sync.Range prev_end_range
|
||||||
|
---@return vim.lsp.sync.Range curr_end_range
|
||||||
local function compute_end_range(
|
local function compute_end_range(
|
||||||
prev_lines,
|
prev_lines,
|
||||||
curr_lines,
|
curr_lines,
|
||||||
@ -222,6 +223,16 @@ local function compute_end_range(
|
|||||||
new_lastline,
|
new_lastline,
|
||||||
offset_encoding
|
offset_encoding
|
||||||
)
|
)
|
||||||
|
-- A special case for the following `firstline == new_lastline` case where lines are deleted.
|
||||||
|
-- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol.
|
||||||
|
if #curr_lines == 1 and curr_lines[1] == '' then
|
||||||
|
local prev_line = prev_lines[lastline - 1]
|
||||||
|
return {
|
||||||
|
line_idx = lastline - 1,
|
||||||
|
byte_idx = #prev_line + 1,
|
||||||
|
char_idx = compute_line_length(prev_line, offset_encoding) + 1,
|
||||||
|
}, { line_idx = 1, byte_idx = 1, char_idx = 1 }
|
||||||
|
end
|
||||||
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
|
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
|
||||||
-- In this case, the last_byte...
|
-- In this case, the last_byte...
|
||||||
if firstline == new_lastline then
|
if firstline == new_lastline then
|
||||||
|
@ -170,7 +170,7 @@ describe('incremental synchronization', function()
|
|||||||
}
|
}
|
||||||
test_edit({ 'a' }, { 'rb' }, expected_text_changes, 'utf-16', '\n')
|
test_edit({ 'a' }, { 'rb' }, expected_text_changes, 'utf-16', '\n')
|
||||||
end)
|
end)
|
||||||
it('deleting a line', function()
|
it('deleting the first line', function()
|
||||||
local expected_text_changes = {
|
local expected_text_changes = {
|
||||||
{
|
{
|
||||||
range = {
|
range = {
|
||||||
@ -183,11 +183,49 @@ describe('incremental synchronization', function()
|
|||||||
line = 1,
|
line = 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rangeLength = 12,
|
rangeLength = 6,
|
||||||
text = '',
|
text = '',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
test_edit({ 'hello world' }, { 'dd' }, expected_text_changes, 'utf-16', '\n')
|
test_edit({ 'hello', 'world' }, { 'ggdd' }, expected_text_changes, 'utf-16', '\n')
|
||||||
|
end)
|
||||||
|
it('deleting the last line', function()
|
||||||
|
local expected_text_changes = {
|
||||||
|
{
|
||||||
|
range = {
|
||||||
|
['start'] = {
|
||||||
|
character = 0,
|
||||||
|
line = 1,
|
||||||
|
},
|
||||||
|
['end'] = {
|
||||||
|
character = 0,
|
||||||
|
line = 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rangeLength = 6,
|
||||||
|
text = '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test_edit({ 'hello', 'world' }, { '2ggdd' }, expected_text_changes, 'utf-16', '\n')
|
||||||
|
end)
|
||||||
|
it('deleting all lines', function()
|
||||||
|
local expected_text_changes = {
|
||||||
|
{
|
||||||
|
range = {
|
||||||
|
['start'] = {
|
||||||
|
character = 0,
|
||||||
|
line = 0,
|
||||||
|
},
|
||||||
|
['end'] = {
|
||||||
|
character = 5,
|
||||||
|
line = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rangeLength = 11,
|
||||||
|
text = '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
test_edit({ 'hello', 'world' }, { 'ggdG' }, expected_text_changes, 'utf-16', '\n')
|
||||||
end)
|
end)
|
||||||
it('deleting an empty line', function()
|
it('deleting an empty line', function()
|
||||||
local expected_text_changes = {
|
local expected_text_changes = {
|
||||||
|
Loading…
Reference in New Issue
Block a user