refactor: change highlights.context config to table structure

Problem: highlights.context was a plain integer, inconsistent with the
table structure used by treesitter, vim, and intra sub-configs.

Solution: change to { enabled = true, lines = 25 } with full
vim.validate() coverage matching the existing pattern.
This commit is contained in:
Barrett Ruth 2026-02-07 13:16:34 -05:00
parent 2e1ebdee03
commit 9e32384f18
5 changed files with 76 additions and 29 deletions

View file

@ -43,8 +43,9 @@ luarocks install diffs.nvim
- **Incomplete syntax context**: Treesitter parses each diff hunk in isolation.
To improve accuracy, `diffs.nvim` reads lines from disk before and after each
hunk for parsing context (controlled by `highlights.context`, default 25).
This resolves most boundary issues. Set `highlights.context = 0` to disable.
hunk for parsing context (`highlights.context`, enabled by default with 25
lines). This resolves most boundary issues. Set
`highlights.context.enabled = false` to disable.
- **Syntax flashing**: `diffs.nvim` hooks into the `FileType fugitive` event
triggered by `vim-fugitive`, at which point the buffer is preliminarily

View file

@ -56,7 +56,10 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
highlights = {
background = true,
gutter = true,
context = 25,
context = {
enabled = true,
lines = 25,
},
treesitter = {
enabled = true,
max_lines = 500,
@ -114,15 +117,9 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
Highlight line numbers with matching colors.
Only visible if line numbers are enabled.
{context} (integer, default: 25)
Number of lines to read from the source file
before and after each hunk for syntax parsing
context. Improves accuracy at hunk boundaries
where incomplete constructs (e.g., a function
definition with no body) would otherwise confuse
the parser. Set to 0 to disable. Lines are read
from disk with early exit — cost scales with the
context value, not file size.
{context} (table, default: see below)
Syntax parsing context options.
See |diffs.ContextConfig| for fields.
{treesitter} (table, default: see below)
Treesitter highlighting options.
@ -136,6 +133,20 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
Character-level (intra-line) diff highlighting.
See |diffs.IntraConfig| for fields.
*diffs.ContextConfig*
Context config fields: ~
{enabled} (boolean, default: true)
Read lines from disk before and after each hunk
to provide surrounding syntax context. Improves
accuracy at hunk boundaries where incomplete
constructs (e.g., a function definition with no
body) would otherwise confuse the parser.
{lines} (integer, default: 25)
Number of context lines to read in each
direction. Lines are read with early exit —
cost scales with this value, not file size.
*diffs.TreesitterConfig*
Treesitter config fields: ~
{enabled} (boolean, default: true)
@ -318,13 +329,13 @@ Incomplete Syntax Context ~
*diffs-syntax-context*
Treesitter parses each diff hunk in isolation. To provide surrounding code
context, diffs.nvim reads lines from disk before and after each hunk
(controlled by `highlights.context`, default 25). This resolves most boundary
(see |diffs.ContextConfig|, enabled by default). This resolves most boundary
issues where incomplete constructs (e.g., a function definition at the edge
of a hunk with no body) would confuse the parser.
Set `highlights.context = 0` to disable context padding and restore the
previous behavior. In rare cases, context padding may not help if the
relevant surrounding code is very far from the hunk boundaries.
Set `highlights.context.enabled = false` to disable context padding. In rare
cases, context padding may not help if the relevant surrounding code is very
far from the hunk boundaries.
Syntax Highlighting Flash ~
*diffs-flash*

View file

@ -289,7 +289,8 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
---@type table<integer, true>
local covered_lines = {}
local context = opts.highlights.context or 0
local ctx_cfg = opts.highlights.context
local context = (ctx_cfg and ctx_cfg.enabled) and ctx_cfg.lines or 0
local leading = {}
local trailing = {}
if (use_ts or use_vim) and context > 0 and hunk.file_new_start and hunk.repo_root then

View file

@ -11,10 +11,14 @@
---@field algorithm string
---@field max_lines integer
---@class diffs.ContextConfig
---@field enabled boolean
---@field lines integer
---@class diffs.Highlights
---@field background boolean
---@field gutter boolean
---@field context integer
---@field context diffs.ContextConfig
---@field treesitter diffs.TreesitterConfig
---@field vim diffs.VimConfig
---@field intra diffs.IntraConfig
@ -81,7 +85,10 @@ local default_config = {
highlights = {
background = true,
gutter = true,
context = 25,
context = {
enabled = true,
lines = 25,
},
treesitter = {
enabled = true,
max_lines = 500,
@ -233,12 +240,19 @@ local function init()
vim.validate({
['highlights.background'] = { opts.highlights.background, 'boolean', true },
['highlights.gutter'] = { opts.highlights.gutter, 'boolean', true },
['highlights.context'] = { opts.highlights.context, 'number', true },
['highlights.context'] = { opts.highlights.context, 'table', true },
['highlights.treesitter'] = { opts.highlights.treesitter, 'table', true },
['highlights.vim'] = { opts.highlights.vim, 'table', true },
['highlights.intra'] = { opts.highlights.intra, 'table', true },
})
if opts.highlights.context then
vim.validate({
['highlights.context.enabled'] = { opts.highlights.context.enabled, 'boolean', true },
['highlights.context.lines'] = { opts.highlights.context.lines, 'number', true },
})
end
if opts.highlights.treesitter then
vim.validate({
['highlights.treesitter.enabled'] = { opts.highlights.treesitter.enabled, 'boolean', true },
@ -294,8 +308,13 @@ local function init()
if opts.debounce_ms and opts.debounce_ms < 0 then
error('diffs: debounce_ms must be >= 0')
end
if opts.highlights and opts.highlights.context and opts.highlights.context < 0 then
error('diffs: highlights.context must be >= 0')
if
opts.highlights
and opts.highlights.context
and opts.highlights.context.lines
and opts.highlights.context.lines < 0
then
error('diffs: highlights.context.lines must be >= 0')
end
if
opts.highlights

View file

@ -37,7 +37,7 @@ describe('highlight', function()
highlights = {
background = false,
gutter = false,
context = 0,
context = { enabled = false, lines = 0 },
treesitter = {
enabled = true,
max_lines = 500,
@ -1087,7 +1087,12 @@ describe('highlight', function()
repo_root = repo_root,
}
highlight.highlight_hunk(bufnr, ns, hunk, default_opts({ highlights = { context = 25 } }))
highlight.highlight_hunk(
bufnr,
ns,
hunk,
default_opts({ highlights = { context = { enabled = true, lines = 25 } } })
)
local extmarks = get_extmarks(bufnr)
for _, mark in ipairs(extmarks) do
@ -1100,7 +1105,7 @@ describe('highlight', function()
vim.fn.delete(repo_root, 'rf')
end)
it('context = 0 matches behavior without padding', function()
it('context disabled matches behavior without padding', function()
local bufnr = create_buffer({
'@@ -1,1 +1,2 @@',
' local x = 1',
@ -1117,7 +1122,12 @@ describe('highlight', function()
repo_root = '/nonexistent',
}
highlight.highlight_hunk(bufnr, ns, hunk, default_opts({ highlights = { context = 0 } }))
highlight.highlight_hunk(
bufnr,
ns,
hunk,
default_opts({ highlights = { context = { enabled = false, lines = 0 } } })
)
local extmarks = get_extmarks(bufnr)
assert.is_true(#extmarks > 0)
@ -1142,7 +1152,12 @@ describe('highlight', function()
}
assert.has_no.errors(function()
highlight.highlight_hunk(bufnr, ns, hunk, default_opts({ highlights = { context = 25 } }))
highlight.highlight_hunk(
bufnr,
ns,
hunk,
default_opts({ highlights = { context = { enabled = true, lines = 25 } } })
)
end)
local extmarks = get_extmarks(bufnr)
@ -1180,7 +1195,7 @@ describe('highlight', function()
highlights = {
background = false,
gutter = false,
context = 0,
context = { enabled = false, lines = 0 },
treesitter = { enabled = true, max_lines = 500 },
vim = { enabled = false, max_lines = 200 },
},
@ -1337,7 +1352,7 @@ describe('highlight', function()
highlights = {
background = false,
gutter = false,
context = 0,
context = { enabled = false, lines = 0 },
treesitter = { enabled = true, max_lines = 500 },
vim = { enabled = false, max_lines = 200 },
},