diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 5b4ff79a53..cf6dc28494 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -282,9 +282,21 @@ end --- @param result vim.lsp.CompletionResult Result of `textDocument/completion` --- @param prefix string prefix to filter the completion items --- @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[] --- @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) if vim.tbl_isempty(items) then return {} @@ -320,6 +332,25 @@ function M._lsp_to_complete_items(result, prefix, client_id) local match, score = matches(item) if match then 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 = '' if item.deprecated @@ -393,16 +424,16 @@ local function adjust_start_col(lnum, line, items, encoding) local min_start_char = nil for _, item in pairs(items) do if item.textEdit then + local start_char = nil 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 - return nil - end - min_start_char = item.textEdit.range.start.character + start_char = item.textEdit.range.start.character 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 - return nil + start_char = item.textEdit.insert.start.character + end + if start_char then + if not min_start_char or start_char < min_start_char then + min_start_char = start_char end - min_start_char = item.textEdit.insert.start.character end end end @@ -456,7 +487,9 @@ function M._convert_results( server_start_boundary = client_start_boundary end 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 end diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index 1c4ff5fd04..41d54566ba 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -1394,6 +1394,39 @@ describe('vim.lsp.completion: integration', function() 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 = '
$0
', + 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') + retry(nil, nil, function() + eq( + 1, + exec_lua(function() + return vim.fn.pumvisible() + end) + ) + end) + feed('') + eq('
', 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() local completion_list = { isIncomplete = false,