feat(lsp): add snippet support and refactor completion into Item class

This commit is contained in:
2026-04-17 00:03:03 +02:00
parent 5ecec7cc6c
commit 47f36adc21
9 changed files with 213 additions and 144 deletions
+17 -28
View File
@@ -22,24 +22,20 @@ local function fence(ft, text)
return string.format("```%s\n%s\n```", ft, text)
end
---@param item lsp.CompletionItem
---@param item ow.lsp.completion.Item
---@param ft string
---@return string? content
---@return integer? width
local function build_content(item, ft)
local signature = item.detail
or vim.tbl_get(item, "labelDetails", "description")
local doc = item.documentation
if type(doc) == "table" then
doc = doc.value
end
local sections = {}
if signature then
table.insert(sections, fence(ft, signature))
if item.detail then
table.insert(sections, fence(ft, item.detail))
end
if doc then
table.insert(sections, doc)
if item.snippet then
table.insert(sections, fence(ft, item.snippet))
end
if item.doc then
table.insert(sections, item.doc)
end
if #sections == 0 then
return nil, nil
@@ -59,7 +55,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 }?
---@field private resolved { word: string, item: ow.lsp.completion.Item }?
local Popup = {}
Popup.__index = Popup
@@ -73,12 +69,8 @@ function Popup.new()
}, 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?
---@return ow.lsp.completion.Item?
function Popup:resolved_for(word)
if self.resolved and self.resolved.word == word then
return self.resolved.item
@@ -99,8 +91,7 @@ function Popup:close()
self.winid = nil
end
--- Build content for `item` and show, or close if there's nothing to render.
---@param item lsp.CompletionItem
---@param item ow.lsp.completion.Item
---@param ft string
---@param pum ow.lsp.completion.Pum
function Popup:show(item, ft, pum)
@@ -112,11 +103,8 @@ function Popup:show(item, ft, pum)
end
end
--- Cancel any in-flight resolve, dispatch a fresh one for `item`, and on
--- response render the resolved popup. If the user moved the selection
--- before the response landed (word mismatch), do nothing.
---@param client vim.lsp.Client
---@param item lsp.CompletionItem
---@param item ow.lsp.completion.Item
---@param ft string
---@param pum ow.lsp.completion.Pum
---@param word string
@@ -126,7 +114,7 @@ function Popup:dispatch_resolve(client, item, ft, pum, word, buf)
self.resolved = nil
local _, request_id = client:request(
vim.lsp.protocol.Methods.completionItem_resolve,
item,
item.raw,
function(err, result)
self.pending = nil
if err or not result then
@@ -136,8 +124,9 @@ 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)
item:apply_resolved(result)
self.resolved = { word = word, item = item }
self:show(item, ft, pum)
end,
buf
)
@@ -146,7 +135,7 @@ function Popup:dispatch_resolve(client, item, ft, pum, word, buf)
end
end
---@param direction string single-line scroll keycode (<C-e> or <C-y>)
---@param direction string single-line scroll keycode (<C-e> or <C-y>)
function Popup:scroll_line(direction)
self:scroll(direction, 1)
end