From a42470d8051e13863a5f22ab7f2a5e4316857527 Mon Sep 17 00:00:00 2001 From: glepnir Date: Tue, 21 May 2024 15:28:26 +0800 Subject: [PATCH] improve performance close #14 --- lua/indentmini/init.lua | 53 +++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/lua/indentmini/init.lua b/lua/indentmini/init.lua index be34ecb..c79aa35 100644 --- a/lua/indentmini/init.lua +++ b/lua/indentmini/init.lua @@ -19,20 +19,14 @@ ffi.cdef([[ int get_sw_value(buf_T *buf); typedef int32_t linenr_T; int get_indent_lnum(linenr_T lnum); - char *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change); + colnr_T ml_get_len(linenr_T lnum); size_t strlen(const char *__s); ]]) local cache = { snapshot = {} } -local function line_is_empty(bufnr, lnum) - local err = ffi.new('Error') - local handle = ffi.C.find_buffer_by_handle(bufnr, err) - if lnum > cache.count then - return nil - end - local data = ffi.C.ml_get_buf(handle, lnum, false) - return tonumber(ffi.C.strlen(data)) == 0 +local function line_is_empty(lnum) + return tonumber(ffi.C.ml_get_len(lnum)) == 0 end local function get_sw_value(bufnr) @@ -50,56 +44,59 @@ local function non_or_space(row, col) return text and (#text == 0 or text == ' ') or false end -local function find_row(bufnr, row, curindent, direction, render) +local function find_in_snapshot(lnum) + if not cache.snapshot[lnum] then + cache.snapshot[lnum] = { get_indent(lnum), line_is_empty(lnum) } + end + return unpack(cache.snapshot[lnum]) +end + +local function find_row(row, curindent, direction, render) local target_row = row + direction - local snapshot = cache.snapshot while true do - local empty = line_is_empty(bufnr, target_row + 1) + if target_row < 0 or target_row > cache.count - 1 then + return INVALID + end + local target_indent, empty = find_in_snapshot(target_row + 1) if empty == nil then return INVALID end - local target_indent = snapshot[target_row + 1] or get_indent(target_row + 1) - snapshot[target_row + 1] = target_indent if target_indent == 0 and not empty and render then break elseif not empty and (render and target_indent > curindent or target_indent < curindent) then return target_row end target_row = target_row + direction - if target_row < 0 or target_row > cache.count - 1 then - return INVALID - end end return INVALID end -local function current_line_range(winid, bufnr, shiftw) +local function current_line_range(winid, shiftw) local row = api.nvim_win_get_cursor(winid)[1] - 1 local indent = get_indent(row + 1) if indent == 0 then return INVALID, INVALID, INVALID end - local top_row = find_row(bufnr, row, indent, UP, false) - local bot_row = find_row(bufnr, row, indent, DOWN, false) + local top_row = find_row(row, indent, UP, false) + local bot_row = find_row(row, indent, DOWN, false) return top_row, bot_row, math.floor(indent / shiftw) end local function on_line(_, winid, bufnr, row) - local is_empty = line_is_empty(bufnr, row + 1) + local indent, is_empty = find_in_snapshot(row + 1) if is_empty == nil then return end - local indent = cache.snapshot[row + 1] or get_indent(row + 1) local top_row, bot_row if indent == 0 and is_empty then - top_row = find_row(bufnr, row, indent, UP, true) - bot_row = find_row(bufnr, row, indent, DOWN, true) + top_row = find_row(row, indent, UP, true) + bot_row = find_row(row, indent, DOWN, true) local top_indent = top_row >= 0 and get_indent(top_row + 1) or 0 local bot_indent = bot_row >= 0 and get_indent(bot_row + 1) or 0 indent = math.max(top_indent, bot_indent) end --TODO(glepnir): should remove this or before find_row ? duplicated - local reg_srow, reg_erow, cur_inlevel = current_line_range(winid, bufnr, cache.shiftwidth) + local reg_srow, reg_erow, cur_inlevel = current_line_range(winid, cache.shiftwidth) for i = 1, indent - 1, cache.shiftwidth do local col = i - 1 local level = math.floor(col / cache.shiftwidth) + 1 @@ -109,10 +106,9 @@ local function on_line(_, winid, bufnr, row) end if col >= cache.leftcol and non_or_space(row, col) then opt.config.virt_text[1][2] = higroup - if line_is_empty and col > 0 then + if is_empty and col > 0 then opt.config.virt_text_win_col = i - 1 end - --TODO(glepnir): store id with changedtick then compare for performance buf_set_extmark(bufnr, ns, row, col, opt.config) opt.config.virt_text_win_col = nil end @@ -133,8 +129,9 @@ local function on_win(_, winid, bufnr, topline, botline) cache.leftcol = vim.fn.winsaveview().leftcol cache.shiftwidth = get_sw_value(bufnr) cache.count = api.nvim_buf_line_count(bufnr) + cache.tick = api.nvim_buf_get_changedtick(bufnr) for i = topline, botline do - cache.snapshot[i] = get_indent(i) + cache.snapshot[i + 1] = { get_indent(i + 1), line_is_empty(i + 1) } end end