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
+13
View File
@@ -162,6 +162,18 @@ end)
vim.keymap.set({ "i", "s" }, "<C-s>", function() vim.keymap.set({ "i", "s" }, "<C-s>", function()
vim.lsp.buf.signature_help({ max_width = 80 }) vim.lsp.buf.signature_help({ max_width = 80 })
end) end)
---@param key string
---@param dir 1 | -1
local function snippet_jump_map(key, dir)
vim.keymap.set({ "i", "s" }, key, function()
if vim.snippet.active({ direction = dir }) then
return string.format("<cmd>lua vim.snippet.jump(%d)<CR>", dir)
end
return key
end, { expr = true, replace_keycodes = true })
end
snippet_jump_map("<Tab>", 1)
snippet_jump_map("<S-Tab>", -1)
vim.keymap.set({ "n", "i" }, "<C-h>", vim.lsp.buf.document_highlight) vim.keymap.set({ "n", "i" }, "<C-h>", vim.lsp.buf.document_highlight)
vim.keymap.set({ "n", "x" }, "<leader>lf", vim.lsp.buf.format) vim.keymap.set({ "n", "x" }, "<leader>lf", vim.lsp.buf.format)
vim.keymap.set("n", "grl", function() vim.keymap.set("n", "grl", function()
@@ -177,6 +189,7 @@ vim.keymap.set({ "n", "i" }, "<C-l>", function()
vim.lsp.buf.clear_references() vim.lsp.buf.clear_references()
vim.cmd.nohlsearch() vim.cmd.nohlsearch()
vim.schedule(vim.cmd.diffupdate) vim.schedule(vim.cmd.diffupdate)
vim.snippet.stop()
return "<C-l>" return "<C-l>"
end, { expr = true }) end, { expr = true })
vim.keymap.set("n", "<leader>fd", vim.diagnostic.setloclist) vim.keymap.set("n", "<leader>fd", vim.diagnostic.setloclist)
+2 -2
View File
@@ -12,7 +12,7 @@ local Node = {}
Node.__index = Node Node.__index = Node
---@param item ow.dap.Item ---@param item ow.dap.Item
---@param parent ow.dap.hover.Node? ---@param parent? ow.dap.hover.Node
---@param lang string ---@param lang string
---@return ow.dap.hover.Node ---@return ow.dap.hover.Node
function Node.new(item, parent, lang) function Node.new(item, parent, lang)
@@ -68,7 +68,7 @@ function Node:at(n)
return search(self) return search(self)
end end
---@param target ow.dap.hover.Node? if nil, returns index of self ---@param target? ow.dap.hover.Node if nil, returns index of self
---@return integer? ---@return integer?
function Node:index_of(target) function Node:index_of(target)
target = target or self target = target or self
+2 -6
View File
@@ -25,7 +25,7 @@ end, REFRESH_DEBOUNCE_MS)
---@class ow.lsp.codelens.Row ---@class ow.lsp.codelens.Row
---@field row integer ---@field row integer
---@field ready lsp.CodeLens[] ---@field ready lsp.CodeLens[]
---@field pending integer count of unresolved lenses still in flight ---@field pending integer
local Row = {} local Row = {}
Row.__index = Row Row.__index = Row
@@ -44,7 +44,7 @@ function Row:await()
self.pending = self.pending + 1 self.pending = self.pending + 1
end end
---@param resolved lsp.CodeLens? ---@param resolved? lsp.CodeLens
function Row:resolved(resolved) function Row:resolved(resolved)
self.pending = self.pending - 1 self.pending = self.pending - 1
if resolved then if resolved then
@@ -52,9 +52,6 @@ function Row:resolved(resolved)
end end
end end
--- Paint this row's extmark. Preserves the previous extmark while any lens
--- on the row is still resolving so the user doesn't see a partial row
--- (e.g. "refs" without "impls").
---@param buf integer ---@param buf integer
function Row:render(buf) function Row:render(buf)
if not vim.api.nvim_buf_is_valid(buf) then if not vim.api.nvim_buf_is_valid(buf) then
@@ -161,7 +158,6 @@ function State.new(buf)
}, State) }, State)
end end
--- Paint every row and drop extmarks on rows that are no longer present.
function State:render() function State:render()
if not vim.api.nvim_buf_is_valid(self.buf) then if not vim.api.nvim_buf_is_valid(self.buf) then
return return
+47 -45
View File
@@ -1,3 +1,4 @@
local Item = require("lsp.completion.item")
local Popup = require("lsp.completion.popup") local Popup = require("lsp.completion.popup")
local request = require("lsp.completion.request") local request = require("lsp.completion.request")
@@ -9,7 +10,7 @@ local M = {}
---@param capabilities lsp.ClientCapabilities ---@param capabilities lsp.ClientCapabilities
function M.apply_capabilities(capabilities) function M.apply_capabilities(capabilities)
capabilities.textDocument.completion.completionItem.snippetSupport = false capabilities.textDocument.completion.completionItem.snippetSupport = true
end end
---@param client vim.lsp.Client ---@param client vim.lsp.Client
@@ -38,18 +39,10 @@ function M.setup()
group = GROUP, group = GROUP,
callback = function(ev) callback = function(ev)
local completed = vim.v.event.completed_item or {} local completed = vim.v.event.completed_item or {}
local lsp_item = vim.tbl_get( local item = Item.from_user_data(completed)
completed, local client = item and vim.lsp.get_client_by_id(item.client_id)
"user_data",
"nvim",
"lsp",
"completion_item"
)
local client_id =
vim.tbl_get(completed, "user_data", "nvim", "lsp", "client_id")
local client = client_id and vim.lsp.get_client_by_id(client_id)
if not lsp_item or not client then if not item or not client then
vim.schedule(function() vim.schedule(function()
popup:close() popup:close()
end) end)
@@ -73,7 +66,7 @@ function M.setup()
then then
popup:dispatch_resolve( popup:dispatch_resolve(
client, client,
lsp_item, item,
ft, ft,
pum, pum,
completed.word, completed.word,
@@ -81,7 +74,7 @@ function M.setup()
) )
else else
vim.schedule(function() vim.schedule(function()
popup:show(lsp_item, ft, pum) popup:show(item, ft, pum)
end) end)
end end
end, end,
@@ -94,52 +87,53 @@ function M.setup()
end, end,
}) })
-- Apply the LSP item's additionalTextEdits (auto-imports, etc.) and
-- run its post-accept command whenever an item's text was committed to
-- the buffer. This covers both explicit accept (<C-y>) and
-- acceptance-by-continuing-to-type, since Vim keeps the inserted text
-- in both cases. Only <C-e>/discard leaves v:completed_item empty.
vim.api.nvim_create_autocmd("CompleteDone", { vim.api.nvim_create_autocmd("CompleteDone", {
group = GROUP, group = GROUP,
callback = function(ev) callback = function(ev)
local completed = vim.v.completed_item or {} local completed = vim.v.completed_item or {}
local original = vim.tbl_get( local item = Item.from_user_data(completed)
completed, if not item then
"user_data",
"nvim",
"lsp",
"completion_item"
)
if not original then
return return
end end
local client_id = local client = vim.lsp.get_client_by_id(item.client_id)
vim.tbl_get(completed, "user_data", "nvim", "lsp", "client_id")
local client = client_id and vim.lsp.get_client_by_id(client_id)
if not client then if not client then
return return
end end
local function apply(item) local function apply(target)
if item.additionalTextEdits then local raw = target.raw
if raw.additionalTextEdits then
vim.lsp.util.apply_text_edits( vim.lsp.util.apply_text_edits(
item.additionalTextEdits, raw.additionalTextEdits,
ev.buf, ev.buf,
client.offset_encoding client.offset_encoding
) )
end end
if item.command then if raw.command then
client:request("workspace/executeCommand", { client:request("workspace/executeCommand", {
command = item.command.command, command = raw.command.command,
arguments = item.command.arguments, arguments = raw.command.arguments,
}, nil, ev.buf) }, nil, ev.buf)
end end
if target.snippet then
local word = completed.word or ""
local cursor = vim.api.nvim_win_get_cursor(0)
local row = cursor[1] - 1
local col = cursor[2]
vim.api.nvim_buf_set_text(
ev.buf,
row,
col - #word,
row,
col,
{}
)
vim.snippet.expand(target.snippet)
end
end end
-- Prefer the resolved item when we have it (servers like -- Prefer the resolved item when we have it. If we haven't cached
-- rust-analyzer only provide additionalTextEdits in resolve -- one yet and the original is missing edits, resolve on the fly.
-- responses). If we haven't cached one yet and the original is
-- missing edits, resolve on the fly.
local cached = popup:resolved_for(completed.word) local cached = popup:resolved_for(completed.word)
if cached then if cached then
apply(cached) apply(cached)
@@ -147,22 +141,23 @@ function M.setup()
client:supports_method( client:supports_method(
vim.lsp.protocol.Methods.completionItem_resolve vim.lsp.protocol.Methods.completionItem_resolve
) )
and not original.additionalTextEdits and not item.raw.additionalTextEdits
and not original.command and not item.raw.command
then then
client:request( client:request(
vim.lsp.protocol.Methods.completionItem_resolve, vim.lsp.protocol.Methods.completionItem_resolve,
original, item.raw,
function(err, resolved) function(err, resolved)
if err or not resolved then if err or not resolved then
return return
end end
apply(resolved) item:apply_resolved(resolved)
apply(item)
end, end,
ev.buf ev.buf
) )
else else
apply(original) apply(item)
end end
end, end,
}) })
@@ -190,6 +185,13 @@ function M.setup()
scroll_map("<C-k>", function() scroll_map("<C-k>", function()
popup:scroll_line(vim.keycode("<C-y>")) popup:scroll_line(vim.keycode("<C-y>"))
end) end)
vim.keymap.set("i", "<CR>", function()
if vim.fn.pumvisible() ~= 0 then
return "<C-y>"
end
return "<CR>"
end, { expr = true, replace_keycodes = true })
end end
return M return M
+90
View File
@@ -0,0 +1,90 @@
local SNIPPET = vim.lsp.protocol.InsertTextFormat.Snippet
---@param raw lsp.CompletionItem
---@return string?
local function edit_text(raw)
local edit = raw.textEdit
return (type(edit) == "table" and edit.newText) or raw.insertText
end
---@param raw lsp.CompletionItem
---@return string
local function label_stem(raw)
return raw.label:match("[^(]+") or raw.label
end
---@param raw lsp.CompletionItem
---@return string?
local function extract_doc(raw)
local doc = raw.documentation
if type(doc) == "table" then
return doc.value
end
---@cast doc string?
return doc
end
---@param raw lsp.CompletionItem
---@return string?
local function extract_detail(raw)
return raw.detail or vim.tbl_get(raw, "labelDetails", "description")
end
---@class ow.lsp.completion.Item
---@field word string
---@field abbr string
---@field menu string
---@field detail string?
---@field doc string?
---@field snippet string?
---@field client_id integer
---@field raw lsp.CompletionItem
local Item = {}
Item.__index = Item
---@param raw lsp.CompletionItem
---@param client_id integer
---@return ow.lsp.completion.Item
function Item.from_lsp(raw, client_id)
local is_snippet = raw.insertTextFormat == SNIPPET
local abbr = label_stem(raw)
local word
if is_snippet then
word = raw.filterText or abbr
abbr = abbr .. "~"
else
word = edit_text(raw) or raw.label
end
return setmetatable({
word = word,
abbr = abbr,
menu = vim.tbl_get(raw, "labelDetails", "detail") or "",
detail = extract_detail(raw),
doc = extract_doc(raw),
snippet = is_snippet and (edit_text(raw) or raw.label) or nil,
client_id = client_id,
raw = raw,
}, Item)
end
---@param resolved lsp.CompletionItem
function Item:apply_resolved(resolved)
self.raw = vim.tbl_deep_extend("force", self.raw, resolved)
self.detail = extract_detail(self.raw)
self.doc = extract_doc(self.raw)
if self.snippet then
self.snippet = edit_text(self.raw) or self.raw.label
end
end
---@param completed vim.v.completed_item
---@return ow.lsp.completion.Item?
function Item.from_user_data(completed)
local t = vim.tbl_get(completed, "user_data", "ow", "item")
if t then
return setmetatable(t, Item)
end
return nil
end
return Item
+16 -27
View File
@@ -22,24 +22,20 @@ local function fence(ft, text)
return string.format("```%s\n%s\n```", ft, text) return string.format("```%s\n%s\n```", ft, text)
end end
---@param item lsp.CompletionItem ---@param item ow.lsp.completion.Item
---@param ft string ---@param ft string
---@return string? content ---@return string? content
---@return integer? width ---@return integer? width
local function build_content(item, ft) 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 = {} local sections = {}
if signature then if item.detail then
table.insert(sections, fence(ft, signature)) table.insert(sections, fence(ft, item.detail))
end end
if doc then if item.snippet then
table.insert(sections, doc) table.insert(sections, fence(ft, item.snippet))
end
if item.doc then
table.insert(sections, item.doc)
end end
if #sections == 0 then if #sections == 0 then
return nil, nil return nil, nil
@@ -59,7 +55,7 @@ end
---@field private winid integer? ---@field private winid integer?
---@field private bufnr integer? ---@field private bufnr integer?
---@field private pending ow.lsp.completion.PendingResolve? ---@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 = {} local Popup = {}
Popup.__index = Popup Popup.__index = Popup
@@ -73,12 +69,8 @@ function Popup.new()
}, Popup) }, Popup)
end 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 ---@param word string
---@return lsp.CompletionItem? ---@return ow.lsp.completion.Item?
function Popup:resolved_for(word) function Popup:resolved_for(word)
if self.resolved and self.resolved.word == word then if self.resolved and self.resolved.word == word then
return self.resolved.item return self.resolved.item
@@ -99,8 +91,7 @@ function Popup:close()
self.winid = nil self.winid = nil
end end
--- Build content for `item` and show, or close if there's nothing to render. ---@param item ow.lsp.completion.Item
---@param item lsp.CompletionItem
---@param ft string ---@param ft string
---@param pum ow.lsp.completion.Pum ---@param pum ow.lsp.completion.Pum
function Popup:show(item, ft, pum) function Popup:show(item, ft, pum)
@@ -112,11 +103,8 @@ function Popup:show(item, ft, pum)
end end
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 client vim.lsp.Client
---@param item lsp.CompletionItem ---@param item ow.lsp.completion.Item
---@param ft string ---@param ft string
---@param pum ow.lsp.completion.Pum ---@param pum ow.lsp.completion.Pum
---@param word string ---@param word string
@@ -126,7 +114,7 @@ function Popup:dispatch_resolve(client, item, ft, pum, word, buf)
self.resolved = nil self.resolved = nil
local _, request_id = client:request( local _, request_id = client:request(
vim.lsp.protocol.Methods.completionItem_resolve, vim.lsp.protocol.Methods.completionItem_resolve,
item, item.raw,
function(err, result) function(err, result)
self.pending = nil self.pending = nil
if err or not result then 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 if (vim.tbl_get(cur, "completed", "word") or "") ~= word then
return return
end end
self.resolved = { word = word, item = result } item:apply_resolved(result)
self:show(result, ft, pum) self.resolved = { word = word, item = item }
self:show(item, ft, pum)
end, end,
buf buf
) )
+31 -56
View File
@@ -1,23 +1,24 @@
---@class ow.lsp.CompletionResponse : ow.lsp.Response
---@field result lsp.CompletionItem[] | lsp.CompletionList
local Item = require("lsp.completion.item")
local kind = require("lsp.kind") local kind = require("lsp.kind")
local util = require("util") local util = require("util")
local REQUEST_DEBOUNCE_MS = 100 local REQUEST_DEBOUNCE_MS = 100
--- Normalize an `itemDefaults.editRange` to a plain Range, since it may be ---@param item_defaults? lsp.CompletionItemDefaults
--- either `Range` or `{ insert: Range, replace: Range }`. ---@return lsp.Range?
---@param edit_range table? local function default_range(item_defaults)
---@return table? local edit_range = item_defaults and item_defaults.editRange
local function default_range(edit_range)
if type(edit_range) ~= "table" then if type(edit_range) ~= "table" then
return nil return nil
end end
return edit_range.insert or (edit_range.start and edit_range) or nil return edit_range.insert or (edit_range.start and edit_range)
end end
--- Normalize an item's `textEdit`, which may be `TextEdit` (has `range`) or
--- `InsertReplaceEdit` (has `insert`/`replace`).
---@param item lsp.CompletionItem ---@param item lsp.CompletionItem
---@return table? ---@return lsp.Range?
local function item_range(item) local function item_range(item)
local te = item.textEdit local te = item.textEdit
if type(te) ~= "table" then if type(te) ~= "table" then
@@ -26,18 +27,18 @@ local function item_range(item)
return te.range or te.insert return te.range or te.insert
end end
---@param response table LSP response entry (as returned by buf_request_all) ---@param response ow.lsp.CompletionResponse
---@return integer? character 0-indexed UTF-16 character where edit starts ---@return integer? character 0-indexed UTF-16 character where edit starts
local function response_edit_start(response) local function response_edit_start(response)
if not response.result or response.err or response.error then if not response.result or response.err then
return nil return nil
end end
local defaults = local result = response.result
default_range(vim.tbl_get(response.result, "itemDefaults", "editRange")) local defaults = default_range(result.items and result.itemDefaults)
if defaults then if defaults then
return defaults.start.character return defaults.start.character
end end
local items = response.result.items or response.result local items = result.items or result
if type(items) ~= "table" then if type(items) ~= "table" then
return nil return nil
end end
@@ -50,23 +51,13 @@ local function response_edit_start(response)
return nil return nil
end end
---@param item lsp.CompletionItem
---@return string
local function item_word(item)
local edit = item.textEdit
if type(edit) == "table" and edit.newText then
return edit.newText
end
return item.insertText or item.label
end
---@param items lsp.CompletionItem[] ---@param items lsp.CompletionItem[]
---@param defaults? table ---@param defaults? lsp.CompletionItemDefaults
local function apply_defaults(items, defaults) local function apply_defaults(items, defaults)
if type(defaults) ~= "table" then if type(defaults) ~= "table" then
return return
end end
local range = default_range(defaults.editRange) local range = default_range(defaults)
for _, item in ipairs(items) do for _, item in ipairs(items) do
item.commitCharacters = item.commitCharacters item.commitCharacters = item.commitCharacters
or defaults.commitCharacters or defaults.commitCharacters
@@ -83,15 +74,13 @@ local function apply_defaults(items, defaults)
end end
end end
--- Transform raw LSP completion responses into Vim |complete()| items, ---@param responses table<integer, ow.lsp.CompletionResponse>
--- filtered by `base` and sorted by sortText/label.
---@param responses table map of client_id -> response from buf_request_all
---@param base string ---@param base string
---@return table[] ---@return vim.v.completed_item[]
local function build_items(responses, base) local function build_items(responses, base)
local entries = {} local entries = {}
for client_id, response in pairs(responses) do for client_id, response in pairs(responses) do
if response.result and not (response.err or response.error) then if response.result and not response.err then
local raw = response.result.items or response.result local raw = response.result.items or response.result
if type(raw) == "table" then if type(raw) == "table" then
apply_defaults(raw, response.result.itemDefaults) apply_defaults(raw, response.result.itemDefaults)
@@ -116,14 +105,15 @@ local function build_items(responses, base)
return ka < kb return ka < kb
end) end)
---@type vim.v.completed_item[]
local result = {} local result = {}
for _, entry in ipairs(entries) do for _, entry in ipairs(entries) do
local item = entry.item local item = Item.from_lsp(entry.item, entry.client_id)
local kind_icon, kind_hl = kind.get(item.kind) local kind_icon, kind_hl = kind.get(entry.item.kind)
result[#result + 1] = { result[#result + 1] = {
word = item_word(item), word = item.word,
abbr = item.label:match("[^(]+") or item.label, abbr = item.abbr,
menu = vim.tbl_get(item, "labelDetails", "detail") or "", menu = item.menu,
kind = kind_icon, kind = kind_icon,
kind_hlgroup = kind_hl, kind_hlgroup = kind_hl,
-- non-empty so our CompleteChanged handler triggers resolve -- non-empty so our CompleteChanged handler triggers resolve
@@ -131,14 +121,7 @@ local function build_items(responses, base)
icase = 1, icase = 1,
dup = 1, dup = 1,
empty = 1, empty = 1,
user_data = { user_data = { ow = { item = item } },
nvim = {
lsp = {
completion_item = item,
client_id = entry.client_id,
},
},
},
} }
end end
return result return result
@@ -182,7 +165,7 @@ end
---@class ow.lsp.completion.Request ---@class ow.lsp.completion.Request
---@field private generation integer ---@field private generation integer
---@field private phase ('awaiting' | 'ready' | 'done')? ---@field private phase ('awaiting' | 'ready' | 'done')?
---@field private result table? ---@field private result table<integer, ow.lsp.CompletionResponse>?
---@field private cancel function? ---@field private cancel function?
local Request = {} local Request = {}
Request.__index = Request Request.__index = Request
@@ -207,7 +190,6 @@ function Request:is_ready()
return self.phase == "ready" return self.phase == "ready"
end end
--- Cancel any in-flight LSP request and clear cached results.
function Request:discard() function Request:discard()
if self.cancel then if self.cancel then
pcall(self.cancel) pcall(self.cancel)
@@ -219,7 +201,7 @@ function Request:discard()
end end
---@param trigger_kind integer ---@param trigger_kind integer
---@param trigger_char string? ---@param trigger_char? string
function Request:dispatch(trigger_kind, trigger_char) function Request:dispatch(trigger_kind, trigger_char)
self:discard() self:discard()
self.phase = "awaiting" self.phase = "awaiting"
@@ -246,8 +228,6 @@ function Request:dispatch(trigger_kind, trigger_char)
) )
end end
--- Byte column where the cached LSP edits start, for |complete-functions|
--- `findstart == 1`. Falls back to the regex end of the word under cursor.
---@return integer ---@return integer
function Request:start_col() function Request:start_col()
for _, response in pairs(self.result or {}) do for _, response in pairs(self.result or {}) do
@@ -261,11 +241,8 @@ function Request:start_col()
return vim.fn.match(line:sub(1, cursor_col), "\\k*$") return vim.fn.match(line:sub(1, cursor_col), "\\k*$")
end end
--- Build the Vim |complete()| items from the cached LSP results filtered by
--- `base`, and transition this request to the "done" phase. Subsequent
--- completion triggers will dispatch a fresh request.
---@param base string ---@param base string
---@return table[] ---@return vim.v.completed_item[]
function Request:consume(base) function Request:consume(base)
local items = build_items(self.result or {}, base) local items = build_items(self.result or {}, base)
self.phase = "done" self.phase = "done"
@@ -310,11 +287,9 @@ end
-- `v:lua.ow_lsp_completion.omnifunc` from on_attach's `vim.bo.omnifunc`. -- `v:lua.ow_lsp_completion.omnifunc` from on_attach's `vim.bo.omnifunc`.
_G.ow_lsp_completion = _G.ow_lsp_completion or {} _G.ow_lsp_completion = _G.ow_lsp_completion or {}
--- Returns cached items without re-requesting, which is what lets Vim filter
--- the popup in-place on backspace.
---@param findstart 0 | 1 ---@param findstart 0 | 1
---@param base string ---@param base string
---@return integer | table ---@return integer | vim.v.completed_item[]
function _G.ow_lsp_completion.omnifunc(findstart, base) function _G.ow_lsp_completion.omnifunc(findstart, base)
if not buffer_has_completion_client() or request:is_awaiting() then if not buffer_has_completion_client() or request:is_awaiting() then
return findstart == 1 and -3 or {} return findstart == 1 and -3 or {}
+5
View File
@@ -1,3 +1,8 @@
---@class ow.lsp.Response
---@field err lsp.ResponseError?
---@field result any
---@field context lsp.HandlerContext
local codelens = require("lsp.codelens") local codelens = require("lsp.codelens")
local completion = require("lsp.completion") local completion = require("lsp.completion")
local diagnostic = require("lsp.diagnostic") local diagnostic = require("lsp.diagnostic")
+5 -6
View File
@@ -56,17 +56,16 @@ local DEFAULT_HIGHLIGHTS = {
Reference = "Special", Reference = "Special",
} }
--- Look up icon and highlight group for an LSP CompletionItemKind number. ---@param kind? integer
---@param kind integer? ---@return string? icon
---@return string icon empty string if the kind is unknown ---@return string? hl
---@return string? hl nil if the kind is unknown
function M.get(kind) function M.get(kind)
if not kind then if not kind then
return "", nil return nil, nil
end end
local name = vim.lsp.protocol.CompletionItemKind[kind] local name = vim.lsp.protocol.CompletionItemKind[kind]
if not name or not M.ICONS[name] then if not name or not M.ICONS[name] then
return "", nil return nil, nil
end end
return M.ICONS[name], "OwLspKind" .. name return M.ICONS[name], "OwLspKind" .. name
end end