From 0d58d4e325534233156844b9d23e7783ca7cf3d3 Mon Sep 17 00:00:00 2001 From: Parker Settle Date: Wed, 28 Jan 2026 21:20:54 -0600 Subject: [PATCH 1/2] feat(chunk): add node_type_styles for scope-type-based coloring - Add node_type field to Scope class to track treesitter node type - Pass node_type from chunkHelper to render function - Add node_type_styles config option for pattern-based style mapping - Create dynamic highlight groups based on node type patterns - Also includes virt_text_repeat_linebreak fix for wrapped lines Example config: node_type_styles = { ["^func"] = { fg = "#99aee5" }, -- functions: blue ["^if"] = { fg = "#c2a2e3" }, -- conditionals: purple ["^for"] = { fg = "#fbdf90" }, -- loops: yellow } This allows different colored chunk indicators for different scope types (functions, loops, conditionals, etc.) when use_treesitter is enabled. --- lua/hlchunk/mods/chunk/chunk_conf.lua | 18 +++++++++++++ lua/hlchunk/mods/chunk/init.lua | 38 ++++++++++++++++++++++++++- lua/hlchunk/utils/chunkHelper.lua | 4 +-- lua/hlchunk/utils/scope.lua | 6 +++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/lua/hlchunk/mods/chunk/chunk_conf.lua b/lua/hlchunk/mods/chunk/chunk_conf.lua index 0a6ce5d..b68a120 100644 --- a/lua/hlchunk/mods/chunk/chunk_conf.lua +++ b/lua/hlchunk/mods/chunk/chunk_conf.lua @@ -7,6 +7,7 @@ local BaseConf = require("hlchunk.mods.base_mod.base_conf") ---@field textobject? string ---@field max_file_size? number ---@field error_sign? boolean +---@field node_type_styles? table mapping of node type patterns to style ---@class HlChunk.ChunkConf : HlChunk.BaseConf ---@field use_treesitter boolean @@ -16,6 +17,7 @@ local BaseConf = require("hlchunk.mods.base_mod.base_conf") ---@field error_sign boolean ---@field duration number ---@field delay number +---@field node_type_styles table mapping of node type patterns to style ---@overload fun(conf?: table): HlChunk.ChunkConf local ChunkConf = class(BaseConf, function(self, conf) local default_conf = { @@ -38,6 +40,21 @@ local ChunkConf = class(BaseConf, function(self, conf) error_sign = true, duration = 200, delay = 300, + -- Node type to style mapping (patterns matched against treesitter node type) + -- If a node type matches multiple patterns, first match wins + -- If no match, falls back to default style[1] + node_type_styles = { + -- Example config (users can override): + -- ["^func"] = { fg = "#99aee5" }, -- functions: blue + -- ["^if"] = { fg = "#c2a2e3" }, -- conditionals: purple + -- ["else"] = { fg = "#c2a2e3" }, -- else: purple + -- ["^for"] = { fg = "#fbdf90" }, -- for loops: yellow + -- ["^while"] = { fg = "#fbdf90" }, -- while loops: yellow + -- ["try"] = { fg = "#9fe8c3" }, -- try/catch: green + -- ["except"] = { fg = "#9fe8c3" }, -- except: green + -- ["catch"] = { fg = "#9fe8c3" }, -- catch: green + -- ["class"] = { fg = "#ef8891" }, -- classes: red + }, } conf = vim.tbl_deep_extend("force", default_conf, conf or {}) --[[@as HlChunk.ChunkConf]] BaseConf.init(self, conf) @@ -50,6 +67,7 @@ local ChunkConf = class(BaseConf, function(self, conf) self.error_sign = conf.error_sign self.duration = conf.duration self.delay = conf.delay + self.node_type_styles = conf.node_type_styles end) return ChunkConf diff --git a/lua/hlchunk/mods/chunk/init.lua b/lua/hlchunk/mods/chunk/init.lua index 4fa9456..7405d33 100644 --- a/lua/hlchunk/mods/chunk/init.lua +++ b/lua/hlchunk/mods/chunk/init.lua @@ -17,6 +17,9 @@ local rangeFromTo = chunkHelper.rangeFromTo local utf8Split = chunkHelper.utf8Split local shallowCmp = chunkHelper.shallowCmp +-- Counter for dynamically created highlight groups +local hl_group_counter = 0 + ---@class HlChunk.ChunkMetaInfo : HlChunk.MetaInfo ---@field task HlChunk.LoopTask | nil ---@field pre_virt_text_list string[] @@ -121,6 +124,37 @@ function ChunkMod:get_chunk_data(range, virt_text_list, row_list, virt_text_win_ end end +--- Get the highlight group for a given node type +--- Matches node_type against patterns in node_type_styles config +---@param node_type string|nil the treesitter node type +---@param is_error boolean whether the chunk has an error +---@return string highlight group name +function ChunkMod:get_hl_group_for_node_type(node_type, is_error) + -- If error, always use error style + if is_error then + return "HLChunk2" + end + + -- If no node type or no node_type_styles configured, use default + if not node_type or not self.conf.node_type_styles or vim.tbl_isempty(self.conf.node_type_styles) then + return "HLChunk1" + end + + -- Try to match node_type against configured patterns + for pattern, style in pairs(self.conf.node_type_styles) do + if node_type:find(pattern) then + -- Create a dynamic highlight group for this style + hl_group_counter = hl_group_counter + 1 + local hl_name = "HLChunkNodeType" .. hl_group_counter + api.nvim_set_hl(0, hl_name, style) + return hl_name + end + end + + -- No match, use default + return "HLChunk1" +end + function ChunkMod:render(range, opts) opts = opts or { error = false, lazy = false } if not self:shouldRender(range.bufnr) then @@ -148,10 +182,12 @@ function ChunkMod:render(range, opts) local row_opts = { virt_text_pos = "overlay", + virt_text_repeat_linebreak = true, hl_mode = "combine", priority = 100, } - local text_hl = opts.error and "HLChunk2" or "HLChunk1" + -- Get highlight based on node type (or error/default) + local text_hl = self:get_hl_group_for_node_type(range.node_type, opts.error) if self.conf.delay == 0 or opts.lazy == false then for i, vt in ipairs(virt_text_list) do row_opts.virt_text = { { vt, text_hl } } diff --git a/lua/hlchunk/utils/chunkHelper.lua b/lua/hlchunk/utils/chunkHelper.lua index a0adf3a..836969c 100644 --- a/lua/hlchunk/utils/chunkHelper.lua +++ b/lua/hlchunk/utils/chunkHelper.lua @@ -80,7 +80,7 @@ local function get_chunk_range_by_treesitter(pos) local node_start, _, node_end, _ = cursor_node:range() if node_start ~= node_end and is_suit_type(node_type) then return cursor_node:has_error() and chunkHelper.CHUNK_RANGE_RET.CHUNK_ERR or chunkHelper.CHUNK_RANGE_RET.OK, - Scope(pos.bufnr, node_start, node_end) + Scope(pos.bufnr, node_start, node_end, node_type) end local parent_node = cursor_node:parent() if parent_node == cursor_node then @@ -88,7 +88,7 @@ local function get_chunk_range_by_treesitter(pos) end cursor_node = parent_node end - return chunkHelper.CHUNK_RANGE_RET.NO_CHUNK, Scope(pos.bufnr, -1, -1) + return chunkHelper.CHUNK_RANGE_RET.NO_CHUNK, Scope(pos.bufnr, -1, -1, nil) end ---@param opts? {pos: HlChunk.Pos, use_treesitter: boolean} diff --git a/lua/hlchunk/utils/scope.lua b/lua/hlchunk/utils/scope.lua index 819d0d5..538b33c 100644 --- a/lua/hlchunk/utils/scope.lua +++ b/lua/hlchunk/utils/scope.lua @@ -2,14 +2,16 @@ ---@field bufnr number ---@field start number ---@field finish number ----@overload fun(bufnr: number, start: number, finish: number): HlChunk.Scope +---@field node_type? string treesitter node type (e.g., "function", "if_statement", "for_loop") +---@overload fun(bufnr: number, start: number, finish: number, node_type?: string): HlChunk.Scope ---0-indexing, include start and finish -- local Scope = class(constructor) -local function Scope(bufnr, start, finish) +local function Scope(bufnr, start, finish, node_type) return { bufnr = bufnr, start = start, finish = finish, + node_type = node_type, } end From 7002042b741129dcbeee7dac0ffb99cab2213605 Mon Sep 17 00:00:00 2001 From: Parker Settle Date: Wed, 28 Jan 2026 22:38:38 -0600 Subject: [PATCH 2/2] feat(nix): add Nix language support for nested scope detection - Add nix.lua with Nix-specific treesitter node types - Enables chunk indicators for let expressions, attrsets, lambdas, etc. - Provides better visual feedback for Nix flake structure This allows hlchunk to show arrows/indicators for all nested scopes in Nix files (inputs, outputs, let blocks, attribute sets, etc.) instead of just top-level scopes. --- lua/hlchunk/utils/ts_node_type/init.lua | 1 + lua/hlchunk/utils/ts_node_type/nix.lua | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 lua/hlchunk/utils/ts_node_type/nix.lua diff --git a/lua/hlchunk/utils/ts_node_type/init.lua b/lua/hlchunk/utils/ts_node_type/init.lua index 2cb6287..55f02ca 100644 --- a/lua/hlchunk/utils/ts_node_type/init.lua +++ b/lua/hlchunk/utils/ts_node_type/init.lua @@ -3,6 +3,7 @@ local M = {} M.cpp = require("hlchunk.utils.ts_node_type.cpp") M.css = require("hlchunk.utils.ts_node_type.css") M.lua = require("hlchunk.utils.ts_node_type.lua") +M.nix = require("hlchunk.utils.ts_node_type.nix") M.rust = require("hlchunk.utils.ts_node_type.rust") M.yaml = require("hlchunk.utils.ts_node_type.yaml") M.zig = require("hlchunk.utils.ts_node_type.zig") diff --git a/lua/hlchunk/utils/ts_node_type/nix.lua b/lua/hlchunk/utils/ts_node_type/nix.lua new file mode 100644 index 0000000..6c017fc --- /dev/null +++ b/lua/hlchunk/utils/ts_node_type/nix.lua @@ -0,0 +1,17 @@ +-- Nix-specific treesitter node types for hlchunk +-- Covers: let expressions, attribute sets, lambda functions, with expressions, etc. +-- Format: node_type = true (table with keys, not array) +return { + let_expression = true, -- let ... in ... + attrset_expression = true, -- { ... } + rec_attrset_expression = true, -- rec { ... } + list_expression = true, -- [ ... ] + lambda_expression = true, -- arg: body + function_expression = true, -- { args }: body + with_expression = true, -- with ...; ... + if_expression = true, -- if ... then ... else ... + assert_expression = true, -- assert ...; ... + binding = true, -- name = value; + inherit = true, -- inherit ...; + inherit_from = true, -- inherit (...) ...; +}