diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-05-19 12:00:21 +0200 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-05-19 15:06:47 +0200 |
| commit | 2f035b58d45ca5d34522a3945073739e66f5c297 (patch) | |
| tree | a9ddd26bcc024de707dd9cb77410abe20c1e145a /plugin/50-extmarks.lua | |
| parent | 0fe3b9ea814e195358b6a92b1881d513be9eb4e6 (diff) | |
| download | nvim-config-2f035b58d45ca5d34522a3945073739e66f5c297.tar.gz nvim-config-2f035b58d45ca5d34522a3945073739e66f5c297.zip | |
feat(nvim): focus-aware cursor line via per-window highlight namespaces
Split 50-highlight.lua into 40-colors.lua (colorscheme, custom
highlights, per-window namespaces, focus tracking) and 50-extmarks.lua
(TODO scanner reading the namespace contract from vim.g.dotfiles). The
40- prefix guarantees colors load before extmarks.
Focused vs unfocused windows now differ by cursor-line color and a
CursorLineNr badge in the focus hue (green on dark, magenta on light);
the number column stays visible on closed folds where CursorLine cannot.
Diffstat (limited to 'plugin/50-extmarks.lua')
| -rw-r--r-- | plugin/50-extmarks.lua | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/plugin/50-extmarks.lua b/plugin/50-extmarks.lua new file mode 100644 index 0000000..c11d10c --- /dev/null +++ b/plugin/50-extmarks.lua @@ -0,0 +1,105 @@ +-- +-- TODO highlighting (treesitter-aware extmarks) +-- +-- The `dotfiles.Todo` highlight group and the storage namespace are owned by +-- 40-colors.lua, because the same per-window namespaces also drive the +-- focused/unfocused cursor line. That file is numbered 40- so it is sourced +-- before this one and `vim.g.dotfiles.todo_ns` / `.todo_hl` exist below. Here +-- we only place the extmarks. + +-- Returns an iterator over the ascendants of `node`, starting at the root node +local function node_ascendants(node) + local root = node:tree():root() + local current = root + return function() + if current:equal(node) then + return nil + end + current = assert(current:child_with_descendant(node)) + return current + end +end + +-- Returns true if `node` or any of its parent has the type `type` +local function node_within_type(node, type) + local next_ascendant = node_ascendants(node) + local next = next_ascendant() + while next do + if next:type() == type then + return true + end + next = next_ascendant() + end + return false +end + +-- Returns an iterator over the positions (`{ col1, col2, row }`) of matches of pattern in lines +local function pmatches(lines, pattern) + local col1, col2, row = nil, nil, 1 + return function() + while row < #lines do + col1, col2 = string.find(lines[row], pattern, (col2 or 0) + 1) + if col1 then + return { col1 = col1, col2 = col2, row = row } + else + col1, col2, row = 0, 0, row + 1 + end + end + return nil + end +end + +-- Set extmarks are the positions (`{ col1, col2, row }`) given by `next_pos()` +-- `opts`: { +-- ns: highlight namespace +-- predicate: is passed row and col, if true then set the mark +-- hl_group: highlight group +-- } +local function set_extmarks(next_pos, opts) + for pos in next_pos do + local marks = vim.api.nvim_buf_get_extmarks( + 0, + opts.ns, + { pos.row - 1, pos.col1 - 1 }, + { pos.row - 1, pos.col2 - 1 }, + {} + ) + if #marks == 0 and opts.predicate(pos.row - 1, pos.col1 - 1) then + vim.api.nvim_buf_set_extmark( + 0, + opts.ns, + pos.row - 1, + pos.col1 - 1, + { end_col = pos.col2, hl_group = opts.hl_group } + ) + end + end +end + +-- TODO a better way to do this: +-- For code, only check in "comment" node ranges +-- For non-code, do it per type. For Markdown, only check "paragraph" nodes +local function hl_todo_predicate(row, col) + if vim.o.filetype == "markdown" or vim.treesitter.get_parser() == nil then + return true + end + local node = assert(vim.treesitter.get_node({ pos = { row, col } })) + return node_within_type(node, "comment") +end + +local patterns = { todo = "TODO" } +local extmarks_todo_opts = { + ns = vim.g.dotfiles.todo_ns, + hl_group = vim.g.dotfiles.todo_hl, + predicate = hl_todo_predicate, +} + +-- Initialize extmarks +vim.api.nvim_create_autocmd("FileType", { + desc = "Initialize extmarks", + group = vim.g.dotfiles.augroup, + callback = function() + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + set_extmarks(pmatches(lines, patterns.todo), extmarks_todo_opts) + end, +}) |
