feat(lsp): user-specified sorting of lsp.completion multi-server results #36401

Problem: No way to customize completion order across multiple servers.

Solution: Add `cmp` function to `vim.lsp.completion.enable()` options
for custom sorting logic.
This commit is contained in:
glepnir
2025-11-19 13:38:53 +08:00
committed by GitHub
parent 2c04ae9fcc
commit c22b03c771
4 changed files with 49 additions and 11 deletions

View File

@@ -313,6 +313,7 @@ function M._lsp_to_complete_items(result, prefix, client_id)
local candidates = {}
local bufnr = api.nvim_get_current_buf()
local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert')
local user_cmp = vim.tbl_get(buf_handles, bufnr, 'cmp')
for _, item in ipairs(items) do
if matches(item) then
local word = get_completion_word(item, prefix, match_item_by_value)
@@ -348,15 +349,16 @@ function M._lsp_to_complete_items(result, prefix, client_id)
table.insert(candidates, completion_item)
end
end
---@diagnostic disable-next-line: no-unknown
table.sort(candidates, function(a, b)
---@type lsp.CompletionItem
local itema = a.user_data.nvim.lsp.completion_item
---@type lsp.CompletionItem
local itemb = b.user_data.nvim.lsp.completion_item
return (itema.sortText or itema.label) < (itemb.sortText or itemb.label)
end)
if not user_cmp then
---@diagnostic disable-next-line: no-unknown
table.sort(candidates, function(a, b)
---@type lsp.CompletionItem
local itema = a.user_data.nvim.lsp.completion_item
---@type lsp.CompletionItem
local itemb = b.user_data.nvim.lsp.completion_item
return (itema.sortText or itema.label) < (itemb.sortText or itemb.label)
end)
end
return candidates
end
@@ -551,6 +553,10 @@ local function trigger(bufnr, clients, ctx)
end, prev_matches)
matches = vim.list_extend(prev_matches, matches)
local user_cmp = vim.tbl_get(buf_handles, bufnr, 'cmp')
if user_cmp then
table.sort(matches, user_cmp)
end
local start_col = (server_start_boundary or word_boundary) + 1
Context.cursor = { cursor_row, start_col }
@@ -712,6 +718,7 @@ end
--- @class vim.lsp.completion.BufferOpts
--- @field autotrigger? boolean (default: false) When true, completion triggers automatically based on the server's `triggerCharacters`.
--- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|.
--- @field cmp? fun(a: table, b: table): boolean Comparator for sorting merged completion items from all servers.
---@param client_id integer
---@param bufnr integer
@@ -719,7 +726,7 @@ end
local function enable_completions(client_id, bufnr, opts)
local buf_handle = buf_handles[bufnr]
if not buf_handle then
buf_handle = { clients = {}, triggers = {}, convert = opts.convert }
buf_handle = { clients = {}, triggers = {}, convert = opts.convert, cmp = opts.cmp }
buf_handles[bufnr] = buf_handle
-- Attach to buffer events.