Skip to content

Commit

Permalink
refactor: improve the performane of blank line indent
Browse files Browse the repository at this point in the history
  • Loading branch information
glepnir committed Aug 25, 2024
1 parent 7315b32 commit ca30465
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 40 deletions.
79 changes: 40 additions & 39 deletions lua/indentmini/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ ffi.cdef([[
int get_indent_lnum(linenr_T lnum);
char *ml_get(linenr_T lnum);
]])

local C = ffi.C
local ml_get = C.ml_get
local find_buffer_by_handle = C.find_buffer_by_handle
local get_sw_value, get_indent_lnum = C.get_sw_value, C.get_indent_lnum

--- @class Snapshot
--- @field indent? integer
--- @field is_empty? boolean
--- @field is_tab? boolean
--- @field indent_cols? integer
--- @field line_text? string
---

--- @class Cache
--- @field snapshot Snapshot
--- @field snapshot table<integer, Snapshot>
local cache = { snapshot = {} }

--- check text only has space or tab
Expand All @@ -52,23 +52,57 @@ local function get_shiftw_value(bufnr)
return get_sw_value(handle)
end

--- store the line data in snapshot and update the blank line indent
--- @param lnum integer
--- @return Snapshot
local function make_snapshot(lnum)
local line_text = ffi.string(ml_get(lnum))
local is_empty = #line_text == 0 or only_spaces_or_tabs(line_text)
local indent = is_empty and 0 or get_indent_lnum(lnum)
if is_empty then
local prev_lnum = lnum - 1
while prev_lnum >= 1 do
if not cache.snapshot[prev_lnum] then
cache.snapshot[prev_lnum] = make_snapshot(prev_lnum)
end
local sp = cache.snapshot[prev_lnum]
if (not sp.is_empty and sp.indent == 0) or (sp.indent > 0) then
if sp.indent > 0 then
indent = sp.indent
end
break
end
prev_lnum = prev_lnum - 1
end
end

local prev = cache.snapshot[lnum - 1]
if prev and prev.is_empty and prev.indent < indent then
local prev_lnum = lnum - 1
while prev_lnum >= 1 do
local sp = cache.snapshot[prev_lnum]
if not sp or not sp.is_empty or sp.indent >= indent then
break
end
sp.indent = indent
sp.indent_cols = indent
prev_lnum = prev_lnum - 1
end
end
local indent_cols = line_text:find('[^ \t]')
indent_cols = indent_cols and indent_cols - 1 or INVALID
local indent = get_indent_lnum(lnum)
if is_empty then
indent_cols = indent
end
return {
local snapshot = {
indent = indent,
is_empty = is_empty,
is_tab = line_text:find('^\t') and true or false,
indent_cols = indent_cols,
}

cache.snapshot[lnum] = snapshot
return snapshot
end

--- @param lnum integer
Expand Down Expand Up @@ -119,44 +153,11 @@ local function update_cache_range(currow_indent)
cache.cur_inlevel = math.floor(currow_indent / cache.step)
end

--- @class Context
--- @field row integer
--- @field indent_above integer
--- @field indent_below integer
---
--- @param row integer
--- @return Context
local function init_context(row)
return {
row = row,
indent_above = INVALID,
indent_below = INVALID,
}
end

--- @param ctx Context
--- @return boolean true the row in code block otherwise not
local function row_in_code_block(ctx)
local function lookup_first_seen(_, empty)
return not empty
end
ctx.indent_above = range_in_snapshot(ctx.row - 1, UP, lookup_first_seen)
ctx.indent_below = range_in_snapshot(ctx.row + 1, DOWN, lookup_first_seen)
return not (ctx.indent_above == 0 and ctx.indent_below == 0)
end

local function on_line(_, _, bufnr, row)
local sp = find_in_snapshot(row + 1)
local ctx = init_context(row)
if (sp.is_empty and not row_in_code_block(ctx)) or out_current_range(row) then
if sp.indent == 0 or out_current_range(row) then
return
end

if sp.is_empty and sp.indent == 0 then
sp.indent = math.max(ctx.indent_above, ctx.indent_below)
sp.indent_cols = sp.indent
end

for i = 1, sp.indent - 1, cache.step do
local col = i - 1
local level = math.floor(col / cache.step) + 1
Expand Down
81 changes: 80 additions & 1 deletion test/indentmini_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require('indentmini').setup()

local same = assert.same
local channel, job_id

local function clean()
Expand Down Expand Up @@ -123,4 +123,83 @@ describe('indent mini', function()
}
assert.same(expected, screenstr)
end)

it('works on blank line case 2', function ()
local lines = {
"local function test()",
" if true then",
" if true then",
" print('hello')",
"",
" if true then",
" print('hello')",
" end",
" end",
" end",
"end"
}
local screenstr = screen(lines)
local expected = {
"local function test() ",
"│ if true then ",
"│ │ if true then ",
"│ │ │ print('hello') ",
"│ │ │ ",
"│ │ │ if true then ",
"│ │ │ │ print('hello') ",
"│ │ │ end ",
"│ │ end ",
"│ end ",
"end "
}
assert.same(expected, screenstr)
end)

it('works test case 3', function ()
local lines = {
"local opt = {",
" only_current = false,",
"}",
"",
"local function test()",
" print('hello')",
" if true then",
" print('hello')",
" end",
"end",
}
local screenstr = screen(lines)
local expected = {
"local opt = { ",
"│ only_current = false, ",
"} ",
" ",
"local function test() ",
"│ print('hello') ",
"│ if true then ",
"│ │ print('hello') ",
"│ end ",
"end "
}
same(expected, screenstr)
end)

-- it('works on non closed filetype',function()
-- local lines = {
-- "if true:",
-- " open('t', 'w') as f:",
-- " f.truncate()",
-- "",
-- "os.remove('t')"
-- }
-- local screenstr = screen(lines)
-- local expected = {
-- "if true: ",
-- "│ │ open('t', 'w') as f: ",
-- "│ │ │ │ f.truncate() ",
-- "",
-- "os.remove('t') "
-- }
-- assert.same(expected, screenstr)
-- end)
end)

0 comments on commit ca30465

Please sign in to comment.