feat: cleanup config options

This commit is contained in:
Barrett Ruth 2026-02-02 15:18:25 -05:00
parent 6c4c1e8e49
commit 69943a09c4
6 changed files with 98 additions and 126 deletions

View file

@ -42,45 +42,51 @@ CONFIGURATION *fugitive-ts-config*
Enable debug logging to |:messages| with
`[fugitive-ts]` prefix.
{languages} (table<string, string>, default: {})
Custom filename to treesitter language mappings.
Useful for non-standard file extensions.
Example: >lua
languages = {
['.envrc'] = 'bash',
['Justfile'] = 'just',
}
<
{disabled_languages} (string[], default: {})
Treesitter language names to skip highlighting.
Example: >lua
disabled_languages = { 'markdown', 'text' }
<
{debounce_ms} (integer, default: 0)
Debounce delay in milliseconds for re-highlighting
after buffer changes. Lower values feel snappier
but use more CPU.
{max_lines_per_hunk} (integer, default: 500)
Skip treesitter highlighting for hunks larger than
this many lines. Prevents lag on massive diffs.
{hide_prefix} (boolean, default: true)
{hide_prefix} (boolean, default: false)
Hide diff prefixes (`+`/`-`/` `) using virtual
text overlay. Makes code appear without the
leading diff character. When `highlights.background`
is also enabled, the overlay inherits the line's
background color.
{treesitter} (table, default: see below)
Treesitter highlighting options.
See |fugitive-ts.TreesitterConfig| for fields.
{vim} (table, default: see below)
Vim syntax highlighting options (experimental).
See |fugitive-ts.VimConfig| for fields.
{highlights} (table, default: see below)
Controls which highlight features are enabled.
See |fugitive-ts.Highlights| for fields.
*fugitive-ts.Highlights*
Highlights table fields: ~
{treesitter} (boolean, default: true)
*fugitive-ts.TreesitterConfig*
Treesitter config fields: ~
{enabled} (boolean, default: true)
Apply treesitter syntax highlighting to code.
{max_lines} (integer, default: 500)
Skip treesitter highlighting for hunks larger than
this many lines. Prevents lag on massive diffs.
*fugitive-ts.VimConfig*
Vim config fields: ~
{enabled} (boolean, default: false)
Experimental: Use vim syntax highlighting as
fallback when no treesitter parser is available.
{max_lines} (integer, default: 200)
Skip vim syntax highlighting for hunks larger than
this many lines.
*fugitive-ts.Highlights*
Highlights table fields: ~
{background} (boolean, default: true)
Apply background highlighting to `+`/`-` lines
using `FugitiveTsAdd`/`FugitiveTsDelete` groups
@ -90,13 +96,14 @@ CONFIGURATION *fugitive-ts-config*
Highlight line numbers with matching colors.
Only visible if line numbers are enabled.
{vim} (boolean, default: false)
Experimental: Use vim syntax highlighting as
fallback when no treesitter parser is available.
Note: Header context (e.g., `@@ -10,3 +10,4 @@ func()`) is always
highlighted with treesitter when a parser is available.
Language detection uses Neovim's built-in |vim.filetype.match()| and
|vim.treesitter.language.get_lang()|. To customize filetype detection
or register treesitter parsers for custom filetypes, use
|vim.filetype.add()| and |vim.treesitter.language.register()|.
==============================================================================
API *fugitive-ts-api*

View file

@ -65,8 +65,9 @@ local function highlight_text(bufnr, ns, hunk, col_offset, text, lang)
end
---@class fugitive-ts.HunkOpts
---@field max_lines integer
---@field hide_prefix boolean
---@field treesitter fugitive-ts.TreesitterConfig
---@field vim fugitive-ts.VimConfig
---@field highlights fugitive-ts.Highlights
---@param bufnr integer
@ -79,13 +80,14 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
return
end
if #hunk.lines > opts.max_lines then
local max_lines = opts.treesitter.max_lines
if #hunk.lines > max_lines then
dbg(
'skipping hunk %s:%d (%d lines > %d max)',
hunk.filename,
hunk.start_line,
#hunk.lines,
opts.max_lines
max_lines
)
return
end
@ -120,7 +122,7 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_line, 0, extmark_opts)
end
if line_len > 1 and opts.highlights.treesitter then
if line_len > 1 and opts.treesitter.enabled then
pcall(vim.api.nvim_buf_set_extmark, bufnr, ns, buf_line, 1, {
end_col = line_len,
hl_group = 'Normal',
@ -129,7 +131,7 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
end
end
if not opts.highlights.treesitter then
if not opts.treesitter.enabled then
return
end

View file

@ -1,17 +1,22 @@
---@class fugitive-ts.Highlights
---@field treesitter boolean
---@field background boolean
---@field gutter boolean
---@field vim boolean
---@class fugitive-ts.TreesitterConfig
---@field enabled boolean
---@field max_lines integer
---@class fugitive-ts.VimConfig
---@field enabled boolean
---@field max_lines integer
---@class fugitive-ts.Config
---@field enabled boolean
---@field debug boolean
---@field languages table<string, string>
---@field disabled_languages string[]
---@field debounce_ms integer
---@field max_lines_per_hunk integer
---@field hide_prefix boolean
---@field treesitter fugitive-ts.TreesitterConfig
---@field vim fugitive-ts.VimConfig
---@field highlights fugitive-ts.Highlights
---@class fugitive-ts
@ -61,16 +66,19 @@ end
local default_config = {
enabled = true,
debug = false,
languages = {},
disabled_languages = {},
debounce_ms = 0,
max_lines_per_hunk = 500,
hide_prefix = false,
treesitter = {
enabled = true,
max_lines = 500,
},
vim = {
enabled = false,
max_lines = 200,
},
highlights = {
treesitter = true,
background = true,
gutter = true,
vim = false,
},
}
@ -102,12 +110,13 @@ local function highlight_buffer(bufnr)
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
local hunks = parser.parse_buffer(bufnr, config.languages, config.disabled_languages)
local hunks = parser.parse_buffer(bufnr)
dbg('found %d hunks in buffer %d', #hunks, bufnr)
for _, hunk in ipairs(hunks) do
highlight.highlight_hunk(bufnr, ns, hunk, {
max_lines = config.max_lines_per_hunk,
hide_prefix = config.hide_prefix,
treesitter = config.treesitter,
vim = config.vim,
highlights = config.highlights,
})
end
@ -192,20 +201,31 @@ function M.setup(opts)
vim.validate({
enabled = { opts.enabled, 'boolean', true },
debug = { opts.debug, 'boolean', true },
languages = { opts.languages, 'table', true },
disabled_languages = { opts.disabled_languages, 'table', true },
debounce_ms = { opts.debounce_ms, 'number', true },
max_lines_per_hunk = { opts.max_lines_per_hunk, 'number', true },
hide_prefix = { opts.hide_prefix, 'boolean', true },
treesitter = { opts.treesitter, 'table', true },
vim = { opts.vim, 'table', true },
highlights = { opts.highlights, 'table', true },
})
if opts.treesitter then
vim.validate({
['treesitter.enabled'] = { opts.treesitter.enabled, 'boolean', true },
['treesitter.max_lines'] = { opts.treesitter.max_lines, 'number', true },
})
end
if opts.vim then
vim.validate({
['vim.enabled'] = { opts.vim.enabled, 'boolean', true },
['vim.max_lines'] = { opts.vim.max_lines, 'number', true },
})
end
if opts.highlights then
vim.validate({
['highlights.treesitter'] = { opts.highlights.treesitter, 'boolean', true },
['highlights.background'] = { opts.highlights.background, 'boolean', true },
['highlights.gutter'] = { opts.highlights.gutter, 'boolean', true },
['highlights.vim'] = { opts.highlights.vim, 'boolean', true },
})
end

View file

@ -26,19 +26,8 @@ local function dbg(msg, ...)
end
---@param filename string
---@param custom_langs? table<string, string>
---@param disabled_langs? string[]
---@return string?
local function get_lang_from_filename(filename, custom_langs, disabled_langs)
if custom_langs and custom_langs[filename] then
local lang = custom_langs[filename]
if disabled_langs and vim.tbl_contains(disabled_langs, lang) then
dbg('lang disabled: %s', lang)
return nil
end
return lang
end
local function get_lang_from_filename(filename)
local ft = vim.filetype.match({ filename = filename })
if not ft then
dbg('no filetype for: %s', filename)
@ -47,10 +36,6 @@ local function get_lang_from_filename(filename, custom_langs, disabled_langs)
local lang = vim.treesitter.language.get_lang(ft)
if lang then
if disabled_langs and vim.tbl_contains(disabled_langs, lang) then
dbg('lang disabled: %s', lang)
return nil
end
local ok = pcall(vim.treesitter.language.inspect, lang)
if ok then
return lang
@ -64,10 +49,8 @@ local function get_lang_from_filename(filename, custom_langs, disabled_langs)
end
---@param bufnr integer
---@param custom_langs? table<string, string>
---@param disabled_langs? string[]
---@return fugitive-ts.Hunk[]
function M.parse_buffer(bufnr, custom_langs, disabled_langs)
function M.parse_buffer(bufnr)
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
---@type fugitive-ts.Hunk[]
local hunks = {}
@ -107,7 +90,7 @@ function M.parse_buffer(bufnr, custom_langs, disabled_langs)
if filename then
flush_hunk()
current_filename = filename
current_lang = get_lang_from_filename(filename, custom_langs, disabled_langs)
current_lang = get_lang_from_filename(filename)
if current_lang then
dbg('file: %s -> lang: %s', filename, current_lang)
end

View file

@ -20,16 +20,19 @@ describe('fugitive-ts', function()
fugitive_ts.setup({
enabled = false,
debug = true,
languages = { ['.envrc'] = 'bash' },
disabled_languages = { 'markdown' },
debounce_ms = 100,
max_lines_per_hunk = 1000,
hide_prefix = false,
treesitter = {
enabled = true,
max_lines = 1000,
},
vim = {
enabled = false,
max_lines = 200,
},
highlights = {
treesitter = true,
background = true,
gutter = true,
vim = false,
},
})
end)

View file

@ -15,19 +15,9 @@ describe('parser', function()
end
end
local test_langs = {
['lua/test.lua'] = 'lua',
['lua/foo.lua'] = 'lua',
['src/bar.py'] = 'python',
['test.lua'] = 'lua',
['test.py'] = 'python',
['other.lua'] = 'lua',
['.envrc'] = 'bash',
}
it('returns empty table for empty buffer', function()
local bufnr = create_buffer({})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.same({}, hunks)
delete_buffer(bufnr)
end)
@ -40,7 +30,7 @@ describe('parser', function()
'Unstaged (1)',
'M lua/test.lua',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.same({}, hunks)
delete_buffer(bufnr)
end)
@ -54,7 +44,7 @@ describe('parser', function()
'+local new = true',
' return M',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(1, #hunks)
assert.are.equal('lua/test.lua', hunks[1].filename)
@ -76,7 +66,7 @@ describe('parser', function()
'+ print("hello")',
' end',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(2, #hunks)
assert.are.equal(2, hunks[1].start_line)
@ -95,7 +85,7 @@ describe('parser', function()
' def hello():',
'+ pass',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(2, #hunks)
assert.are.equal('lua/foo.lua', hunks[1].filename)
@ -113,7 +103,7 @@ describe('parser', function()
'+print(msg)',
' end',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(1, #hunks)
assert.are.equal('function M.hello()', hunks[1].header_context)
@ -128,46 +118,13 @@ describe('parser', function()
' local M = {}',
'+local x = 1',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(1, #hunks)
assert.is_nil(hunks[1].header_context)
delete_buffer(bufnr)
end)
it('respects custom language mappings', function()
local bufnr = create_buffer({
'M .envrc',
'@@ -1,1 +1,2 @@',
' export FOO=bar',
'+export BAZ=qux',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
assert.are.equal(1, #hunks)
assert.are.equal('bash', hunks[1].lang)
delete_buffer(bufnr)
end)
it('respects disabled_languages', function()
local bufnr = create_buffer({
'M test.lua',
'@@ -1,1 +1,2 @@',
' local M = {}',
'+local x = 1',
'M test.py',
'@@ -1,1 +1,2 @@',
' def foo():',
'+ pass',
})
local hunks = parser.parse_buffer(bufnr, test_langs, { 'lua' }, false)
assert.are.equal(1, #hunks)
assert.are.equal('test.py', hunks[1].filename)
assert.are.equal('python', hunks[1].lang)
delete_buffer(bufnr)
end)
it('handles all git status prefixes', function()
local prefixes = { 'M', 'A', 'D', 'R', 'C', '?', '!' }
for _, prefix in ipairs(prefixes) do
@ -177,7 +134,7 @@ describe('parser', function()
' local x = 1',
'+local y = 2',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(1, #hunks, 'Failed for prefix: ' .. prefix)
delete_buffer(bufnr)
end
@ -192,7 +149,7 @@ describe('parser', function()
'',
'Some other content',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(1, #hunks)
assert.are.equal(2, #hunks[1].lines)
@ -209,7 +166,7 @@ describe('parser', function()
'@@ -1,1 +1,1 @@',
' local z = 3',
})
local hunks = parser.parse_buffer(bufnr, test_langs, {}, false)
local hunks = parser.parse_buffer(bufnr)
assert.are.equal(2, #hunks)
assert.are.equal(2, #hunks[1].lines)