feat(lsp): apply additionalTextEdits on completion accept

This commit is contained in:
2026-04-16 04:14:13 +02:00
parent e3e4d81ab0
commit 7162d00b43
4 changed files with 100 additions and 26 deletions
+22 -24
View File
@@ -22,42 +22,23 @@ local function fence(ft, text)
return string.format("```%s\n%s\n```", ft, text)
end
---@param item lsp.CompletionItem
---@return string
local function signature_of(item)
return item.detail or vim.tbl_get(item, "labelDetails", "description") or ""
end
---@param item lsp.CompletionItem
---@param ft string
---@return string? content
---@return integer? width
local function build_content(item, ft)
local signature = signature_of(item)
local signature = item.detail
or vim.tbl_get(item, "labelDetails", "description")
local doc = item.documentation
if type(doc) == "table" then
doc = doc.value
end
doc = doc or ""
local code_parts = {}
if item.additionalTextEdits then
for _, edit in ipairs(item.additionalTextEdits) do
local text = (edit.newText or ""):gsub("%s+$", "")
if text ~= "" then
table.insert(code_parts, fence(ft, text))
end
end
end
if signature ~= "" then
table.insert(code_parts, fence(ft, signature))
end
local sections = {}
if #code_parts > 0 then
table.insert(sections, table.concat(code_parts, "\n\n"))
if signature then
table.insert(sections, fence(ft, signature))
end
if doc ~= "" then
if doc then
table.insert(sections, doc)
end
if #sections == 0 then
@@ -78,6 +59,7 @@ end
---@field private winid integer?
---@field private bufnr integer?
---@field private pending ow.lsp.completion.PendingResolve?
---@field private resolved { word: string, item: lsp.CompletionItem }?
local Popup = {}
Popup.__index = Popup
@@ -87,9 +69,23 @@ function Popup.new()
winid = nil,
bufnr = nil,
pending = nil,
resolved = nil,
}, Popup)
end
--- Most recently resolved completion item for the given selection `word`,
--- or `nil` if we don't have one cached. Servers like rust-analyzer fill in
--- `additionalTextEdits` only in the resolve response, so on accept we want
--- the resolved version rather than the original one in `user_data`.
---@param word string
---@return lsp.CompletionItem?
function Popup:resolved_for(word)
if self.resolved and self.resolved.word == word then
return self.resolved.item
end
return nil
end
---@return boolean
function Popup:is_visible()
return self.winid ~= nil and vim.api.nvim_win_is_valid(self.winid)
@@ -127,6 +123,7 @@ end
---@param buf integer
function Popup:dispatch_resolve(client, item, ft, pum, word, buf)
self:cancel_pending()
self.resolved = nil
local _, request_id = client:request(
vim.lsp.protocol.Methods.completionItem_resolve,
item,
@@ -139,6 +136,7 @@ function Popup:dispatch_resolve(client, item, ft, pum, word, buf)
if (vim.tbl_get(cur, "completed", "word") or "") ~= word then
return
end
self.resolved = { word = word, item = result }
self:show(result, ft, pum)
end,
buf