mirror of
https://github.com/neovim/neovim.git
synced 2026-04-20 14:25:32 +00:00
fix(lsp): use LSP textEdit range for completion start boundary (#37491)
Previously, adjust_start_col returned nil when completion items had different start position from lsp textEdit range This caused the completion to fall back to \k*$ which ignores the non-keyword characters Changes: - adjust_start_col: now returns the minimum start postion among all items instead of nil - _lsp_to_complete_items - normalizes the items by adding the gap between current and minimum start Fixes: https://github.com/neovim/neovim/issues/37441
This commit is contained in:
@@ -282,9 +282,21 @@ end
|
|||||||
--- @param result vim.lsp.CompletionResult Result of `textDocument/completion`
|
--- @param result vim.lsp.CompletionResult Result of `textDocument/completion`
|
||||||
--- @param prefix string prefix to filter the completion items
|
--- @param prefix string prefix to filter the completion items
|
||||||
--- @param client_id integer? Client ID
|
--- @param client_id integer? Client ID
|
||||||
|
--- @param server_start_boundary integer? server start boundary
|
||||||
|
--- @param line string? current line content
|
||||||
|
--- @param lnum integer? 0-indexed line number
|
||||||
|
--- @param encoding string? encoding
|
||||||
--- @return table[]
|
--- @return table[]
|
||||||
--- @see complete-items
|
--- @see complete-items
|
||||||
function M._lsp_to_complete_items(result, prefix, client_id)
|
function M._lsp_to_complete_items(
|
||||||
|
result,
|
||||||
|
prefix,
|
||||||
|
client_id,
|
||||||
|
server_start_boundary,
|
||||||
|
line,
|
||||||
|
lnum,
|
||||||
|
encoding
|
||||||
|
)
|
||||||
local items = get_items(result)
|
local items = get_items(result)
|
||||||
if vim.tbl_isempty(items) then
|
if vim.tbl_isempty(items) then
|
||||||
return {}
|
return {}
|
||||||
@@ -320,6 +332,25 @@ function M._lsp_to_complete_items(result, prefix, client_id)
|
|||||||
local match, score = matches(item)
|
local match, score = matches(item)
|
||||||
if match then
|
if match then
|
||||||
local word = get_completion_word(item, prefix, match_item_by_value)
|
local word = get_completion_word(item, prefix, match_item_by_value)
|
||||||
|
|
||||||
|
if server_start_boundary and line and lnum and encoding and item.textEdit then
|
||||||
|
--- @type integer?
|
||||||
|
local item_start_char
|
||||||
|
if item.textEdit.range and item.textEdit.range.start.line == lnum then
|
||||||
|
item_start_char = item.textEdit.range.start.character
|
||||||
|
elseif item.textEdit.insert and item.textEdit.insert.start.line == lnum then
|
||||||
|
item_start_char = item.textEdit.insert.start.character
|
||||||
|
end
|
||||||
|
|
||||||
|
if item_start_char then
|
||||||
|
local item_start_byte = vim.str_byteindex(line, encoding, item_start_char, false)
|
||||||
|
if item_start_byte > server_start_boundary then
|
||||||
|
local missing_prefix = line:sub(server_start_boundary + 1, item_start_byte)
|
||||||
|
word = missing_prefix .. word
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local hl_group = ''
|
local hl_group = ''
|
||||||
if
|
if
|
||||||
item.deprecated
|
item.deprecated
|
||||||
@@ -393,16 +424,16 @@ local function adjust_start_col(lnum, line, items, encoding)
|
|||||||
local min_start_char = nil
|
local min_start_char = nil
|
||||||
for _, item in pairs(items) do
|
for _, item in pairs(items) do
|
||||||
if item.textEdit then
|
if item.textEdit then
|
||||||
|
local start_char = nil
|
||||||
if item.textEdit.range and item.textEdit.range.start.line == lnum then
|
if item.textEdit.range and item.textEdit.range.start.line == lnum then
|
||||||
if min_start_char and min_start_char ~= item.textEdit.range.start.character then
|
start_char = item.textEdit.range.start.character
|
||||||
return nil
|
|
||||||
end
|
|
||||||
min_start_char = item.textEdit.range.start.character
|
|
||||||
elseif item.textEdit.insert and item.textEdit.insert.start.line == lnum then
|
elseif item.textEdit.insert and item.textEdit.insert.start.line == lnum then
|
||||||
if min_start_char and min_start_char ~= item.textEdit.insert.start.character then
|
start_char = item.textEdit.insert.start.character
|
||||||
return nil
|
end
|
||||||
|
if start_char then
|
||||||
|
if not min_start_char or start_char < min_start_char then
|
||||||
|
min_start_char = start_char
|
||||||
end
|
end
|
||||||
min_start_char = item.textEdit.insert.start.character
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -456,7 +487,9 @@ function M._convert_results(
|
|||||||
server_start_boundary = client_start_boundary
|
server_start_boundary = client_start_boundary
|
||||||
end
|
end
|
||||||
local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col)
|
local prefix = line:sub((server_start_boundary or client_start_boundary) + 1, cursor_col)
|
||||||
local matches = M._lsp_to_complete_items(result, prefix, client_id)
|
local matches =
|
||||||
|
M._lsp_to_complete_items(result, prefix, client_id, server_start_boundary, line, lnum, encoding)
|
||||||
|
|
||||||
return matches, server_start_boundary
|
return matches, server_start_boundary
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1394,6 +1394,39 @@ describe('vim.lsp.completion: integration', function()
|
|||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('prepends prefix for items with different start positions', function()
|
||||||
|
local completion_list = {
|
||||||
|
isIncomplete = false,
|
||||||
|
items = {
|
||||||
|
{
|
||||||
|
label = 'div.foo',
|
||||||
|
insertTextFormat = 2,
|
||||||
|
textEdit = {
|
||||||
|
newText = '<div class="foo">$0</div>',
|
||||||
|
range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 7 } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exec_lua(function()
|
||||||
|
vim.o.completeopt = 'menu,menuone,noinsert'
|
||||||
|
end)
|
||||||
|
create_server('dummy', completion_list)
|
||||||
|
feed('Adiv.foo<C-x><C-O>')
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.fn.pumvisible()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
feed('<C-Y>')
|
||||||
|
eq('<div class="foo"></div>', n.api.nvim_get_current_line())
|
||||||
|
eq({ 1, 17 }, n.api.nvim_win_get_cursor(0))
|
||||||
|
end)
|
||||||
|
|
||||||
it('sorts items when fuzzy is enabled and prefix not empty #33610', function()
|
it('sorts items when fuzzy is enabled and prefix not empty #33610', function()
|
||||||
local completion_list = {
|
local completion_list = {
|
||||||
isIncomplete = false,
|
isIncomplete = false,
|
||||||
|
|||||||
Reference in New Issue
Block a user