fix(lsp): handle locations exceeding line length #30253

Problem:
LSP spec [states](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position)
that "if the character value is greater than the line length it defaults
back to the line length", but `locations_to_items` fails in that case.

Solution:
Adjust locations_to_items to follow the spec.

closes #28281
This commit is contained in:
Tristan Knight 2024-09-05 08:23:11 +01:00 committed by GitHub
parent 220b8aa6fe
commit 882a450a29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 19 deletions

View File

@ -1795,8 +1795,18 @@ function M.locations_to_items(locations, offset_encoding)
local row = pos.line
local end_row = end_pos.line
local line = lines[row] or ''
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
local end_col = M._str_byteindex_enc(lines[end_row] or '', end_pos.character, offset_encoding)
local line_len = vim.fn.strcharlen(line)
local end_line = lines[end_row] or ''
local end_line_len = vim.fn.strcharlen(end_line)
-- LSP spec: if character > line length, default to the line length.
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
local col = pos.character <= line_len
and M._str_byteindex_enc(line, pos.character, offset_encoding)
or line_len
local end_col = end_pos.character <= end_line_len
and M._str_byteindex_enc(end_line, end_pos.character, offset_encoding)
or end_line_len
table.insert(items, {
filename = filename,
lnum = row + 1,

View File

@ -2673,7 +2673,7 @@ describe('LSP', function()
describe('lsp.util.locations_to_items', function()
it('Convert Location[] to items', function()
local expected = {
local expected_template = {
{
filename = '/fake/uri',
lnum = 1,
@ -2681,20 +2681,11 @@ describe('LSP', function()
col = 3,
end_col = 4,
text = 'testing',
user_data = {
uri = 'file:///fake/uri',
range = {
start = { line = 0, character = 2 },
['end'] = { line = 1, character = 3 },
},
},
user_data = {},
},
}
local actual = exec_lua(function()
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
local lines = { 'testing', '123' }
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
local locations = {
local test_params = {
{
{
uri = 'file:///fake/uri',
range = {
@ -2702,10 +2693,29 @@ describe('LSP', function()
['end'] = { line = 1, character = 3 },
},
},
}
return vim.lsp.util.locations_to_items(locations, 'utf-16')
end)
eq(expected, actual)
},
{
{
uri = 'file:///fake/uri',
range = {
start = { line = 0, character = 2 },
-- LSP spec: if character > line length, default to the line length.
['end'] = { line = 1, character = 10000 },
},
},
},
}
for _, params in ipairs(test_params) do
local actual = exec_lua(function(params0)
local bufnr = vim.uri_to_bufnr('file:///fake/uri')
local lines = { 'testing', '123' }
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
return vim.lsp.util.locations_to_items(params0, 'utf-16')
end, params)
local expected = vim.deepcopy(expected_template)
expected[1].user_data = params[1]
eq(expected, actual)
end
end)
it('Convert LocationLink[] to items', function()