mirror of
https://github.com/hrsh7th/cmp-buffer
synced 2025-04-24 20:29:54 +00:00
Merge pull request #18 from dmitmel/optimize-hidden-nested-loops
Fix O(n^2) complexity in the watcher hidden within nested loops created by table.{insert,remove}
This commit is contained in:
commit
a46e1ce308
@ -4,7 +4,8 @@
|
|||||||
---@field public length number
|
---@field public length number
|
||||||
---@field public pattern string
|
---@field public pattern string
|
||||||
---@field public timer any|nil
|
---@field public timer any|nil
|
||||||
---@field public words table<number, string[]>
|
---@field public lines_count number
|
||||||
|
---@field public lines_words table<number, string[]>
|
||||||
---@field public processing boolean
|
---@field public processing boolean
|
||||||
local buffer = {}
|
local buffer = {}
|
||||||
|
|
||||||
@ -20,7 +21,8 @@ function buffer.new(bufnr, length, pattern)
|
|||||||
self.length = length
|
self.length = length
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.timer = nil
|
self.timer = nil
|
||||||
self.words = {}
|
self.lines_count = 0
|
||||||
|
self.lines_words = {}
|
||||||
self.processing = false
|
self.processing = false
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -32,12 +34,14 @@ function buffer.close(self)
|
|||||||
self.timer:close()
|
self.timer:close()
|
||||||
self.timer = nil
|
self.timer = nil
|
||||||
end
|
end
|
||||||
self.words = {}
|
self.lines_words = {}
|
||||||
|
self.lines_count = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
---Indexing buffer
|
---Indexing buffer
|
||||||
function buffer.index(self)
|
function buffer.index(self)
|
||||||
self.processing = true
|
self.processing = true
|
||||||
|
self.lines_count = vim.api.nvim_buf_line_count(self.bufnr)
|
||||||
local index = 1
|
local index = 1
|
||||||
local lines = vim.api.nvim_buf_get_lines(self.bufnr, 0, -1, false)
|
local lines = vim.api.nvim_buf_get_lines(self.bufnr, 0, -1, false)
|
||||||
self.timer = vim.loop.new_timer()
|
self.timer = vim.loop.new_timer()
|
||||||
@ -68,32 +72,64 @@ end
|
|||||||
--- watch
|
--- watch
|
||||||
function buffer.watch(self)
|
function buffer.watch(self)
|
||||||
vim.api.nvim_buf_attach(self.bufnr, false, {
|
vim.api.nvim_buf_attach(self.bufnr, false, {
|
||||||
on_lines = vim.schedule_wrap(function(_, _, _, firstline, old_lastline, new_lastline, _, _, _)
|
-- NOTE: line indexes are 0-based and the last line is not inclusive.
|
||||||
|
on_lines = function(_, _, _, first_line, old_last_line, new_last_line, _, _, _)
|
||||||
if not vim.api.nvim_buf_is_valid(self.bufnr) then
|
if not vim.api.nvim_buf_is_valid(self.bufnr) then
|
||||||
self:close()
|
self:close()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- append
|
local delta = new_last_line - old_last_line
|
||||||
for i = old_lastline, new_lastline - 1 do
|
local new_lines_count = self.lines_count + delta
|
||||||
table.insert(self.words, i + 1, {})
|
if new_lines_count == 0 then -- clear
|
||||||
|
-- This branch protects against bugs after full-file deletion. If you
|
||||||
|
-- do, for example, gdGG, the new_last_line of the event will be zero.
|
||||||
|
-- Which is not true, a buffer always contains at least one empty line,
|
||||||
|
-- only unloaded buffers contain zero lines.
|
||||||
|
new_lines_count = 1
|
||||||
|
for i = self.lines_count, 2, -1 do
|
||||||
|
self.lines_words[i] = nil
|
||||||
end
|
end
|
||||||
|
self.lines_words[1] = {}
|
||||||
-- remove
|
elseif delta > 0 then -- append
|
||||||
for _ = new_lastline, old_lastline - 1 do
|
-- Explicitly reserve more slots in the array part of the lines table,
|
||||||
table.remove(self.words, new_lastline + 1)
|
-- all of them will be filled in the next loop, but in reverse order
|
||||||
|
-- (which is why I am concerned about preallocation). Why is there no
|
||||||
|
-- built-in function to do this in Lua???
|
||||||
|
for i = self.lines_count + 1, new_lines_count do
|
||||||
|
self.lines_words[i] = vim.NIL
|
||||||
end
|
end
|
||||||
|
-- Move forwards the unchanged elements in the tail part.
|
||||||
|
for i = self.lines_count, old_last_line + 1, -1 do
|
||||||
|
self.lines_words[i + delta] = self.lines_words[i]
|
||||||
|
end
|
||||||
|
-- Fill in new tables for the added lines.
|
||||||
|
for i = old_last_line + 1, new_last_line do
|
||||||
|
self.lines_words[i] = {}
|
||||||
|
end
|
||||||
|
elseif delta < 0 then -- remove
|
||||||
|
-- Move backwards the unchanged elements in the tail part.
|
||||||
|
for i = old_last_line + 1, self.lines_count do
|
||||||
|
self.lines_words[i + delta] = self.lines_words[i]
|
||||||
|
end
|
||||||
|
-- Remove (already copied) tables from the end, in reverse order, so
|
||||||
|
-- that we don't make holes in the lines table.
|
||||||
|
for i = self.lines_count, new_lines_count + 1, -1 do
|
||||||
|
self.lines_words[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.lines_count = new_lines_count
|
||||||
|
|
||||||
-- replace lines
|
-- replace lines
|
||||||
local lines = vim.api.nvim_buf_get_lines(self.bufnr, firstline, new_lastline, false)
|
local lines = vim.api.nvim_buf_get_lines(self.bufnr, first_line, new_last_line, true)
|
||||||
vim.api.nvim_buf_call(self.bufnr, function()
|
vim.api.nvim_buf_call(self.bufnr, function()
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
if line then
|
if line then
|
||||||
self:index_line(firstline + i, line or '')
|
self:index_line(first_line + i, line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end),
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -120,13 +156,13 @@ function buffer.index_line(self, linenr, line)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.words[linenr] = words
|
self.lines_words[linenr] = words
|
||||||
end
|
end
|
||||||
|
|
||||||
--- get_words
|
--- get_words
|
||||||
function buffer.get_words(self)
|
function buffer.get_words(self)
|
||||||
local words = {}
|
local words = {}
|
||||||
for _, line in ipairs(self.words) do
|
for _, line in ipairs(self.lines_words) do
|
||||||
for _, w in ipairs(line) do
|
for _, w in ipairs(line) do
|
||||||
table.insert(words, w)
|
table.insert(words, w)
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user