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. - **Incomplete syntax context**: Treesitter parses each diff hunk in isolation.
To improve accuracy, `diffs.nvim` reads lines from disk before and after each To improve accuracy, `diffs.nvim` reads lines from disk before and after each
hunk for parsing context (controlled by `highlights.context`, default 25). hunk for parsing context (`highlights.context`, enabled by default with 25
This resolves most boundary issues. Set `highlights.context = 0` to disable. lines). This resolves most boundary issues. Set
`highlights.context.enabled = false` to disable.
- **Syntax flashing**: `diffs.nvim` hooks into the `FileType fugitive` event - **Syntax flashing**: `diffs.nvim` hooks into the `FileType fugitive` event
triggered by `vim-fugitive`, at which point the buffer is preliminarily 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 = { highlights = {
background = true, background = true,
gutter = true, gutter = true,
context = 25, context = {
enabled = true,
lines = 25,
},
treesitter = { treesitter = {
enabled = true, enabled = true,
max_lines = 500, 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. Highlight line numbers with matching colors.
Only visible if line numbers are enabled. Only visible if line numbers are enabled.
{context} (integer, default: 25) {context} (table, default: see below)
Number of lines to read from the source file Syntax parsing context options.
before and after each hunk for syntax parsing See |diffs.ContextConfig| for fields.
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.
{treesitter} (table, default: see below) {treesitter} (table, default: see below)
Treesitter highlighting options. 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. Character-level (intra-line) diff highlighting.
See |diffs.IntraConfig| for fields. 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* *diffs.TreesitterConfig*
Treesitter config fields: ~ Treesitter config fields: ~
{enabled} (boolean, default: true) {enabled} (boolean, default: true)
@ -318,13 +329,13 @@ Incomplete Syntax Context ~
*diffs-syntax-context* *diffs-syntax-context*
Treesitter parses each diff hunk in isolation. To provide surrounding code Treesitter parses each diff hunk in isolation. To provide surrounding code
context, diffs.nvim reads lines from disk before and after each hunk 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 issues where incomplete constructs (e.g., a function definition at the edge
of a hunk with no body) would confuse the parser. of a hunk with no body) would confuse the parser.
Set `highlights.context = 0` to disable context padding and restore the Set `highlights.context.enabled = false` to disable context padding. In rare
previous behavior. In rare cases, context padding may not help if the cases, context padding may not help if the relevant surrounding code is very
relevant surrounding code is very far from the hunk boundaries. far from the hunk boundaries.
Syntax Highlighting Flash ~ Syntax Highlighting Flash ~
*diffs-flash* *diffs-flash*

View file

@ -289,7 +289,8 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
---@type table<integer, true> ---@type table<integer, true>
local covered_lines = {} 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 leading = {}
local trailing = {} local trailing = {}
if (use_ts or use_vim) and context > 0 and hunk.file_new_start and hunk.repo_root then 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 algorithm string
---@field max_lines integer ---@field max_lines integer
---@class diffs.ContextConfig
---@field enabled boolean
---@field lines integer
---@class diffs.Highlights ---@class diffs.Highlights
---@field background boolean ---@field background boolean
---@field gutter boolean ---@field gutter boolean
---@field context integer ---@field context diffs.ContextConfig
---@field treesitter diffs.TreesitterConfig ---@field treesitter diffs.TreesitterConfig
---@field vim diffs.VimConfig ---@field vim diffs.VimConfig
---@field intra diffs.IntraConfig ---@field intra diffs.IntraConfig
@ -81,7 +85,10 @@ local default_config = {
highlights = { highlights = {
background = true, background = true,
gutter = true, gutter = true,
context = 25, context = {
enabled = true,
lines = 25,
},
treesitter = { treesitter = {
enabled = true, enabled = true,
max_lines = 500, max_lines = 500,
@ -233,12 +240,19 @@ local function init()
vim.validate({ vim.validate({
['highlights.background'] = { opts.highlights.background, 'boolean', true }, ['highlights.background'] = { opts.highlights.background, 'boolean', true },
['highlights.gutter'] = { opts.highlights.gutter, '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.treesitter'] = { opts.highlights.treesitter, 'table', true },
['highlights.vim'] = { opts.highlights.vim, 'table', true }, ['highlights.vim'] = { opts.highlights.vim, 'table', true },
['highlights.intra'] = { opts.highlights.intra, '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 if opts.highlights.treesitter then
vim.validate({ vim.validate({
['highlights.treesitter.enabled'] = { opts.highlights.treesitter.enabled, 'boolean', true }, ['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 if opts.debounce_ms and opts.debounce_ms < 0 then
error('diffs: debounce_ms must be >= 0') error('diffs: debounce_ms must be >= 0')
end end
if opts.highlights and opts.highlights.context and opts.highlights.context < 0 then if
error('diffs: highlights.context must be >= 0') 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 end
if if
opts.highlights opts.highlights

View file

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