perf: improve for source providing huge list of items (#1980)

* perf: avoid creating closure in cache.ensure and drop some cached getters

This mainly addresses the perf issue on large amount of calls to
`entry.new`. Previously every `cache.ensure` calls in the code path of
it creates an anonymous function, and it seems that luajit just could
not inline it. Function creation is not expensive in luajit, but that
overhead is noticeable if every `cache.ensure` call creates a function.
The first improvemnt is to solidate the cache callback and attach it to
the metatable of `entry`. This ensures that every created entry instance
share the same cache callback and no new functions will be frequently created,
reduces the ram usage and GC overhead.

To improve it further, some frequently accessed fields of entry like
`completion_item` and `offset` is refactored to use simple table access
instead of getter pattern. The current cached getter is implemented
using `cache.ensure`, which introduces two more levels of function calls
on each access: `cache.key` and `cache.get`. The overhead is okay if but
noticeable if entries amount is quite large: you need to call 4 functions on
a simple `completion_item` field access for each item.

All of the changes done in the commit is just constant time
optimization. But the different is huge if tested with LS providing
large amount of entries like tailwindcss.

* perf: delay fuzzy match on displayed vim item

`entry.get_vim_item` is a very heavy call, especially when user do
complex stuff on item formatting. Delay its call to window displaying to
let `performance.max_view_entries` applied to it.

* remove unneeded fill_defaults

* update gha

---------

Co-authored-by: hrsh7th <629908+hrsh7th@users.noreply.github.com>
This commit is contained in:
yioneko
2024-10-20 12:28:20 +08:00
committed by GitHub
parent ae644feb7b
commit 1a1d7ecb73
12 changed files with 468 additions and 408 deletions

View File

@@ -124,7 +124,7 @@ core.on_keymap = function(self, keys, fallback)
commit_character = chars,
}, function()
local ctx = self:get_context()
local word = e:get_word()
local word = e.word
if string.sub(ctx.cursor_before_line, -#word, ctx.cursor.col - 1) == word and is_printable then
fallback()
else
@@ -358,7 +358,7 @@ core.confirm = function(self, e, option, callback)
end
e.confirmed = true
debug.log('entry.confirm', e:get_completion_item())
debug.log('entry.confirm', e.completion_item)
async.sync(function(done)
e:resolve(done)
@@ -374,8 +374,8 @@ core.confirm = function(self, e, option, callback)
-- Emulate `<C-y>` behavior to save `.` register.
local ctx = context.new()
local keys = {}
table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e:get_offset())))
table.insert(keys, e:get_word())
table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))
table.insert(keys, e.word)
table.insert(keys, keymap.undobreak())
feedkeys.call(table.concat(keys, ''), 'in')
end)
@@ -384,15 +384,15 @@ core.confirm = function(self, e, option, callback)
local ctx = context.new()
if api.is_cmdline_mode() then
local keys = {}
table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e:get_offset())))
table.insert(keys, string.sub(e.context.cursor_before_line, e:get_offset()))
table.insert(keys, keymap.backspace(ctx.cursor_before_line:sub(e.offset)))
table.insert(keys, string.sub(e.context.cursor_before_line, e.offset))
feedkeys.call(table.concat(keys, ''), 'in')
else
vim.cmd([[silent! undojoin]])
-- This logic must be used nvim_buf_set_text.
-- If not used, the snippet engine's placeholder wil be broken.
vim.api.nvim_buf_set_text(0, e.context.cursor.row - 1, e:get_offset() - 1, ctx.cursor.row - 1, ctx.cursor.col - 1, {
e.context.cursor_before_line:sub(e:get_offset()),
vim.api.nvim_buf_set_text(0, e.context.cursor.row - 1, e.offset - 1, ctx.cursor.row - 1, ctx.cursor.col - 1, {
e.context.cursor_before_line:sub(e.offset),
})
vim.api.nvim_win_set_cursor(0, { e.context.cursor.row, e.context.cursor.col - 1 })
end
@@ -400,10 +400,10 @@ core.confirm = function(self, e, option, callback)
feedkeys.call('', 'n', function()
-- Apply additionalTextEdits.
local ctx = context.new()
if #(e:get_completion_item().additionalTextEdits or {}) == 0 then
if #(e.completion_item.additionalTextEdits or {}) == 0 then
e:resolve(function()
local new = context.new()
local text_edits = e:get_completion_item().additionalTextEdits or {}
local text_edits = e.completion_item.additionalTextEdits or {}
if #text_edits == 0 then
return
end
@@ -428,12 +428,12 @@ core.confirm = function(self, e, option, callback)
end)
else
vim.cmd([[silent! undojoin]])
vim.lsp.util.apply_text_edits(e:get_completion_item().additionalTextEdits, ctx.bufnr, e.source:get_position_encoding_kind())
vim.lsp.util.apply_text_edits(e.completion_item.additionalTextEdits, ctx.bufnr, e.source:get_position_encoding_kind())
end
end)
feedkeys.call('', 'n', function()
local ctx = context.new()
local completion_item = misc.copy(e:get_completion_item())
local completion_item = misc.copy(e.completion_item)
if not completion_item.textEdit then
completion_item.textEdit = {}
local insertText = completion_item.insertText
@@ -444,9 +444,9 @@ core.confirm = function(self, e, option, callback)
end
local behavior = option.behavior or config.get().confirmation.default_behavior
if behavior == types.cmp.ConfirmBehavior.Replace then
completion_item.textEdit.range = e:get_replace_range()
completion_item.textEdit.range = e.replace_range
else
completion_item.textEdit.range = e:get_insert_range()
completion_item.textEdit.range = e.insert_range
end
local diff_before = math.max(0, e.context.cursor.col - (completion_item.textEdit.range.start.character + 1))
@@ -460,15 +460,15 @@ core.confirm = function(self, e, option, callback)
if false then
--To use complex expansion debug.
vim.print({ -- luacheck: ignore
item = e:get_completion_item(),
item = e.completion_item,
diff_before = diff_before,
diff_after = diff_after,
new_text = new_text,
text_edit_new_text = completion_item.textEdit.newText,
range_start = completion_item.textEdit.range.start.character,
range_end = completion_item.textEdit.range['end'].character,
original_range_start = e:get_completion_item().textEdit.range.start.character,
original_range_end = e:get_completion_item().textEdit.range['end'].character,
original_range_start = e.completion_item.textEdit.range.start.character,
original_range_end = e.completion_item.textEdit.range['end'].character,
cursor_line = ctx.cursor_line,
cursor_col0 = ctx.cursor.col - 1,
})