feat(lsp): vim.lsp.inline_completion on_accept #35507

This commit is contained in:
Yi Ming
2025-09-01 23:46:29 +08:00
committed by GitHub
parent 06df337617
commit 6888f65be1
3 changed files with 107 additions and 27 deletions

View File

@@ -11,11 +11,11 @@ local M = {}
local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
---@class (private) vim.lsp.inline_completion.CurrentItem
---@field index integer The index among all items form all clients.
---@class vim.lsp.inline_completion.Item
---@field _index integer The index among all items form all clients.
---@field client_id integer Client ID
---@field insert_text string|lsp.StringValue The text to be inserted, can be a snippet.
---@field filter_text? string
---@field _filter_text? string
---@field range? vim.Range Which range it be applied.
---@field command? lsp.Command Corresponding server command.
@@ -25,7 +25,7 @@ local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
---@class (private) vim.lsp.inline_completion.Completor : vim.lsp.Capability
---@field active table<integer, vim.lsp.inline_completion.Completor?>
---@field timer? uv.uv_timer_t Timer for debouncing automatic requests
---@field current? vim.lsp.inline_completion.CurrentItem Currently selected item
---@field current? vim.lsp.inline_completion.Item Currently selected item
---@field client_state table<integer, vim.lsp.inline_completion.ClientState>
local Completor = {
name = 'inline_completion',
@@ -146,11 +146,11 @@ function Completor:select(index, show_index)
local client = assert(vim.lsp.get_client_by_id(client_id))
local range = item.range and vim.range.lsp(self.bufnr, item.range, client.offset_encoding)
self.current = {
index = index,
_index = index,
client_id = client_id,
insert_text = item.insertText,
range = range,
filter_text = item.filterText,
_filter_text = item.filterText,
command = item.command,
}
@@ -281,19 +281,14 @@ function Completor:abort()
self.current = nil
end
--- Apply the current completion item to the buffer.
--- Accept the current completion item to the buffer.
---
---@package
function Completor:apply()
local current = self.current
self:abort()
if not current then
return
end
local insert_text = current.insert_text
---@param item vim.lsp.inline_completion.Item
function Completor:accept(item)
local insert_text = item.insert_text
if type(insert_text) == 'string' then
local range = current.range
local range = item.range
if range then
local lines = vim.split(insert_text, '\n')
api.nvim_buf_set_text(
@@ -304,7 +299,7 @@ function Completor:apply()
range.end_.col,
lines
)
local pos = range.start:to_cursor()
local pos = item.range.start:to_cursor()
api.nvim_win_set_cursor(vim.fn.bufwinid(self.bufnr), {
pos[1] + #lines - 1,
(#lines == 1 and pos[2] or 0) + #lines[#lines],
@@ -317,9 +312,9 @@ function Completor:apply()
end
-- Execute the command *after* inserting this completion.
if current.command then
local client = assert(vim.lsp.get_client_by_id(current.client_id))
client:exec_cmd(current.command, { bufnr = self.bufnr })
if item.command then
local client = assert(vim.lsp.get_client_by_id(item.client_id))
client:exec_cmd(item.command, { bufnr = self.bufnr })
end
end
@@ -381,7 +376,7 @@ function M.select(opts)
end
local n = completor:count_items()
local index = current.index + count
local index = current._index + count
if wrap then
index = (index - 1) % n + 1
else
@@ -396,10 +391,15 @@ end
--- Buffer handle, or 0 for current.
--- (default: 0)
---@field bufnr? integer
--- Apply the currently displayed completion candidate to the buffer.
---
--- It returns false when no candidate can be applied,
--- Accept handler, called with the accepted item.
--- If not provided, the default handler is used,
--- which applies changes to the buffer based on the completion item.
---@field on_accept? fun(item: vim.lsp.inline_completion.Item)
--- Accept the currently displayed completion candidate to the buffer.
---
--- It returns false when no candidate can be accepted,
--- so you can use the return value to implement a fallback:
---
--- ```lua
@@ -420,11 +420,23 @@ function M.get(opts)
opts = opts or {}
local bufnr = vim._resolve_bufnr(opts.bufnr)
local on_accept = opts.on_accept
local completor = Completor.active[bufnr]
if completor and completor.current then
-- Schedule apply to allow `get()` can be mapped with `<expr>`.
vim.schedule(function()
completor:apply()
local item = completor.current
completor:abort()
if not item then
return
end
if on_accept then
on_accept(item)
else
completor:accept(item)
end
end)
return true
end