From e5ebf3b3ecb4ed7f7d150eb20b584025a261652c Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Sun, 1 Feb 2026 19:35:19 -0500 Subject: [PATCH] feat: more config options --- doc/fugitive-ts.nvim.txt | 8 +++++ lua/fugitive-ts/highlight.lua | 67 +++++++++++++++++++++++++++++++++-- lua/fugitive-ts/init.lua | 16 +++++++-- lua/fugitive-ts/parser.lua | 29 +++++++++++++-- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/doc/fugitive-ts.nvim.txt b/doc/fugitive-ts.nvim.txt index f91fb09..cc900b1 100644 --- a/doc/fugitive-ts.nvim.txt +++ b/doc/fugitive-ts.nvim.txt @@ -32,6 +32,14 @@ SETUP *fugitive-ts-setup* -- Custom filename -> language mappings (optional) languages = {}, + -- Languages to skip treesitter highlighting for (default: {}) + -- Uses treesitter language names, e.g. {"markdown", "vimdoc"} + disabled_languages = {}, + + -- Highlight context in hunk headers (default: true) + -- e.g. "@@ -10,3 +10,4 @@ function foo()" -> "function foo()" gets highlighted + highlight_headers = true, + -- Debounce delay in ms (default: 50) debounce_ms = 50, diff --git a/lua/fugitive-ts/highlight.lua b/lua/fugitive-ts/highlight.lua index ea292d0..6286f10 100644 --- a/lua/fugitive-ts/highlight.lua +++ b/lua/fugitive-ts/highlight.lua @@ -10,9 +10,58 @@ end ---@param bufnr integer ---@param ns integer ---@param hunk fugitive-ts.Hunk ----@param max_lines integer +---@param col_offset integer +---@param text string +---@param lang string ---@param debug? boolean -function M.highlight_hunk(bufnr, ns, hunk, max_lines, debug) +---@return integer +local function highlight_text(bufnr, ns, hunk, col_offset, text, lang, debug) + local ok, parser_obj = pcall(vim.treesitter.get_string_parser, text, lang) + if not ok or not parser_obj then + return 0 + end + + local trees = parser_obj:parse() + if not trees or #trees == 0 then + return 0 + end + + local query = vim.treesitter.query.get(lang, 'highlights') + if not query then + return 0 + end + + local extmark_count = 0 + local header_line = hunk.start_line - 1 + + for id, node, _ in query:iter_captures(trees[1]:root(), text) do + local capture_name = '@' .. query.captures[id] + local sr, sc, er, ec = node:range() + + local buf_sr = header_line + sr + local buf_er = header_line + er + local buf_sc = col_offset + sc + local buf_ec = col_offset + ec + + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_sr, buf_sc, { + end_row = buf_er, + end_col = buf_ec, + hl_group = capture_name, + priority = 200, + }) + extmark_count = extmark_count + 1 + end + + return extmark_count +end + +---@param bufnr integer +---@param ns integer +---@param hunk fugitive-ts.Hunk +---@param max_lines integer +---@param highlight_headers boolean +---@param debug? boolean +function M.highlight_hunk(bufnr, ns, hunk, max_lines, highlight_headers, debug) local lang = hunk.lang if not lang then return @@ -66,6 +115,20 @@ function M.highlight_hunk(bufnr, ns, hunk, max_lines, debug) return end + if highlight_headers and hunk.header_context and hunk.header_context_col then + local header_line = hunk.start_line - 1 + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, header_line, hunk.header_context_col, { + end_col = hunk.header_context_col + #hunk.header_context, + hl_group = 'Normal', + priority = 199, + }) + local header_extmarks = + highlight_text(bufnr, ns, hunk, hunk.header_context_col, hunk.header_context, lang, debug) + if debug and header_extmarks > 0 then + dbg('header %s:%d applied %d extmarks', hunk.filename, hunk.start_line, header_extmarks) + end + end + for i, line in ipairs(hunk.lines) do local buf_line = hunk.start_line + i - 1 local line_len = #line diff --git a/lua/fugitive-ts/init.lua b/lua/fugitive-ts/init.lua index 4247062..209a19c 100644 --- a/lua/fugitive-ts/init.lua +++ b/lua/fugitive-ts/init.lua @@ -2,6 +2,8 @@ ---@field enabled boolean ---@field debug boolean ---@field languages table +---@field disabled_languages string[] +---@field highlight_headers boolean ---@field debounce_ms integer ---@field max_lines_per_hunk integer @@ -21,6 +23,8 @@ local default_config = { enabled = true, debug = false, languages = {}, + disabled_languages = {}, + highlight_headers = true, debounce_ms = 50, max_lines_per_hunk = 500, } @@ -53,10 +57,18 @@ local function highlight_buffer(bufnr) vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) - local hunks = parser.parse_buffer(bufnr, config.languages, config.debug) + local hunks = + parser.parse_buffer(bufnr, config.languages, config.disabled_languages, config.debug) dbg('found %d hunks in buffer %d', #hunks, bufnr) for _, hunk in ipairs(hunks) do - highlight.highlight_hunk(bufnr, ns, hunk, config.max_lines_per_hunk, config.debug) + highlight.highlight_hunk( + bufnr, + ns, + hunk, + config.max_lines_per_hunk, + config.highlight_headers, + config.debug + ) end end diff --git a/lua/fugitive-ts/parser.lua b/lua/fugitive-ts/parser.lua index fec0dda..67c84ee 100644 --- a/lua/fugitive-ts/parser.lua +++ b/lua/fugitive-ts/parser.lua @@ -2,6 +2,8 @@ ---@field filename string ---@field lang string ---@field start_line integer +---@field header_context string? +---@field header_context_col integer? ---@field lines string[] local M = {} @@ -15,9 +17,10 @@ end ---@param filename string ---@param custom_langs? table +---@param disabled_langs? string[] ---@param debug? boolean ---@return string? -local function get_lang_from_filename(filename, custom_langs, debug) +local function get_lang_from_filename(filename, custom_langs, disabled_langs, debug) if custom_langs and custom_langs[filename] then return custom_langs[filename] end @@ -32,6 +35,12 @@ local function get_lang_from_filename(filename, custom_langs, debug) local lang = vim.treesitter.language.get_lang(ft) if lang then + if disabled_langs and vim.tbl_contains(disabled_langs, lang) then + if debug then + dbg('lang disabled: %s', lang) + end + return nil + end local ok = pcall(vim.treesitter.language.inspect, lang) if ok then return lang @@ -48,9 +57,10 @@ end ---@param bufnr integer ---@param custom_langs? table +---@param disabled_langs? string[] ---@param debug? boolean ---@return fugitive-ts.Hunk[] -function M.parse_buffer(bufnr, custom_langs, debug) +function M.parse_buffer(bufnr, custom_langs, disabled_langs, debug) local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) ---@type fugitive-ts.Hunk[] local hunks = {} @@ -61,6 +71,10 @@ function M.parse_buffer(bufnr, custom_langs, debug) local current_lang = nil ---@type integer? local hunk_start = nil + ---@type string? + local hunk_header_context = nil + ---@type integer? + local hunk_header_context_col = nil ---@type string[] local hunk_lines = {} @@ -70,10 +84,14 @@ function M.parse_buffer(bufnr, custom_langs, debug) filename = current_filename, lang = current_lang, start_line = hunk_start, + header_context = hunk_header_context, + header_context_col = hunk_header_context_col, lines = hunk_lines, }) end hunk_start = nil + hunk_header_context = nil + hunk_header_context_col = nil hunk_lines = {} end @@ -82,13 +100,18 @@ function M.parse_buffer(bufnr, custom_langs, debug) if filename then flush_hunk() current_filename = filename - current_lang = get_lang_from_filename(filename, custom_langs, debug) + current_lang = get_lang_from_filename(filename, custom_langs, disabled_langs, debug) if debug and current_lang then dbg('file: %s -> lang: %s', filename, current_lang) end elseif line:match('^@@.-@@') then flush_hunk() hunk_start = i + local prefix, context = line:match('^(@@.-@@%s*)(.*)') + if context and context ~= '' then + hunk_header_context = context + hunk_header_context_col = #prefix + end elseif hunk_start then local prefix = line:sub(1, 1) if prefix == ' ' or prefix == '+' or prefix == '-' then