summaryrefslogtreecommitdiffstats
path: root/.config/nvim/plugin/50-highlight.lua
diff options
context:
space:
mode:
Diffstat (limited to '.config/nvim/plugin/50-highlight.lua')
-rw-r--r--.config/nvim/plugin/50-highlight.lua101
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,
+})