mirror of
https://github.com/neovim/neovim.git
synced 2026-04-01 05:12:02 +00:00
fix(lsp): sort items when completeopt include fuzzy #36974
Problem: When fuzzy is enabled and the prefix is not empty, items are not sorted by fuzzy score before calling fn.complete. Solution: Use matchfuzzypos to get the scores and sort the items by fuzzy score before calling fn.complete.
This commit is contained in:
@@ -260,18 +260,20 @@ end
|
|||||||
---@param value string
|
---@param value string
|
||||||
---@param prefix string
|
---@param prefix string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
|
---@return integer?
|
||||||
local function match_item_by_value(value, prefix)
|
local function match_item_by_value(value, prefix)
|
||||||
if prefix == '' then
|
if prefix == '' then
|
||||||
return true
|
return true, nil
|
||||||
end
|
end
|
||||||
if vim.o.completeopt:find('fuzzy') ~= nil then
|
if vim.o.completeopt:find('fuzzy') ~= nil then
|
||||||
return next(vim.fn.matchfuzzy({ value }, prefix)) ~= nil
|
local score = vim.fn.matchfuzzypos({ value }, prefix)[3] ---@type table
|
||||||
|
return #score > 0, score[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
if vim.o.ignorecase and (not vim.o.smartcase or not prefix:find('%u')) then
|
if vim.o.ignorecase and (not vim.o.smartcase or not prefix:find('%u')) then
|
||||||
return vim.startswith(value:lower(), prefix:lower())
|
return vim.startswith(value:lower(), prefix:lower()), nil
|
||||||
end
|
end
|
||||||
return vim.startswith(value, prefix)
|
return vim.startswith(value, prefix), nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Turns the result of a `textDocument/completion` request into vim-compatible
|
--- Turns the result of a `textDocument/completion` request into vim-compatible
|
||||||
@@ -315,7 +317,8 @@ function M._lsp_to_complete_items(result, prefix, client_id)
|
|||||||
local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert')
|
local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert')
|
||||||
local user_cmp = vim.tbl_get(buf_handles, bufnr, 'cmp')
|
local user_cmp = vim.tbl_get(buf_handles, bufnr, 'cmp')
|
||||||
for _, item in ipairs(items) do
|
for _, item in ipairs(items) do
|
||||||
if matches(item) then
|
local match, score = matches(item)
|
||||||
|
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)
|
||||||
local hl_group = ''
|
local hl_group = ''
|
||||||
if
|
if
|
||||||
@@ -342,6 +345,7 @@ function M._lsp_to_complete_items(result, prefix, client_id)
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
_fuzzy_score = score,
|
||||||
}
|
}
|
||||||
if user_convert then
|
if user_convert then
|
||||||
completion_item = vim.tbl_extend('keep', user_convert(item), completion_item)
|
completion_item = vim.tbl_extend('keep', user_convert(item), completion_item)
|
||||||
@@ -349,15 +353,33 @@ function M._lsp_to_complete_items(result, prefix, client_id)
|
|||||||
table.insert(candidates, completion_item)
|
table.insert(candidates, completion_item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not user_cmp then
|
if not user_cmp then
|
||||||
---@diagnostic disable-next-line: no-unknown
|
local compare_by_sortText_and_label = function(a, b)
|
||||||
table.sort(candidates, function(a, b)
|
|
||||||
---@type lsp.CompletionItem
|
---@type lsp.CompletionItem
|
||||||
local itema = a.user_data.nvim.lsp.completion_item
|
local itema = a.user_data.nvim.lsp.completion_item
|
||||||
---@type lsp.CompletionItem
|
---@type lsp.CompletionItem
|
||||||
local itemb = b.user_data.nvim.lsp.completion_item
|
local itemb = b.user_data.nvim.lsp.completion_item
|
||||||
return (itema.sortText or itema.label) < (itemb.sortText or itemb.label)
|
return (itema.sortText or itema.label) < (itemb.sortText or itemb.label)
|
||||||
end)
|
end
|
||||||
|
|
||||||
|
local use_fuzzy_sort = vim.o.completeopt:find('fuzzy') ~= nil
|
||||||
|
and vim.o.completeopt:find('nosort') == nil
|
||||||
|
and not result.isIncomplete
|
||||||
|
and #prefix > 0
|
||||||
|
|
||||||
|
local compare_fn = use_fuzzy_sort
|
||||||
|
and function(a, b)
|
||||||
|
local score_a = a._fuzzy_score or 0
|
||||||
|
local score_b = b._fuzzy_score or 0
|
||||||
|
if score_a ~= score_b then
|
||||||
|
return score_a > score_b
|
||||||
|
end
|
||||||
|
return compare_by_sortText_and_label(a, b)
|
||||||
|
end
|
||||||
|
or compare_by_sortText_and_label
|
||||||
|
|
||||||
|
table.sort(candidates, compare_fn)
|
||||||
end
|
end
|
||||||
return candidates
|
return candidates
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1394,6 +1394,64 @@ describe('vim.lsp.completion: integration', function()
|
|||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
it('sorts items when fuzzy is enabled and prefix not empty #33610', function()
|
||||||
|
local completion_list = {
|
||||||
|
isIncomplete = false,
|
||||||
|
items = {
|
||||||
|
{
|
||||||
|
kind = 21,
|
||||||
|
label = '-row-end-1',
|
||||||
|
sortText = '0327',
|
||||||
|
textEdit = {
|
||||||
|
newText = '-row-end-1',
|
||||||
|
range = {
|
||||||
|
['end'] = {
|
||||||
|
character = 1,
|
||||||
|
line = 0,
|
||||||
|
},
|
||||||
|
start = {
|
||||||
|
character = 0,
|
||||||
|
line = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind = 21,
|
||||||
|
label = 'w-1/2',
|
||||||
|
sortText = '3052',
|
||||||
|
textEdit = {
|
||||||
|
newText = 'w-1/2',
|
||||||
|
range = {
|
||||||
|
['end'] = {
|
||||||
|
character = 1,
|
||||||
|
line = 0,
|
||||||
|
},
|
||||||
|
start = {
|
||||||
|
character = 0,
|
||||||
|
line = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exec_lua(function()
|
||||||
|
vim.o.completeopt = 'menuone,fuzzy'
|
||||||
|
end)
|
||||||
|
create_server('dummy', completion_list, { trigger_chars = { '-' } })
|
||||||
|
feed('Sw-')
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.fn.pumvisible()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
feed('<C-y>')
|
||||||
|
eq('w-1/2', n.api.nvim_get_current_line())
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
||||||
|
|||||||
Reference in New Issue
Block a user