mirror of
https://github.com/neovim/neovim
synced 2025-07-16 09:11:51 +00:00
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:
@ -1795,8 +1795,18 @@ function M.locations_to_items(locations, offset_encoding)
|
|||||||
local row = pos.line
|
local row = pos.line
|
||||||
local end_row = end_pos.line
|
local end_row = end_pos.line
|
||||||
local line = lines[row] or ''
|
local line = lines[row] or ''
|
||||||
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
|
local line_len = vim.fn.strcharlen(line)
|
||||||
local end_col = M._str_byteindex_enc(lines[end_row] or '', end_pos.character, offset_encoding)
|
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, {
|
table.insert(items, {
|
||||||
filename = filename,
|
filename = filename,
|
||||||
lnum = row + 1,
|
lnum = row + 1,
|
||||||
|
@ -2673,7 +2673,7 @@ describe('LSP', function()
|
|||||||
|
|
||||||
describe('lsp.util.locations_to_items', function()
|
describe('lsp.util.locations_to_items', function()
|
||||||
it('Convert Location[] to items', function()
|
it('Convert Location[] to items', function()
|
||||||
local expected = {
|
local expected_template = {
|
||||||
{
|
{
|
||||||
filename = '/fake/uri',
|
filename = '/fake/uri',
|
||||||
lnum = 1,
|
lnum = 1,
|
||||||
@ -2681,20 +2681,11 @@ describe('LSP', function()
|
|||||||
col = 3,
|
col = 3,
|
||||||
end_col = 4,
|
end_col = 4,
|
||||||
text = 'testing',
|
text = 'testing',
|
||||||
user_data = {
|
user_data = {},
|
||||||
uri = 'file:///fake/uri',
|
|
||||||
range = {
|
|
||||||
start = { line = 0, character = 2 },
|
|
||||||
['end'] = { line = 1, character = 3 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
local actual = exec_lua(function()
|
local test_params = {
|
||||||
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 = {
|
|
||||||
{
|
{
|
||||||
uri = 'file:///fake/uri',
|
uri = 'file:///fake/uri',
|
||||||
range = {
|
range = {
|
||||||
@ -2702,10 +2693,29 @@ describe('LSP', function()
|
|||||||
['end'] = { line = 1, character = 3 },
|
['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)
|
end)
|
||||||
|
|
||||||
it('Convert LocationLink[] to items', function()
|
it('Convert LocationLink[] to items', function()
|
||||||
|
Reference in New Issue
Block a user