From 13b549efc1f6cf9b97360a92ed5077601d9df348 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 12 Oct 2023 21:19:05 +0200 Subject: [PATCH 01/13] Prepare current_indent pull request --- lua/ibl/config.lua | 23 +++++ lua/ibl/config.types.lua | 26 +++++- lua/ibl/highlights.lua | 19 +++++ lua/ibl/init.lua | 176 ++++++++++++++++++++++++++++++++------- lua/ibl/utils.lua | 4 +- lua/ibl/virt_text.lua | 24 +++++- 6 files changed, 237 insertions(+), 35 deletions(-) diff --git a/lua/ibl/config.lua b/lua/ibl/config.lua index 9fa36595..a7f8e1e5 100644 --- a/lua/ibl/config.lua +++ b/lua/ibl/config.lua @@ -62,6 +62,11 @@ M.default_config = { }, }, }, + current_indent = { + enabled = false, + char = nil, + highlight = "IblCurrentIndent", + }, exclude = { filetypes = { "lspinfo", @@ -116,6 +121,7 @@ local validate_config = function(config) indent = { config.indent, "table", true }, whitespace = { config.whitespace, "table", true }, scope = { config.scope, "table", true }, + current_indent = { config.current_indent, "table", true }, exclude = { config.exclude, "table", true }, }, config, "ibl.config") @@ -229,6 +235,23 @@ local validate_config = function(config) end end + if config.current_indent then + utils.validate({ + enabled = { config.current_indent.enabled, "boolean", true }, + char = { config.current_indent.char, "string", true }, + highlight = { config.current_indent.highlight, "string", true } + }, config.current_indent, "ibl.config.current_indent") + if config.current_indent.char then + vim.validate { + char = { + config.current_indent.char, + validate_char, + "current_indent.char to have a display width of 0 or 1", + }, + } + end + end + if config.exclude then if config.exclude then utils.validate({ diff --git a/lua/ibl/config.types.lua b/lua/ibl/config.types.lua index eb13694b..abe81417 100644 --- a/lua/ibl/config.types.lua +++ b/lua/ibl/config.types.lua @@ -15,6 +15,8 @@ ---@field whitespace ibl.config.whitespace? --- Configures the scope ---@field scope ibl.config.scope? +--- Configures the current_indent +---@field current_indent ibl.config.current_indent? --- Configures what is excluded from indent-blankline ---@field exclude ibl.config.exclude? @@ -109,6 +111,16 @@ --- ---@field node_type table? +---@class ibl.config.current_indent +--- Enables or disables scope +---@field enabled boolean? +--- Character that gets used to display the current_indent indentation guide +--- +--- The character has to have a display width of 0 or 1 +---@field char string? +--- Highlight group that get applied to the current_indent +---@field highlight string? + ---@class ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled ---@field filetypes string[]? @@ -130,7 +142,9 @@ --- Configures the whitespace ---@field whitespace ibl.config.full.whitespace: ibl.config.whitespace --- Configures the scope ----@field scope ibl.config.full.scope: ig.config.scope +---@field scope ibl.config.full.scope: ibl.config.scope +--- Configures the current_indent +---@field current_indent ibl.config.full.current_indent: ibl.config.current_indent --- Configures what is excluded from indent-blankline ---@field exclude ibl.config.full.exclude: ibl.config.exclude @@ -225,6 +239,16 @@ --- ---@field node_type table +---@class ibl.config.full.current_indent: ibl.config.current_indent +--- Enables or disables current_indent +---@field enabled boolean +--- Character that gets used to display the current_indent indentation guide +--- +--- The character has to have a display width of 0 or 1 +---@field char string? +--- Highlight group that get applied to the current_indent +---@field highlight string + ---@class ibl.config.full.exclude: ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled ---@field filetypes string[] diff --git a/lua/ibl/highlights.lua b/lua/ibl/highlights.lua index 6bb876f6..f2f0c9ca 100644 --- a/lua/ibl/highlights.lua +++ b/lua/ibl/highlights.lua @@ -12,6 +12,8 @@ local M = { whitespace = {}, ---@type ibl.highlight[] scope = {}, + ---@type ibl.highlight[] + current_indent = {}, } ---@param name string @@ -33,9 +35,11 @@ end local setup_builtin_hl_groups = function() local whitespace_hl = get "Whitespace" local line_nr_hl = get "LineNr" + local cursor_line_nr_hl = get "CursorLineNr" local ibl_indent_hl_name = "IblIndent" local ibl_whitespace_hl_name = "IblWhitespace" local ibl_scope_hl_name = "IblScope" + local ibl_current_indent_hl_name = "IblCurrentIndent" if not_set(get(ibl_indent_hl_name)) then vim.api.nvim_set_hl(0, ibl_indent_hl_name, whitespace_hl) @@ -46,6 +50,9 @@ local setup_builtin_hl_groups = function() if not_set(get(ibl_scope_hl_name)) then vim.api.nvim_set_hl(0, ibl_scope_hl_name, line_nr_hl) end + if not_set(get(ibl_current_indent_hl_name)) then + vim.api.nvim_set_hl(0, ibl_current_indent_hl_name, cursor_line_nr_hl) + end end M.setup = function() @@ -107,6 +114,18 @@ M.setup = function() vim.api.nvim_set_hl(0, M.scope[i].char, char_hl) vim.api.nvim_set_hl(0, M.scope[i].underline, { sp = char_hl.fg, underline = true }) end + + local current_indent_highlights = config.current_indent.highlight + M.current_indent = {} + local char_hl = get(current_indent_highlights) + if not_set(char_hl) then + error(string.format("No highlight group '%s' found", current_indent_highlights)) + end + char_hl.nocombine = true + M.current_indent = { + char = "@ibl.current_indent.char", + } + vim.api.nvim_set_hl(0, M.current_indent.char, char_hl) end return M diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 76772517..3376d859 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -169,7 +169,7 @@ M.refresh = function(bufnr) end end - local left_offset, top_offset, win_end, win_height = utils.get_offset(bufnr) + local left_offset, top_offset, win_end, win_height, cursor_row = utils.get_offset(bufnr) if top_offset > win_end then return end @@ -211,7 +211,6 @@ M.refresh = function(bufnr) end local indent_state - local next_whitespace_tbl = {} local empty_line_counter = 0 local buffer_state = global_buffer_state[bufnr] @@ -244,14 +243,18 @@ M.refresh = function(bufnr) end local exact_scope_col_start = scope_col_start - ---@type ibl.indent.whitespace[] - local last_whitespace_tbl = {} + local cursor_row_stack_size = -1 + local current_indent_row_start = offset + 1 + local current_indent_row_end = range + ---@type table local line_skipped = {} + ---@type ibl.hooks.cb.skip_line[] local skip_line_hooks = hooks.get(bufnr, hooks.type.SKIP_LINE) for i, line in ipairs(lines) do local row = i + offset + line_skipped[i] = false for _, fn in pairs(skip_line_hooks) do if fn(buffer_state.tick, bufnr, row - 1, line) then line_skipped[i] = true @@ -260,8 +263,30 @@ M.refresh = function(bufnr) end end + -- a table + ---@type ibl.indent.whitespace[] + local next_whitespace_tbl = {} + + -- array of tables + ---@type table + local arr_whitespace_tbl = {} + + -- arrays of numbers + ---@type table + local current_indent_stack = {} -- current indent stack of line nr + ---@type table + local arr_whitespace_len = {} + + -- boolean arrays + ---@type table + local arr_blankline = {} + ---@type table + local arr_whitespace_only = {} + + -- in the first loop we calculate the things we need to setup the virtual text via extmarks for i, line in ipairs(lines) do local row = i + offset + if line_skipped[i] then vt.clear_buffer(bufnr, row) goto continue @@ -269,23 +294,36 @@ M.refresh = function(bufnr) local whitespace = utils.get_whitespace(line) local foldclosed = vim.fn.foldclosed(row) + if is_current_buffer and foldclosed == row then local foldtext = vim.fn.foldtextresult(row) local foldtext_whitespace = utils.get_whitespace(foldtext) if vim.fn.strdisplaywidth(foldtext_whitespace, 0) < vim.fn.strdisplaywidth(whitespace, 0) then vt.clear_buffer(bufnr, row) + line_skipped[i] = true goto continue end end if is_current_buffer and foldclosed > -1 and foldclosed + win_height < row then vt.clear_buffer(bufnr, row) + line_skipped[i] = true goto continue end ---@type ibl.indent.whitespace[] local whitespace_tbl + + local prev_indent_stack_size = 0 + if config.current_indent.enabled and indent_state then + prev_indent_stack_size = #indent_state.stack or 0 + end + local blankline = line:len() == 0 + arr_blankline[i] = blankline + + arr_whitespace_len[i] = #whitespace + arr_whitespace_only[i] = not blankline and line == whitespace -- #### calculate indent #### if not blankline then @@ -307,6 +345,7 @@ M.refresh = function(bufnr) whitespace_tbl, indent_state = indent.get(j_whitespace, indent_opts, indent_state) if utils.has_end(lines[j]) then + local last_whitespace_tbl = arr_whitespace_tbl[i - 1] or {} local trail = last_whitespace_tbl[indent_state.stack[#indent_state.stack] + 1] local trail_whitespace = last_whitespace_tbl[indent_state.stack[#indent_state.stack]] if trail then @@ -323,25 +362,6 @@ M.refresh = function(bufnr) next_whitespace_tbl = whitespace_tbl end - local scope_active = row >= scope_row_start and row <= scope_row_end - if - scope_active - and scope_col_start_single > -1 - and (whitespace_tbl[scope_col_start_single + 1] or blankline) - and not indent.is_indent(whitespace_tbl[scope_col_start_single + 1]) - then - if indent.is_space_indent(whitespace_tbl[scope_col_start_single + 1]) then - whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.INDENT - else - whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.TAB_START - end - local k = scope_col_start_single - while not whitespace_tbl[k] and k >= 0 do - whitespace_tbl[k] = indent.whitespace.SPACE - k = k - 1 - end - end - -- remove blankline trail if blankline and config.whitespace.remove_blankline_trail then while #whitespace_tbl > 0 do @@ -352,6 +372,39 @@ M.refresh = function(bufnr) end end + if config.current_indent.enabled then + local cur_indent_stack_size = 0 + if indent_state then + cur_indent_stack_size = #indent_state.stack + end + if row <= cursor_row then + if prev_indent_stack_size > cur_indent_stack_size then + -- we need to take into account that we might jump back more than + -- only one indent level at once + while prev_indent_stack_size > cur_indent_stack_size do + current_indent_stack[#current_indent_stack] = nil + prev_indent_stack_size = prev_indent_stack_size - 1 + end + elseif prev_indent_stack_size < cur_indent_stack_size then + current_indent_stack[#current_indent_stack + 1] = row + end + else + -- row > cursor_row + -- if cur_indent_stack_size >= 1, we should stop when we get to a line with no more whitespace in the whitespace_tbl + -- since that line won't have any indents to highlight + if + (cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size and (not blankline)) + or (cursor_row_stack_size >= 0 and cur_indent_stack_size >= 1 and #whitespace_tbl == 0) + then + current_indent_row_end = row - 1 + cursor_row_stack_size = -1 + end + end + if row == cursor_row then + cursor_row_stack_size = cur_indent_stack_size + end + end + -- Fix horizontal scroll local current_left_offset = left_offset while #whitespace_tbl > 0 and current_left_offset > 0 do @@ -365,13 +418,74 @@ M.refresh = function(bufnr) whitespace_tbl = fn(buffer_state.tick, bufnr, row - 1, whitespace_tbl) end - last_whitespace_tbl = whitespace_tbl + arr_whitespace_tbl[i] = whitespace_tbl + + ::continue:: + end + + -- between the two loops we find the current indent column and current indent start quickly + local find_last_indent = function(ws_tbl) + if not ws_tbl then + return 0 + end + local k = 0 + while k < #ws_tbl do + if indent.is_indent(ws_tbl[#ws_tbl - k]) then + return #ws_tbl - k + end + k = k + 1 + end + end + + local current_indent_col = -1 + if config.current_indent.enabled then + current_indent_col = find_last_indent(arr_whitespace_tbl[cursor_row - offset]) + if current_indent_stack[#current_indent_stack] then + current_indent_row_start = current_indent_stack[#current_indent_stack] + end + end + + -- set up the virtual text via extmarks + for i, line in ipairs(lines) do + if line_skipped[i] then + goto continue1 + end + + local row = i + offset + local whitespace_len = arr_whitespace_len[i] + + local blankline = arr_blankline[i] + local whitespace_tbl = arr_whitespace_tbl[i] + local whitespace_only = arr_whitespace_only[i] + + local scope_active = row >= scope_row_start and row <= scope_row_end + if + scope_active + and scope_col_start_single > -1 + and (whitespace_tbl[scope_col_start_single + 1] or blankline) + and not indent.is_indent(whitespace_tbl[scope_col_start_single + 1]) + then + if indent.is_space_indent(whitespace_tbl[scope_col_start_single + 1]) then + whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.INDENT + else + whitespace_tbl[scope_col_start_single + 1] = indent.whitespace.TAB_START + end + local k = scope_col_start_single + while not whitespace_tbl[k] and k >= 0 do + whitespace_tbl[k] = indent.whitespace.SPACE + k = k - 1 + end + end -- #### make virtual text #### local scope_start = row == scope_row_start local scope_end = row == scope_row_end + + local current_indent_active = row >= current_indent_row_start and row <= current_indent_row_end and + config.current_indent.enabled + if scope_start and scope then - scope_col_start = #whitespace + scope_col_start = whitespace_len scope_col_start_single = #whitespace_tbl scope_index = #vim.tbl_filter(function(w) return indent.is_indent(w) @@ -383,16 +497,16 @@ M.refresh = function(bufnr) end end - local whitespace_only = not blankline and line == whitespace local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) local virt_text, scope_hl = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, + current_indent_active, current_indent_col) -- #### set virtual text #### vt.clear_buffer(bufnr, row) -- Show exact scope - local scope_col_start_draw = #whitespace + local scope_col_start_draw = whitespace_len local scope_show_end_cond = #whitespace_tbl > scope_col_start_single if config.scope.show_exact_scope then @@ -408,7 +522,7 @@ M.refresh = function(bufnr) priority = config.scope.priority, strict = false, }) - inlay_hints.set(bufnr, row - 1, #whitespace, scope_hl.underline, scope_hl.underline) + inlay_hints.set(bufnr, row - 1, whitespace_len, scope_hl.underline, scope_hl.underline) end -- Scope end @@ -419,7 +533,7 @@ M.refresh = function(bufnr) priority = config.scope.priority, strict = false, }) - inlay_hints.set(bufnr, row - 1, #whitespace, scope_hl.underline, scope_hl.underline) + inlay_hints.set(bufnr, row - 1, whitespace_len, scope_hl.underline, scope_hl.underline) end for _, fn in @@ -439,7 +553,7 @@ M.refresh = function(bufnr) }) end - ::continue:: + ::continue1:: end end diff --git a/lua/ibl/utils.lua b/lua/ibl/utils.lua index addbfa34..b4e48be3 100644 --- a/lua/ibl/utils.lua +++ b/lua/ibl/utils.lua @@ -168,7 +168,7 @@ M.get_offset = function(bufnr) else local win_list = vim.fn.win_findbuf(bufnr) if not win_list or not win_list[1] then - return 0, 0, 0, 0 + return 0, 0, 0, 0, 0 end win = win_list[1] win_view = vim.api.nvim_win_call(win, vim.fn.winsaveview) @@ -183,7 +183,7 @@ M.get_offset = function(bufnr) win_end = win_view.lnum + win_height end - return win_view.leftcol or 0, win_view.topline or 0, win_end, win_height + return win_view.leftcol or 0, win_view.topline or 0, win_end, win_height, win_view.lnum or 0 end ---@param bufnr number diff --git a/lua/ibl/virt_text.lua b/lua/ibl/virt_text.lua index d59c8d9d..5ff5543c 100644 --- a/lua/ibl/virt_text.lua +++ b/lua/ibl/virt_text.lua @@ -61,9 +61,13 @@ end ---@param scope_index number ---@param scope_end boolean ---@param scope_col_start_single number +---@param current_indent_active boolean +---@param current_indent_col number ---@return ibl.virtual_text, ibl.highlight -M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) +M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, + current_indent_active, current_indent_col) local scope_hl = utils.tbl_get_index(highlights.scope, scope_index) + local current_indent_hl = highlights.current_indent.char local indent_index = 1 local virt_text = {} for i, ws in ipairs(whitespace_tbl) do @@ -71,6 +75,7 @@ M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, sc local indent_hl local underline_hl local sa = scope_active + local ca = current_indent_active local char = get_char(char_map[ws], indent_index) if indent.is_indent(ws) then @@ -89,6 +94,23 @@ M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, sc underline_hl = scope_hl.underline end + if ca and i == current_indent_col then + indent_hl = current_indent_hl + + if config.current_indent.char then + local current_indent_char = config.current_indent.char + if vim.fn.strdisplaywidth(current_indent_char or "") == 1 then + char = current_indent_char or "" + end + elseif not indent.is_indent(ws) then + if indent.is_space_indent(ws) then + char = get_char(char_map[whitespace.INDENT], indent_index) + else + char = get_char(char_map[whitespace.TAB_START], indent_index) + end + end + end + if sa and i - 1 == scope_col_start_single then indent_hl = scope_hl.char From 900f3f75eb7b80e3afe255d76d2c62a7db4c686f Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 12 Oct 2023 21:45:57 +0200 Subject: [PATCH 02/13] Added docs. --- doc/indent_blankline.txt | 68 ++++++++++++++++++++++++++++++++++++++++ lua/ibl/config.types.lua | 2 +- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/doc/indent_blankline.txt b/doc/indent_blankline.txt index 1b399e05..6de4f5ea 100644 --- a/doc/indent_blankline.txt +++ b/doc/indent_blankline.txt @@ -201,6 +201,9 @@ config *ibl.config* • {scope} (|ibl.config.scope|) Configures the scope + • {current_indent} (|ibl.config.current_indent|) + Configures the current_indent + • {exclude} (|ibl.config.exclude|) Configures what is excluded from indent-blankline @@ -211,6 +214,7 @@ config *ibl.config* indent = { char = "|" }, whitespace = { highlight = { "Whitespace", "NonText" } }, scope = { exclude = { language = { "lua" } } }, + current_indent = { enabled = true }, } < @@ -541,6 +545,70 @@ config.scope.exclude *ibl.config.scope.exclude* } < +config.current_indent *ibl.config.current_indent* + + Configures the current indent + + The current indent *is* the current indentation level (unlike scope). + + Example: ~ + + In Python, using the same example as above, current_indent will highlight the + current inner most indentation guide. With scope disabled, this will look like + this: + >python + def foo(); + if True: + ┋ a = "foo █ar" + ┋ # ↳ cursor here + print(a) +< + If we have the same code with scope enabled, it will look like this: + >python + def foo(); + ┋ if True: + ┋ ┋ a = "foo █ar" + ┋ ┋ # ↳ cursor here + ┋ print(a) +< + If you have both scope and current_indent enabled and the two overlap, the + scope will take prriority. So the Rust example from above will look identical + since the scope matches the current indent level. + (this code would not compile) + >rust + fn foo() { + if true { + ┋ let a = "foo █ar"; + ┋ // ↳ cursor here + } + print(a); + } +< + Fields: ~ + *ibl.config.current_indent.enabled* + • {enabled} (boolean) + Enables or disables current_indent + + Default: `false` ~ + + *ibl.config.current_indent.char* + • {char} (string) + Character that gets used to display the current + indents indentation guide + Each character has to have a display width + of 0 or 1 + + Default: |ibl.config.indent.char| ~ + + + *ibl.config.current_indent.highlight* + • {highlight} (string) + Highlight group that gets applied to the current + indent + + Default: |hl-IblCurrentIndent| ~ + + config.exclude *ibl.config.exclude* Configures what is excluded from indent-blankline diff --git a/lua/ibl/config.types.lua b/lua/ibl/config.types.lua index abe81417..c0b93027 100644 --- a/lua/ibl/config.types.lua +++ b/lua/ibl/config.types.lua @@ -112,7 +112,7 @@ ---@field node_type table? ---@class ibl.config.current_indent ---- Enables or disables scope +--- Enables or disables current_indent ---@field enabled boolean? --- Character that gets used to display the current_indent indentation guide --- From 5d4315bbbd87fa32432b6bcaa660eabe90f0807a Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 12 Oct 2023 22:09:33 +0200 Subject: [PATCH 03/13] Switched to 0-index + added tests --- lua/ibl/init.lua | 2 +- lua/ibl/virt_text.lua | 2 +- specs/features/virt_text_spec.lua | 100 +++++++++++++++++++++++++++--- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 3376d859..159d044b 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -431,7 +431,7 @@ M.refresh = function(bufnr) local k = 0 while k < #ws_tbl do if indent.is_indent(ws_tbl[#ws_tbl - k]) then - return #ws_tbl - k + return #ws_tbl - k - 1 end k = k + 1 end diff --git a/lua/ibl/virt_text.lua b/lua/ibl/virt_text.lua index 5ff5543c..78627676 100644 --- a/lua/ibl/virt_text.lua +++ b/lua/ibl/virt_text.lua @@ -94,7 +94,7 @@ M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, sc underline_hl = scope_hl.underline end - if ca and i == current_indent_col then + if ca and i - 1 == current_indent_col then indent_hl = current_indent_hl if config.current_indent.char then diff --git a/specs/features/virt_text_spec.lua b/specs/features/virt_text_spec.lua index 2e39c927..245373a9 100644 --- a/specs/features/virt_text_spec.lua +++ b/specs/features/virt_text_spec.lua @@ -282,9 +282,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, {}) end) @@ -305,9 +307,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -333,9 +337,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -363,9 +369,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -393,9 +401,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "e", { "@ibl.whitespace.char.1" } }, @@ -421,9 +431,11 @@ describe("virt_text", function() local scope_index = 1 local scope_end = false local scope_col_start_single = 2 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -433,6 +445,66 @@ describe("virt_text", function() }) end) + it("handles current_indent", function() + local config = conf.set_config() + highlights.setup() + local char_map = { + [TAB_START] = "a", + [TAB_START_SINGLE] = "b", + [TAB_FILL] = "c", + [TAB_END] = "d", + [SPACE] = "e", + [INDENT] = "f", + } + local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } + local scope_active = false + local scope_index = -1 + local scope_end = false + local scope_col_start_single = -1 + local current_indent_active = true + local current_indent_col = 2 + + local virt_text = + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + + assert.are.same(virt_text, { + { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, + { "e", { "@ibl.whitespace.char.1" } }, + { "f", { "@ibl.whitespace.char.1", "@ibl.current_indent.char" } }, + { "e", { "@ibl.whitespace.char.1" } }, + }) + end) + + it("handles current_indent and scope together", function() + local config = conf.set_config() + highlights.setup() + local char_map = { + [TAB_START] = "a", + [TAB_START_SINGLE] = "b", + [TAB_FILL] = "c", + [TAB_END] = "d", + [SPACE] = "e", + [INDENT] = "f", + } + local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } + local scope_active = true + local scope_index = 1 + local scope_end = false + local scope_col_start_single = 0 + local current_indent_active = true + local current_indent_col = 2 + + local virt_text = + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + + assert.are.same(virt_text, { + { "f", { "@ibl.whitespace.char.1", "@ibl.scope.char.1" } }, + { "e", { "@ibl.whitespace.char.1" } }, + { "f", { "@ibl.whitespace.char.1", "@ibl.current_indent.char" } }, + { "e", { "@ibl.whitespace.char.1" } }, + }) + end) + it("handles tabs", function() local config = conf.set_config() highlights.setup() @@ -449,9 +521,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -481,9 +555,11 @@ describe("virt_text", function() local scope_index = -1 local scope_end = false local scope_col_start_single = 0 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -515,9 +591,11 @@ describe("virt_text", function() local scope_index = 2 local scope_end = false local scope_col_start_single = 2 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -549,9 +627,11 @@ describe("virt_text", function() local scope_index = 2 local scope_end = true local scope_col_start_single = 2 + local current_indent_active = false + local current_indent_col = -1 local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single) + vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, From fb9497424581a50737f17eeeb6a08a1a70993284 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 12 Oct 2023 22:13:38 +0200 Subject: [PATCH 04/13] Multiple stylua fixes --- lua/ibl/config.lua | 2 +- lua/ibl/init.lua | 26 +++-- lua/ibl/virt_text.lua | 13 ++- specs/features/virt_text_spec.lua | 156 +++++++++++++++++++++++++----- 4 files changed, 163 insertions(+), 34 deletions(-) diff --git a/lua/ibl/config.lua b/lua/ibl/config.lua index a7f8e1e5..499e112d 100644 --- a/lua/ibl/config.lua +++ b/lua/ibl/config.lua @@ -239,7 +239,7 @@ local validate_config = function(config) utils.validate({ enabled = { config.current_indent.enabled, "boolean", true }, char = { config.current_indent.char, "string", true }, - highlight = { config.current_indent.highlight, "string", true } + highlight = { config.current_indent.highlight, "string", true }, }, config.current_indent, "ibl.config.current_indent") if config.current_indent.char then vim.validate { diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 159d044b..0ad841d4 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -393,7 +393,7 @@ M.refresh = function(bufnr) -- if cur_indent_stack_size >= 1, we should stop when we get to a line with no more whitespace in the whitespace_tbl -- since that line won't have any indents to highlight if - (cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size and (not blankline)) + (cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size and not blankline) or (cursor_row_stack_size >= 0 and cur_indent_stack_size >= 1 and #whitespace_tbl == 0) then current_indent_row_end = row - 1 @@ -418,7 +418,10 @@ M.refresh = function(bufnr) whitespace_tbl = fn(buffer_state.tick, bufnr, row - 1, whitespace_tbl) end - arr_whitespace_tbl[i] = whitespace_tbl + -- this do block is purely to help stylua with the scope + do + arr_whitespace_tbl[i] = whitespace_tbl + end ::continue:: end @@ -481,8 +484,9 @@ M.refresh = function(bufnr) local scope_start = row == scope_row_start local scope_end = row == scope_row_end - local current_indent_active = row >= current_indent_row_start and row <= current_indent_row_end and - config.current_indent.enabled + local current_indent_active = row >= current_indent_row_start + and row <= current_indent_row_end + and config.current_indent.enabled if scope_start and scope then scope_col_start = whitespace_len @@ -498,9 +502,17 @@ M.refresh = function(bufnr) end local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) - local virt_text, scope_hl = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, - current_indent_active, current_indent_col) + local virt_text, scope_hl = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) -- #### set virtual text #### vt.clear_buffer(bufnr, row) diff --git a/lua/ibl/virt_text.lua b/lua/ibl/virt_text.lua index 78627676..5ff58f4c 100644 --- a/lua/ibl/virt_text.lua +++ b/lua/ibl/virt_text.lua @@ -64,8 +64,17 @@ end ---@param current_indent_active boolean ---@param current_indent_col number ---@return ibl.virtual_text, ibl.highlight -M.get = function(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, - current_indent_active, current_indent_col) +M.get = function( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col +) local scope_hl = utils.tbl_get_index(highlights.scope, scope_index) local current_indent_hl = highlights.current_indent.char local indent_index = 1 diff --git a/specs/features/virt_text_spec.lua b/specs/features/virt_text_spec.lua index 245373a9..04d39d05 100644 --- a/specs/features/virt_text_spec.lua +++ b/specs/features/virt_text_spec.lua @@ -285,8 +285,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, {}) end) @@ -310,8 +319,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -340,8 +358,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -372,8 +399,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -404,8 +440,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "e", { "@ibl.whitespace.char.1" } }, @@ -434,8 +479,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -464,8 +518,17 @@ describe("virt_text", function() local current_indent_active = true local current_indent_col = 2 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -494,8 +557,17 @@ describe("virt_text", function() local current_indent_active = true local current_indent_col = 2 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.scope.char.1" } }, @@ -524,8 +596,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "a", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -558,8 +639,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -594,8 +684,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, @@ -630,8 +729,17 @@ describe("virt_text", function() local current_indent_active = false local current_indent_col = -1 - local virt_text = - vt.get(config, char_map, whitespace_tbl, scope_active, scope_index, scope_end, scope_col_start_single, current_indent_active, current_indent_col) + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) assert.are.same(virt_text, { { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, From 863cfc30f8d82e09a2048744b69ed7f47e98b263 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 17:26:29 +0200 Subject: [PATCH 05/13] Fix bug with some blank lines (add drawn_indents) --- lua/ibl/indent.lua | 8 ++++-- lua/ibl/init.lua | 64 ++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/lua/ibl/indent.lua b/lua/ibl/indent.lua index 0e394719..ca33eb4c 100644 --- a/lua/ibl/indent.lua +++ b/lua/ibl/indent.lua @@ -25,7 +25,7 @@ M.whitespace = { ---@param whitespace string ---@param opts ibl.indent_options ---@param indent_state ibl.indent_state? ----@return ibl.indent.whitespace[], ibl.indent_state +---@return ibl.indent.whitespace[], ibl.indent_state, number[] M.get = function(whitespace, opts, indent_state) if not indent_state then indent_state = { cap = false, stack = {} } @@ -45,7 +45,9 @@ M.get = function(whitespace, opts, indent_state) if shiftwidth == 0 then shiftwidth = tabstop end + local whitespace_tbl = {} + local drawn_indents = {} for ch in whitespace:gmatch "." do if ch == "\t" then @@ -76,10 +78,12 @@ M.get = function(whitespace, opts, indent_state) local mod = (spaces + tabs + extra) % shiftwidth if vim.tbl_contains(indent_state.stack, spaces + tabs) then table.insert(whitespace_tbl, M.whitespace.INDENT) + table.insert(drawn_indents, #whitespace_tbl - 1) extra = extra + mod elseif mod == 0 then if #whitespace_tbl < indent_cap or not opts.smart_indent_cap then table.insert(whitespace_tbl, M.whitespace.INDENT) + table.insert(drawn_indents, #whitespace_tbl - 1) extra = extra + mod else indent_state.cap = true @@ -97,7 +101,7 @@ M.get = function(whitespace, opts, indent_state) end, indent_state.stack) table.insert(indent_state.stack, spaces + tabs) - return whitespace_tbl, indent_state + return whitespace_tbl, indent_state, drawn_indents end --- Returns true if the passed whitespace is an indent diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 0ad841d4..84b6fb42 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -263,7 +263,15 @@ M.refresh = function(bufnr) end end - -- a table + ---@type number[] + local next_drawn_indents + + ---@type number + local cur_indent_stack_size = 0 + + ---@type number + local current_indent_col = -1 + ---@type ibl.indent.whitespace[] local next_whitespace_tbl = {} @@ -314,10 +322,8 @@ M.refresh = function(bufnr) ---@type ibl.indent.whitespace[] local whitespace_tbl - local prev_indent_stack_size = 0 - if config.current_indent.enabled and indent_state then - prev_indent_stack_size = #indent_state.stack or 0 - end + ---@type number[] + local drawn_indents local blankline = line:len() == 0 arr_blankline[i] = blankline @@ -327,10 +333,11 @@ M.refresh = function(bufnr) -- #### calculate indent #### if not blankline then - whitespace_tbl, indent_state = indent.get(whitespace, indent_opts, indent_state) + whitespace_tbl, indent_state, drawn_indents = indent.get(whitespace, indent_opts, indent_state) elseif empty_line_counter > 0 then empty_line_counter = empty_line_counter - 1 whitespace_tbl = next_whitespace_tbl + drawn_indents = next_drawn_indents else if i == #lines then whitespace_tbl = {} @@ -342,7 +349,7 @@ M.refresh = function(bufnr) end local j_whitespace = utils.get_whitespace(lines[j]) - whitespace_tbl, indent_state = indent.get(j_whitespace, indent_opts, indent_state) + whitespace_tbl, indent_state, drawn_indents = indent.get(j_whitespace, indent_opts, indent_state) if utils.has_end(lines[j]) then local last_whitespace_tbl = arr_whitespace_tbl[i - 1] or {} @@ -350,9 +357,11 @@ M.refresh = function(bufnr) local trail_whitespace = last_whitespace_tbl[indent_state.stack[#indent_state.stack]] if trail then table.insert(whitespace_tbl, trail) + table.insert(drawn_indents, indent_state.stack[#indent_state.stack]) elseif trail_whitespace then if indent.is_space_indent(trail_whitespace) then table.insert(whitespace_tbl, indent.whitespace.INDENT) + table.insert(drawn_indents, indent_state.stack[#indent_state.stack]) else table.insert(whitespace_tbl, indent.whitespace.TAB_START) end @@ -360,6 +369,7 @@ M.refresh = function(bufnr) end end next_whitespace_tbl = whitespace_tbl + next_drawn_indents = drawn_indents end -- remove blankline trail @@ -373,9 +383,10 @@ M.refresh = function(bufnr) end if config.current_indent.enabled then - local cur_indent_stack_size = 0 - if indent_state then - cur_indent_stack_size = #indent_state.stack + local prev_indent_stack_size = cur_indent_stack_size + cur_indent_stack_size = 0 + if drawn_indents then + cur_indent_stack_size = #drawn_indents end if row <= cursor_row then if prev_indent_stack_size > cur_indent_stack_size then @@ -390,11 +401,8 @@ M.refresh = function(bufnr) end else -- row > cursor_row - -- if cur_indent_stack_size >= 1, we should stop when we get to a line with no more whitespace in the whitespace_tbl - -- since that line won't have any indents to highlight if - (cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size and not blankline) - or (cursor_row_stack_size >= 0 and cur_indent_stack_size >= 1 and #whitespace_tbl == 0) + cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size then current_indent_row_end = row - 1 cursor_row_stack_size = -1 @@ -402,6 +410,12 @@ M.refresh = function(bufnr) end if row == cursor_row then cursor_row_stack_size = cur_indent_stack_size + if drawn_indents[#drawn_indents] then + current_indent_col = drawn_indents[#drawn_indents] + end + if current_indent_stack[#current_indent_stack] then + current_indent_row_start = current_indent_stack[#current_indent_stack] + end end end @@ -426,28 +440,6 @@ M.refresh = function(bufnr) ::continue:: end - -- between the two loops we find the current indent column and current indent start quickly - local find_last_indent = function(ws_tbl) - if not ws_tbl then - return 0 - end - local k = 0 - while k < #ws_tbl do - if indent.is_indent(ws_tbl[#ws_tbl - k]) then - return #ws_tbl - k - 1 - end - k = k + 1 - end - end - - local current_indent_col = -1 - if config.current_indent.enabled then - current_indent_col = find_last_indent(arr_whitespace_tbl[cursor_row - offset]) - if current_indent_stack[#current_indent_stack] then - current_indent_row_start = current_indent_stack[#current_indent_stack] - end - end - -- set up the virtual text via extmarks for i, line in ipairs(lines) do if line_skipped[i] then From 51b984e2b6b7d28841ee08b93f238af95f48bba9 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 23:05:09 +0200 Subject: [PATCH 06/13] Added show_start, show_end and priority --- doc/indent_blankline.txt | 26 +++++++++- lua/ibl/config.lua | 6 +++ lua/ibl/config.types.lua | 12 +++++ lua/ibl/highlights.lua | 2 + lua/ibl/init.lua | 27 ++++++++++- lua/ibl/virt_text.lua | 81 ++++++++++++++++++++++--------- specs/features/indent_spec.lua | 22 ++++----- specs/features/virt_text_spec.lua | 40 +++++++++++++++ 8 files changed, 179 insertions(+), 37 deletions(-) diff --git a/doc/indent_blankline.txt b/doc/indent_blankline.txt index 6de4f5ea..1114426a 100644 --- a/doc/indent_blankline.txt +++ b/doc/indent_blankline.txt @@ -572,8 +572,10 @@ config.current_indent *ibl.config.current_indent* ┋ print(a) < If you have both scope and current_indent enabled and the two overlap, the - scope will take prriority. So the Rust example from above will look identical - since the scope matches the current indent level. + scope will take priority by default. So the Rust example from above will look + identical since the scope matches the current indent level. To make + current_indent take priority, make sure that its priority is set to a higher + value than scope's priority. (this code would not compile) >rust fn foo() { @@ -608,6 +610,26 @@ config.current_indent *ibl.config.current_indent* Default: |hl-IblCurrentIndent| ~ + *ibl.config.current_indent.show_start* + • {show_start} (boolean) + Shows an underline on the line above the current + indent level + + Default: `true` ~ + + *ibl.config.current_indent.show_end* + • {show_end} (boolean) + Shows an underline on the line below the current + indent level + + Default: `true` ~ + + *ibl.config.current_indent.priority* + • {priority} (number) + Virtual text priority for the current_indent + + Default: `64` ~ + config.exclude *ibl.config.exclude* diff --git a/lua/ibl/config.lua b/lua/ibl/config.lua index 499e112d..2948b2c3 100644 --- a/lua/ibl/config.lua +++ b/lua/ibl/config.lua @@ -66,6 +66,9 @@ M.default_config = { enabled = false, char = nil, highlight = "IblCurrentIndent", + show_start = false, + show_end = false, + priority = 64, }, exclude = { filetypes = { @@ -240,6 +243,9 @@ local validate_config = function(config) enabled = { config.current_indent.enabled, "boolean", true }, char = { config.current_indent.char, "string", true }, highlight = { config.current_indent.highlight, "string", true }, + show_start = { config.current_indent.show_start, "boolean", true }, + show_end = { config.current_indent.show_end, "boolean", true }, + priority = { config.current_indent.priority, "number", true }, }, config.current_indent, "ibl.config.current_indent") if config.current_indent.char then vim.validate { diff --git a/lua/ibl/config.types.lua b/lua/ibl/config.types.lua index c0b93027..2ec96583 100644 --- a/lua/ibl/config.types.lua +++ b/lua/ibl/config.types.lua @@ -120,6 +120,12 @@ ---@field char string? --- Highlight group that get applied to the current_indent ---@field highlight string? +--- Shows an underline on the first line of the current_indent +---@field show_start boolean? +--- Shows an underline on the last line of the current_indent +---@field show_end boolean? +--- Virtual text priority for the current_indent +---@field priority number? ---@class ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled @@ -248,6 +254,12 @@ ---@field char string? --- Highlight group that get applied to the current_indent ---@field highlight string +--- Shows an underline on the first line of the current_indent +---@field show_start boolean +--- Shows an underline on the last line of the current_indent +---@field show_end boolean +--- Virtual text priority for the current_indent +---@field priority number ---@class ibl.config.full.exclude: ibl.config.exclude --- List of `filetypes` for which indent-blankline is disabled diff --git a/lua/ibl/highlights.lua b/lua/ibl/highlights.lua index f2f0c9ca..d8057bce 100644 --- a/lua/ibl/highlights.lua +++ b/lua/ibl/highlights.lua @@ -124,8 +124,10 @@ M.setup = function() char_hl.nocombine = true M.current_indent = { char = "@ibl.current_indent.char", + underline = "@ibl.current_indent.underline" } vim.api.nvim_set_hl(0, M.current_indent.char, char_hl) + vim.api.nvim_set_hl(0, M.current_indent.underline, { sp = char_hl.fg, underline = true }) end return M diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 84b6fb42..645b4f58 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -476,6 +476,9 @@ M.refresh = function(bufnr) local scope_start = row == scope_row_start local scope_end = row == scope_row_end + local current_indent_start = row == current_indent_row_start - 1 + local current_indent_end = row == current_indent_row_end + 1 + local current_indent_active = row >= current_indent_row_start and row <= current_indent_row_end and config.current_indent.enabled @@ -494,7 +497,7 @@ M.refresh = function(bufnr) end local char_map = vt.get_char_map(config, listchars, whitespace_only, blankline) - local virt_text, scope_hl = vt.get( + local virt_text, scope_hl, current_indent_hl = vt.get( config, char_map, whitespace_tbl, @@ -540,6 +543,28 @@ M.refresh = function(bufnr) inlay_hints.set(bufnr, row - 1, whitespace_len, scope_hl.underline, scope_hl.underline) end + -- current_indent start + if config.current_indent.show_start and current_indent_start then + vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, current_indent_col, { + end_col = #line, + hl_group = current_indent_hl.underline, + priority = config.current_indent.priority, + strict = false, + }) + inlay_hints.set(bufnr, row - 1, whitespace_len, current_indent_hl.underline, current_indent_hl.underline) + end + + -- current_indent end + if config.current_indent.show_end and current_indent_end and #whitespace_tbl >= current_indent_col then + vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, current_indent_col, { + end_col = #line, + hl_group = current_indent_hl.underline, + priority = config.current_indent.priority, + strict = false, + }) + inlay_hints.set(bufnr, row - 1, whitespace_len, current_indent_hl.underline, current_indent_hl.underline) + end + for _, fn in pairs(hooks.get(bufnr, hooks.type.VIRTUAL_TEXT) --[[ @as ibl.hooks.cb.virtual_text[] ]]) do diff --git a/lua/ibl/virt_text.lua b/lua/ibl/virt_text.lua index 5ff58f4c..bead6dab 100644 --- a/lua/ibl/virt_text.lua +++ b/lua/ibl/virt_text.lua @@ -63,7 +63,7 @@ end ---@param scope_col_start_single number ---@param current_indent_active boolean ---@param current_indent_col number ----@return ibl.virtual_text, ibl.highlight +---@return ibl.virtual_text, ibl.highlight, ibl.highlight M.get = function( config, char_map, @@ -76,7 +76,7 @@ M.get = function( current_indent_col ) local scope_hl = utils.tbl_get_index(highlights.scope, scope_index) - local current_indent_hl = highlights.current_indent.char + local current_indent_hl = highlights.current_indent local indent_index = 1 local virt_text = {} for i, ws in ipairs(whitespace_tbl) do @@ -103,35 +103,70 @@ M.get = function( underline_hl = scope_hl.underline end - if ca and i - 1 == current_indent_col then - indent_hl = current_indent_hl + if config.scope.priority >= config.current_indent.priority then + if ca and i - 1 == current_indent_col then + indent_hl = current_indent_hl.char - if config.current_indent.char then - local current_indent_char = config.current_indent.char - if vim.fn.strdisplaywidth(current_indent_char or "") == 1 then - char = current_indent_char or "" + if config.current_indent.char then + local current_indent_char = config.current_indent.char + if vim.fn.strdisplaywidth(current_indent_char or "") == 1 then + char = current_indent_char or "" + end + elseif not indent.is_indent(ws) then + if indent.is_space_indent(ws) then + char = get_char(char_map[whitespace.INDENT], indent_index) + else + char = get_char(char_map[whitespace.TAB_START], indent_index) + end end - elseif not indent.is_indent(ws) then - if indent.is_space_indent(ws) then - char = get_char(char_map[whitespace.INDENT], indent_index) - else - char = get_char(char_map[whitespace.TAB_START], indent_index) + end + + if sa and i - 1 == scope_col_start_single then + indent_hl = scope_hl.char + + if config.scope.char then + local scope_char = get_char(config.scope.char, scope_index) + if vim.fn.strdisplaywidth(scope_char) == 1 then + char = scope_char + end + end + + if config.scope.show_end and scope_end then + underline_hl = scope_hl.underline end end - end + else + -- config.scope.priority < config.current_indent.priority + if sa and i - 1 == scope_col_start_single then + indent_hl = scope_hl.char - if sa and i - 1 == scope_col_start_single then - indent_hl = scope_hl.char + if config.scope.char then + local scope_char = get_char(config.scope.char, scope_index) + if vim.fn.strdisplaywidth(scope_char) == 1 then + char = scope_char + end + end - if config.scope.char then - local scope_char = get_char(config.scope.char, scope_index) - if vim.fn.strdisplaywidth(scope_char) == 1 then - char = scope_char + if config.scope.show_end and scope_end then + underline_hl = scope_hl.underline end end - if config.scope.show_end and scope_end then - underline_hl = scope_hl.underline + if ca and i - 1 == current_indent_col then + indent_hl = current_indent_hl.char + + if config.current_indent.char then + local current_indent_char = config.current_indent.char + if vim.fn.strdisplaywidth(current_indent_char or "") == 1 then + char = current_indent_char or "" + end + elseif not indent.is_indent(ws) then + if indent.is_space_indent(ws) then + char = get_char(char_map[whitespace.INDENT], indent_index) + else + char = get_char(char_map[whitespace.TAB_START], indent_index) + end + end end end @@ -143,7 +178,7 @@ M.get = function( }) end - return virt_text, scope_hl + return virt_text, scope_hl, current_indent_hl end return M diff --git a/specs/features/indent_spec.lua b/specs/features/indent_spec.lua index 6c451987..49ec84de 100644 --- a/specs/features/indent_spec.lua +++ b/specs/features/indent_spec.lua @@ -19,40 +19,40 @@ describe("indent", function() end) it("no whitespace", function() - local whitespace_tbl, _ = indent.get("", opts) + local whitespace_tbl, _, _ = indent.get("", opts) assert.are.same(whitespace_tbl, {}) end) it("normal space indentation", function() - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE }) end) it("normal tab", function() - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START, TAB_FILL, TAB_FILL, TAB_END }) end) it("single width tab", function() opts.tabstop = 1 - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START_SINGLE }) end) it("double width tab", function() opts.tabstop = 2 - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START, TAB_END }) end) it("vartabstop", function() opts.vartabstop = "1,3" - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same( whitespace_tbl, @@ -61,33 +61,33 @@ describe("indent", function() end) it("mix of tabs and spaces", function() - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, TAB_START, TAB_END, SPACE, TAB_START_SINGLE }) end) it("mix of tabs and spaces with vartabstop", function() opts.vartabstop = "1,3" - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START_SINGLE, SPACE, TAB_START, TAB_END, SPACE }) end) it("caps after first indent after last item in stack", function() - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, SPACE, SPACE }) end) it("caps after first indent of first item in stack when cap is true", function() - local whitespace_tbl, _ = indent.get(" ", opts, { cap = true, stack = { 0, 4 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = true, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, SPACE, SPACE, INDENT, SPACE, SPACE, SPACE }) end) it("doesn't cap with smart_indent_cap off", function() opts.smart_indent_cap = false - local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) + local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, INDENT, SPACE }) end) diff --git a/specs/features/virt_text_spec.lua b/specs/features/virt_text_spec.lua index 04d39d05..2fcad40d 100644 --- a/specs/features/virt_text_spec.lua +++ b/specs/features/virt_text_spec.lua @@ -577,6 +577,46 @@ describe("virt_text", function() }) end) + it("handles current_indent and scope with priority in mind", function() + local config = conf.set_config { current_indent = { priority = 2000 } } + highlights.setup() + local char_map = { + [TAB_START] = "a", + [TAB_START_SINGLE] = "b", + [TAB_FILL] = "c", + [TAB_END] = "d", + [SPACE] = "e", + [INDENT] = "f", + } + local whitespace_tbl = { INDENT, SPACE, INDENT, SPACE } + local scope_active = true + local scope_index = 1 + local scope_end = false + local scope_col_start_single = 0 + local current_indent_active = true + local current_indent_col = 2 + + local virt_text = vt.get( + config, + char_map, + whitespace_tbl, + scope_active, + scope_index, + scope_end, + scope_col_start_single, + current_indent_active, + current_indent_col + ) + + assert.are.same(virt_text, { + { "f", { "@ibl.whitespace.char.1", "@ibl.current_indent.char" } }, + { "e", { "@ibl.whitespace.char.1" } }, + { "f", { "@ibl.whitespace.char.1", "@ibl.indent.char.1" } }, + { "e", { "@ibl.whitespace.char.1" } }, + }) + end) + + it("handles tabs", function() local config = conf.set_config() highlights.setup() From b9a2b6326c24138504433abc8ab937abf549c1f7 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 23:09:30 +0200 Subject: [PATCH 07/13] Cleanup --- lua/ibl/highlights.lua | 2 +- lua/ibl/init.lua | 5 ++--- specs/features/virt_text_spec.lua | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lua/ibl/highlights.lua b/lua/ibl/highlights.lua index d8057bce..b2203f17 100644 --- a/lua/ibl/highlights.lua +++ b/lua/ibl/highlights.lua @@ -124,7 +124,7 @@ M.setup = function() char_hl.nocombine = true M.current_indent = { char = "@ibl.current_indent.char", - underline = "@ibl.current_indent.underline" + underline = "@ibl.current_indent.underline", } vim.api.nvim_set_hl(0, M.current_indent.char, char_hl) vim.api.nvim_set_hl(0, M.current_indent.underline, { sp = char_hl.fg, underline = true }) diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 645b4f58..c9a03816 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -388,6 +388,7 @@ M.refresh = function(bufnr) if drawn_indents then cur_indent_stack_size = #drawn_indents end + if row <= cursor_row then if prev_indent_stack_size > cur_indent_stack_size then -- we need to take into account that we might jump back more than @@ -401,9 +402,7 @@ M.refresh = function(bufnr) end else -- row > cursor_row - if - cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size - then + if cursor_row_stack_size >= 0 and cursor_row_stack_size > cur_indent_stack_size then current_indent_row_end = row - 1 cursor_row_stack_size = -1 end diff --git a/specs/features/virt_text_spec.lua b/specs/features/virt_text_spec.lua index 2fcad40d..06e2ff1b 100644 --- a/specs/features/virt_text_spec.lua +++ b/specs/features/virt_text_spec.lua @@ -594,7 +594,7 @@ describe("virt_text", function() local scope_end = false local scope_col_start_single = 0 local current_indent_active = true - local current_indent_col = 2 + local current_indent_col = 0 local virt_text = vt.get( config, From 85b10a56555e75f1767b61defd71e145ce2c0e4e Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 23:11:32 +0200 Subject: [PATCH 08/13] Stylua fix --- lua/ibl/init.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index c9a03816..4f0d3171 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -385,9 +385,7 @@ M.refresh = function(bufnr) if config.current_indent.enabled then local prev_indent_stack_size = cur_indent_stack_size cur_indent_stack_size = 0 - if drawn_indents then - cur_indent_stack_size = #drawn_indents - end + if drawn_indents then cur_indent_stack_size = #drawn_indents end if row <= cursor_row then if prev_indent_stack_size > cur_indent_stack_size then From 5d449f89bff5c83d5edef62c75e76f745c34e0ef Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 23:12:55 +0200 Subject: [PATCH 09/13] Stylua retry --- lua/ibl/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 4f0d3171..f40cd69b 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -385,7 +385,10 @@ M.refresh = function(bufnr) if config.current_indent.enabled then local prev_indent_stack_size = cur_indent_stack_size cur_indent_stack_size = 0 - if drawn_indents then cur_indent_stack_size = #drawn_indents end + + if drawn_indents then + cur_indent_stack_size = #drawn_indents + end if row <= cursor_row then if prev_indent_stack_size > cur_indent_stack_size then From ccf3361664540f5f7ac3fef6a6379e878660f2cb Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 13 Oct 2023 23:14:19 +0200 Subject: [PATCH 10/13] Even more stylua... --- specs/features/virt_text_spec.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/features/virt_text_spec.lua b/specs/features/virt_text_spec.lua index 06e2ff1b..e3c173d4 100644 --- a/specs/features/virt_text_spec.lua +++ b/specs/features/virt_text_spec.lua @@ -616,7 +616,6 @@ describe("virt_text", function() }) end) - it("handles tabs", function() local config = conf.set_config() highlights.setup() From 3fa6ee67cb763180e6b0ff12e8c341c4ab425596 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Sat, 14 Oct 2023 00:22:10 +0200 Subject: [PATCH 11/13] drawn_indents counts TAB_STARTs too --- lua/ibl/indent.lua | 2 ++ lua/ibl/init.lua | 1 + 2 files changed, 3 insertions(+) diff --git a/lua/ibl/indent.lua b/lua/ibl/indent.lua index ca33eb4c..bbe3bb98 100644 --- a/lua/ibl/indent.lua +++ b/lua/ibl/indent.lua @@ -63,8 +63,10 @@ M.get = function(whitespace, opts, indent_state) if tab_width == 1 then table.insert(whitespace_tbl, M.whitespace.TAB_START_SINGLE) + table.insert(drawn_indents, #whitespace_tbl - 1) else table.insert(whitespace_tbl, M.whitespace.TAB_START) + table.insert(drawn_indents, #whitespace_tbl - 1) end for i = 2, tab_width do diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index f40cd69b..2c5610f7 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -364,6 +364,7 @@ M.refresh = function(bufnr) table.insert(drawn_indents, indent_state.stack[#indent_state.stack]) else table.insert(whitespace_tbl, indent.whitespace.TAB_START) + table.insert(drawn_indents, #whitespace_tbl - 1) end end end From b71ecd25e7e1e6251701b3a517a868164c199d50 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Tue, 17 Oct 2023 14:44:44 +0200 Subject: [PATCH 12/13] Use whitespace_tbl instead of drawn_indents --- lua/ibl/indent.lua | 9 ++---- lua/ibl/init.lua | 54 +++++++++++++++++++++------------- specs/features/indent_spec.lua | 22 +++++++------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/lua/ibl/indent.lua b/lua/ibl/indent.lua index bbe3bb98..1c30f0b7 100644 --- a/lua/ibl/indent.lua +++ b/lua/ibl/indent.lua @@ -25,7 +25,7 @@ M.whitespace = { ---@param whitespace string ---@param opts ibl.indent_options ---@param indent_state ibl.indent_state? ----@return ibl.indent.whitespace[], ibl.indent_state, number[] +---@return ibl.indent.whitespace[], ibl.indent_state M.get = function(whitespace, opts, indent_state) if not indent_state then indent_state = { cap = false, stack = {} } @@ -47,7 +47,6 @@ M.get = function(whitespace, opts, indent_state) end local whitespace_tbl = {} - local drawn_indents = {} for ch in whitespace:gmatch "." do if ch == "\t" then @@ -63,10 +62,8 @@ M.get = function(whitespace, opts, indent_state) if tab_width == 1 then table.insert(whitespace_tbl, M.whitespace.TAB_START_SINGLE) - table.insert(drawn_indents, #whitespace_tbl - 1) else table.insert(whitespace_tbl, M.whitespace.TAB_START) - table.insert(drawn_indents, #whitespace_tbl - 1) end for i = 2, tab_width do @@ -80,12 +77,10 @@ M.get = function(whitespace, opts, indent_state) local mod = (spaces + tabs + extra) % shiftwidth if vim.tbl_contains(indent_state.stack, spaces + tabs) then table.insert(whitespace_tbl, M.whitespace.INDENT) - table.insert(drawn_indents, #whitespace_tbl - 1) extra = extra + mod elseif mod == 0 then if #whitespace_tbl < indent_cap or not opts.smart_indent_cap then table.insert(whitespace_tbl, M.whitespace.INDENT) - table.insert(drawn_indents, #whitespace_tbl - 1) extra = extra + mod else indent_state.cap = true @@ -103,7 +98,7 @@ M.get = function(whitespace, opts, indent_state) end, indent_state.stack) table.insert(indent_state.stack, spaces + tabs) - return whitespace_tbl, indent_state, drawn_indents + return whitespace_tbl, indent_state end --- Returns true if the passed whitespace is an indent diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index 2c5610f7..b217e5e4 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -263,8 +263,26 @@ M.refresh = function(bufnr) end end - ---@type number[] - local next_drawn_indents + local get_indent_details = function(ws_tbl) + local number_of_indents = 0 + local last_indent = -1 + if not ws_tbl then + return number_of_indents, last_indent + end + local k = 0 + local has_seen_indent = false + while k < #ws_tbl do + if indent.is_indent(ws_tbl[#ws_tbl - k]) then + if not has_seen_indent then + has_seen_indent = true + last_indent = #ws_tbl - k - 1 + end + number_of_indents = number_of_indents + 1 + end + k = k + 1 + end + return number_of_indents, last_indent + end ---@type number local cur_indent_stack_size = 0 @@ -322,9 +340,6 @@ M.refresh = function(bufnr) ---@type ibl.indent.whitespace[] local whitespace_tbl - ---@type number[] - local drawn_indents - local blankline = line:len() == 0 arr_blankline[i] = blankline @@ -333,11 +348,10 @@ M.refresh = function(bufnr) -- #### calculate indent #### if not blankline then - whitespace_tbl, indent_state, drawn_indents = indent.get(whitespace, indent_opts, indent_state) + whitespace_tbl, indent_state = indent.get(whitespace, indent_opts, indent_state) elseif empty_line_counter > 0 then empty_line_counter = empty_line_counter - 1 whitespace_tbl = next_whitespace_tbl - drawn_indents = next_drawn_indents else if i == #lines then whitespace_tbl = {} @@ -349,7 +363,7 @@ M.refresh = function(bufnr) end local j_whitespace = utils.get_whitespace(lines[j]) - whitespace_tbl, indent_state, drawn_indents = indent.get(j_whitespace, indent_opts, indent_state) + whitespace_tbl, indent_state = indent.get(j_whitespace, indent_opts, indent_state) if utils.has_end(lines[j]) then local last_whitespace_tbl = arr_whitespace_tbl[i - 1] or {} @@ -357,20 +371,16 @@ M.refresh = function(bufnr) local trail_whitespace = last_whitespace_tbl[indent_state.stack[#indent_state.stack]] if trail then table.insert(whitespace_tbl, trail) - table.insert(drawn_indents, indent_state.stack[#indent_state.stack]) elseif trail_whitespace then if indent.is_space_indent(trail_whitespace) then table.insert(whitespace_tbl, indent.whitespace.INDENT) - table.insert(drawn_indents, indent_state.stack[#indent_state.stack]) else table.insert(whitespace_tbl, indent.whitespace.TAB_START) - table.insert(drawn_indents, #whitespace_tbl - 1) end end end end next_whitespace_tbl = whitespace_tbl - next_drawn_indents = drawn_indents end -- remove blankline trail @@ -384,12 +394,11 @@ M.refresh = function(bufnr) end if config.current_indent.enabled then + local number_of_indents, last_indent = get_indent_details(whitespace_tbl) + local prev_indent_stack_size = cur_indent_stack_size - cur_indent_stack_size = 0 - if drawn_indents then - cur_indent_stack_size = #drawn_indents - end + cur_indent_stack_size = number_of_indents if row <= cursor_row then if prev_indent_stack_size > cur_indent_stack_size then @@ -411,9 +420,7 @@ M.refresh = function(bufnr) end if row == cursor_row then cursor_row_stack_size = cur_indent_stack_size - if drawn_indents[#drawn_indents] then - current_indent_col = drawn_indents[#drawn_indents] - end + current_indent_col = last_indent if current_indent_stack[#current_indent_stack] then current_indent_row_start = current_indent_stack[#current_indent_stack] end @@ -545,7 +552,7 @@ M.refresh = function(bufnr) end -- current_indent start - if config.current_indent.show_start and current_indent_start then + if config.current_indent.show_start and current_indent_start and current_indent_col >= 0 then vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, current_indent_col, { end_col = #line, hl_group = current_indent_hl.underline, @@ -556,7 +563,12 @@ M.refresh = function(bufnr) end -- current_indent end - if config.current_indent.show_end and current_indent_end and #whitespace_tbl >= current_indent_col then + if + config.current_indent.show_end + and current_indent_end + and #whitespace_tbl >= current_indent_col + and current_indent_col >= 0 + then vim.api.nvim_buf_set_extmark(bufnr, namespace, row - 1, current_indent_col, { end_col = #line, hl_group = current_indent_hl.underline, diff --git a/specs/features/indent_spec.lua b/specs/features/indent_spec.lua index 49ec84de..6c451987 100644 --- a/specs/features/indent_spec.lua +++ b/specs/features/indent_spec.lua @@ -19,40 +19,40 @@ describe("indent", function() end) it("no whitespace", function() - local whitespace_tbl, _, _ = indent.get("", opts) + local whitespace_tbl, _ = indent.get("", opts) assert.are.same(whitespace_tbl, {}) end) it("normal space indentation", function() - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE }) end) it("normal tab", function() - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START, TAB_FILL, TAB_FILL, TAB_END }) end) it("single width tab", function() opts.tabstop = 1 - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START_SINGLE }) end) it("double width tab", function() opts.tabstop = 2 - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START, TAB_END }) end) it("vartabstop", function() opts.vartabstop = "1,3" - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same( whitespace_tbl, @@ -61,33 +61,33 @@ describe("indent", function() end) it("mix of tabs and spaces", function() - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, TAB_START, TAB_END, SPACE, TAB_START_SINGLE }) end) it("mix of tabs and spaces with vartabstop", function() opts.vartabstop = "1,3" - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0 } }) assert.are.same(whitespace_tbl, { TAB_START_SINGLE, SPACE, TAB_START, TAB_END, SPACE }) end) it("caps after first indent after last item in stack", function() - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, SPACE, SPACE }) end) it("caps after first indent of first item in stack when cap is true", function() - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = true, stack = { 0, 4 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = true, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, SPACE, SPACE, INDENT, SPACE, SPACE, SPACE }) end) it("doesn't cap with smart_indent_cap off", function() opts.smart_indent_cap = false - local whitespace_tbl, _, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) + local whitespace_tbl, _ = indent.get(" ", opts, { cap = false, stack = { 0, 4 } }) assert.are.same(whitespace_tbl, { INDENT, SPACE, INDENT, SPACE, INDENT, SPACE, INDENT, SPACE }) end) From ad76c8673667df46bb9ec2694d74881a9a8b0175 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Tue, 17 Oct 2023 14:57:19 +0200 Subject: [PATCH 13/13] Better variable name --- lua/ibl/init.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/ibl/init.lua b/lua/ibl/init.lua index b217e5e4..01efb87d 100644 --- a/lua/ibl/init.lua +++ b/lua/ibl/init.lua @@ -265,9 +265,9 @@ M.refresh = function(bufnr) local get_indent_details = function(ws_tbl) local number_of_indents = 0 - local last_indent = -1 + local last_indent_col = -1 if not ws_tbl then - return number_of_indents, last_indent + return number_of_indents, last_indent_col end local k = 0 local has_seen_indent = false @@ -275,13 +275,13 @@ M.refresh = function(bufnr) if indent.is_indent(ws_tbl[#ws_tbl - k]) then if not has_seen_indent then has_seen_indent = true - last_indent = #ws_tbl - k - 1 + last_indent_col = #ws_tbl - k - 1 end number_of_indents = number_of_indents + 1 end k = k + 1 end - return number_of_indents, last_indent + return number_of_indents, last_indent_col end ---@type number @@ -394,7 +394,7 @@ M.refresh = function(bufnr) end if config.current_indent.enabled then - local number_of_indents, last_indent = get_indent_details(whitespace_tbl) + local number_of_indents, last_indent_col = get_indent_details(whitespace_tbl) local prev_indent_stack_size = cur_indent_stack_size @@ -420,7 +420,7 @@ M.refresh = function(bufnr) end if row == cursor_row then cursor_row_stack_size = cur_indent_stack_size - current_indent_col = last_indent + current_indent_col = last_indent_col if current_indent_stack[#current_indent_stack] then current_indent_row_start = current_indent_stack[#current_indent_stack] end