mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
fix(lsp): out of bounds error in lsp.util.apply_text_edits (#20137)
Co-authored-by: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com>
This commit is contained in:
parent
62db91f06c
commit
ec94014cd1
@ -459,21 +459,37 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
|
|||||||
text = split(text_edit.newText, '\n', true),
|
text = split(text_edit.newText, '\n', true),
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
|
|
||||||
local max = api.nvim_buf_line_count(bufnr)
|
local max = api.nvim_buf_line_count(bufnr)
|
||||||
if max <= e.start_row or max <= e.end_row then
|
-- If the whole edit is after the lines in the buffer we can simply add the new text to the end
|
||||||
local len = #(get_line(bufnr, max - 1) or '')
|
-- of the buffer.
|
||||||
if max <= e.start_row then
|
if max <= e.start_row then
|
||||||
e.start_row = max - 1
|
api.nvim_buf_set_lines(bufnr, max, max, false, e.text)
|
||||||
e.start_col = len
|
else
|
||||||
table.insert(e.text, 1, '')
|
local last_line_len = #(get_line(bufnr, math.min(e.end_row, max - 1)) or '')
|
||||||
end
|
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't
|
||||||
|
-- accept it so we should fix it here.
|
||||||
if max <= e.end_row then
|
if max <= e.end_row then
|
||||||
e.end_row = max - 1
|
e.end_row = max - 1
|
||||||
e.end_col = len
|
e.end_col = last_line_len
|
||||||
end
|
|
||||||
has_eol_text_edit = true
|
has_eol_text_edit = true
|
||||||
|
else
|
||||||
|
-- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the
|
||||||
|
-- replacement text ends with a newline We can likely assume that the replacement is assumed
|
||||||
|
-- to be meant to replace the newline with another newline and we need to make sure this
|
||||||
|
-- doens't add an extra empty line. E.g. when the last line to be replaced contains a '\r'
|
||||||
|
-- in the file some servers (clangd on windows) will include that character in the line
|
||||||
|
-- while nvim_buf_set_text doesn't count it as part of the line.
|
||||||
|
if
|
||||||
|
e.end_col > last_line_len
|
||||||
|
and #text_edit.newText > 0
|
||||||
|
and string.sub(text_edit.newText, -1) == '\n'
|
||||||
|
then
|
||||||
|
table.remove(e.text, #e.text)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
-- Make sure we don't go out of bounds for e.end_col
|
||||||
|
e.end_col = math.min(last_line_len, e.end_col)
|
||||||
|
|
||||||
api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
|
api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
|
||||||
|
|
||||||
-- Fix cursor position.
|
-- Fix cursor position.
|
||||||
@ -490,6 +506,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
|
|||||||
is_cursor_fixed = true
|
is_cursor_fixed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local max = api.nvim_buf_line_count(bufnr)
|
local max = api.nvim_buf_line_count(bufnr)
|
||||||
|
|
||||||
|
@ -1719,6 +1719,46 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('apply_text_edits regression tests for #20116', function()
|
||||||
|
before_each(function()
|
||||||
|
insert(dedent([[
|
||||||
|
Test line one
|
||||||
|
Test line two 21 char]]))
|
||||||
|
end)
|
||||||
|
describe('with LSP end column out of bounds and start column at 0', function()
|
||||||
|
it('applies edits at the end of the buffer', function()
|
||||||
|
local edits = {
|
||||||
|
make_edit(0, 0, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
|
||||||
|
}
|
||||||
|
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
|
||||||
|
eq({'#include "whatever.h"', '#include <algorithm>'}, buf_lines(1))
|
||||||
|
end)
|
||||||
|
it('applies edits in the middle of the buffer', function()
|
||||||
|
local edits = {
|
||||||
|
make_edit(0, 0, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
|
||||||
|
}
|
||||||
|
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
|
||||||
|
eq({'#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('with LSP end column out of bounds and start column NOT at 0', function()
|
||||||
|
it('applies edits at the end of the buffer', function()
|
||||||
|
local edits = {
|
||||||
|
make_edit(0, 2, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
|
||||||
|
}
|
||||||
|
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
|
||||||
|
eq({'Te#include "whatever.h"', '#include <algorithm>'}, buf_lines(1))
|
||||||
|
end)
|
||||||
|
it('applies edits in the middle of the buffer', function()
|
||||||
|
local edits = {
|
||||||
|
make_edit(0, 2, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'});
|
||||||
|
}
|
||||||
|
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8")
|
||||||
|
eq({'Te#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe('apply_text_document_edit', function()
|
describe('apply_text_document_edit', function()
|
||||||
local target_bufnr
|
local target_bufnr
|
||||||
local text_document_edit = function(editVersion)
|
local text_document_edit = function(editVersion)
|
||||||
|
Loading…
Reference in New Issue
Block a user