diff options
Diffstat (limited to '.config/nvim/plugin/50-highlight.lua')
| -rw-r--r-- | .config/nvim/plugin/50-highlight.lua | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/.config/nvim/plugin/50-highlight.lua b/.config/nvim/plugin/50-highlight.lua new file mode 100644 index 0000000..0e8fb97 --- /dev/null +++ b/.config/nvim/plugin/50-highlight.lua @@ -0,0 +1,101 @@ +-- +-- Custom highlighting plugin (in `after/` to that the autocommand in `treesitter/init.lua` can be set first in order) +-- + +-- 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 + pos = next_pos() + 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 hl_ns = vim.api.nvim_create_namespace("extmarks") + +vim.api.nvim_set_hl(hl_ns, "dotfiles.Todo", { bg = "Yellow", fg = "Black", bold = true }) +local extmarks_todo_opts = { ns = hl_ns, hl_group = "dotfiles.Todo", predicate = hl_todo_predicate } + +-- Initialize extmarks +vim.api.nvim_create_autocmd("FileType", { + desc = "Initialize extmarks", + group = vim.g.dotfiles.augroup, + callback = function() + vim.api.nvim_win_set_hl_ns(vim.api.nvim_get_current_win(), hl_ns) + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + set_extmarks(pmatches(lines, patterns.todo), extmarks_todo_opts) + end, +}) |
