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:
Jaehwang Jung
2024-07-31 23:18:24 +09:00
committed by Mathias Fußenegger
parent c40057f372
commit 7e5b7ae4e0
2 changed files with 53 additions and 4 deletions

View File

@@ -212,7 +212,8 @@ end
---@param lastline integer
---@param new_lastline integer
---@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(
prev_lines,
curr_lines,
@@ -222,6 +223,16 @@ local function compute_end_range(
new_lastline,
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.
-- In this case, the last_byte...
if firstline == new_lastline then