feat(highlight): warn when hunks exceed max_lines
Problem: when a hunk's highlighted lines exceed `max_lines`, syntax highlighting is silently skipped. Users have no indication why parts of their diff lack highlighting. Solution: add `highlights.warn_max_lines` (default `true`) that emits a `vim.notify` warning with the hunk index and line count vs threshold. Also change `max_lines` to count only highlighted (`+`/`-`) lines rather than total body lines including context.
This commit is contained in:
parent
c7cd8fc24c
commit
5199e72bd0
4 changed files with 74 additions and 22 deletions
|
|
@ -93,6 +93,7 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
background = true,
|
||||
gutter = true,
|
||||
blend_alpha = 0.6,
|
||||
warn_max_lines = true,
|
||||
context = {
|
||||
enabled = true,
|
||||
lines = 25,
|
||||
|
|
@ -249,6 +250,14 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
(inclusive). Higher values produce more vivid
|
||||
backgrounds.
|
||||
|
||||
{warn_max_lines} (boolean, default: true)
|
||||
Show a |vim.notify()| warning when a hunk's
|
||||
highlighted lines (`+`/`-`) exceed {max_lines}
|
||||
and syntax highlighting is skipped. The warning
|
||||
includes the 1-indexed hunk number and the
|
||||
line count vs. threshold so the user knows
|
||||
which hunk was affected and what to adjust.
|
||||
|
||||
{context} (table, default: see below)
|
||||
Syntax parsing context options.
|
||||
See |diffs.ContextConfig| for fields.
|
||||
|
|
@ -322,8 +331,10 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
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.
|
||||
Skip treesitter highlighting for hunks with more
|
||||
highlighted lines (`+`/`-`) than this threshold.
|
||||
Context lines are not counted. Prevents lag on
|
||||
massive diffs.
|
||||
|
||||
*diffs.VimConfig*
|
||||
Vim config fields: ~
|
||||
|
|
@ -338,9 +349,11 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
parser installed (e.g., COBOL, Fortran).
|
||||
|
||||
{max_lines} (integer, default: 200)
|
||||
Skip vim syntax highlighting for hunks larger than
|
||||
this many lines. Lower than the treesitter default
|
||||
due to the per-character cost of |synID()|.
|
||||
Skip vim syntax highlighting for hunks with more
|
||||
highlighted lines (`+`/`-`) than this threshold.
|
||||
Context lines are not counted. Lower than the
|
||||
treesitter default due to the per-character cost
|
||||
of |synID()|.
|
||||
|
||||
*diffs.IntraConfig*
|
||||
Intra config fields: ~
|
||||
|
|
@ -359,8 +372,9 @@ Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
|
|||
(falls back to default if not available).
|
||||
|
||||
{max_lines} (integer, default: 500)
|
||||
Skip character-level highlighting for hunks larger
|
||||
than this many lines.
|
||||
Skip character-level highlighting for hunks with
|
||||
more highlighted lines (`+`/`-`) than this
|
||||
threshold. Context lines are not counted.
|
||||
|
||||
Note: Header context (e.g., `@@ -10,3 +10,4 @@ func()`) is always
|
||||
highlighted with treesitter when a parser is available.
|
||||
|
|
|
|||
|
|
@ -310,16 +310,26 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
|
|||
local use_vim = not use_ts and hunk.ft and opts.highlights.vim.enabled
|
||||
|
||||
local max_lines = use_ts and opts.highlights.treesitter.max_lines or opts.highlights.vim.max_lines
|
||||
if (use_ts or use_vim) and #hunk.lines > max_lines then
|
||||
dbg(
|
||||
'skipping hunk %s:%d (%d lines > %d max)',
|
||||
hunk.filename,
|
||||
hunk.start_line,
|
||||
#hunk.lines,
|
||||
max_lines
|
||||
)
|
||||
use_ts = false
|
||||
use_vim = false
|
||||
if use_ts or use_vim then
|
||||
local hl_count = 0
|
||||
for _, line in ipairs(hunk.lines) do
|
||||
local c = line:sub(1, 1)
|
||||
if c == '+' or c == '-' then
|
||||
hl_count = hl_count + 1
|
||||
end
|
||||
end
|
||||
hunk._hl_line_count = hl_count
|
||||
if hl_count > max_lines then
|
||||
dbg(
|
||||
'skipping hunk %s:%d (%d highlighted lines > %d max)',
|
||||
hunk.filename,
|
||||
hunk.start_line,
|
||||
hl_count,
|
||||
max_lines
|
||||
)
|
||||
use_ts = false
|
||||
use_vim = false
|
||||
end
|
||||
end
|
||||
|
||||
if use_vim and opts.defer_vim_syntax then
|
||||
|
|
@ -456,7 +466,7 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
|
|||
and intra_cfg
|
||||
and intra_cfg.enabled
|
||||
and pw == 1
|
||||
and #hunk.lines <= intra_cfg.max_lines
|
||||
and (hunk._hl_line_count or #hunk.lines) <= intra_cfg.max_lines
|
||||
then
|
||||
dbg('computing intra for hunk %s:%d (%d lines)', hunk.filename, hunk.start_line, #hunk.lines)
|
||||
intra = diff.compute_intra_hunks(hunk.lines, intra_cfg.algorithm)
|
||||
|
|
@ -467,8 +477,8 @@ function M.highlight_hunk(bufnr, ns, hunk, opts)
|
|||
end
|
||||
elseif intra_cfg and not intra_cfg.enabled then
|
||||
dbg('intra disabled by config')
|
||||
elseif intra_cfg and #hunk.lines > intra_cfg.max_lines then
|
||||
dbg('intra skipped: %d lines > %d max', #hunk.lines, intra_cfg.max_lines)
|
||||
elseif intra_cfg and (hunk._hl_line_count or #hunk.lines) > intra_cfg.max_lines then
|
||||
dbg('intra skipped: %d highlighted lines > %d max', hunk._hl_line_count or #hunk.lines, intra_cfg.max_lines)
|
||||
end
|
||||
|
||||
---@type table<integer, diffs.CharSpan[]>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
---@field gutter boolean
|
||||
---@field blend_alpha? number
|
||||
---@field overrides? table<string, table>
|
||||
---@field warn_max_lines boolean
|
||||
---@field context diffs.ContextConfig
|
||||
---@field treesitter diffs.TreesitterConfig
|
||||
---@field vim diffs.VimConfig
|
||||
|
|
@ -132,6 +133,7 @@ local default_config = {
|
|||
highlights = {
|
||||
background = true,
|
||||
gutter = true,
|
||||
warn_max_lines = true,
|
||||
context = {
|
||||
enabled = true,
|
||||
lines = 25,
|
||||
|
|
@ -203,6 +205,7 @@ local diff_windows = {}
|
|||
---@field tick integer
|
||||
---@field highlighted table<integer, true>
|
||||
---@field pending_clear boolean
|
||||
---@field warned_max_lines boolean
|
||||
---@field line_count integer
|
||||
---@field byte_count integer
|
||||
|
||||
|
|
@ -423,6 +426,7 @@ local function ensure_cache(bufnr)
|
|||
tick = tick,
|
||||
highlighted = carried or {},
|
||||
pending_clear = not carried,
|
||||
warned_max_lines = false,
|
||||
line_count = lc,
|
||||
byte_count = bc,
|
||||
}
|
||||
|
|
@ -703,6 +707,7 @@ local function init()
|
|||
vim.validate('highlights.gutter', opts.highlights.gutter, 'boolean', true)
|
||||
vim.validate('highlights.blend_alpha', opts.highlights.blend_alpha, 'number', true)
|
||||
vim.validate('highlights.overrides', opts.highlights.overrides, 'table', true)
|
||||
vim.validate('highlights.warn_max_lines', opts.highlights.warn_max_lines, 'boolean', true)
|
||||
vim.validate('highlights.context', opts.highlights.context, 'table', true)
|
||||
vim.validate('highlights.treesitter', opts.highlights.treesitter, 'table', true)
|
||||
vim.validate('highlights.vim', opts.highlights.vim, 'table', true)
|
||||
|
|
@ -890,6 +895,7 @@ local function init()
|
|||
if entry and entry.pending_clear then
|
||||
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||
entry.highlighted = {}
|
||||
entry.warned_max_lines = {}
|
||||
entry.pending_clear = false
|
||||
end
|
||||
if t0 then
|
||||
|
|
@ -910,6 +916,7 @@ local function init()
|
|||
end
|
||||
local t0 = config.debug and vim.uv.hrtime() or nil
|
||||
local deferred_syntax = {}
|
||||
local skipped_warnings = {}
|
||||
local count = 0
|
||||
for i = first, last do
|
||||
if not entry.highlighted[i] then
|
||||
|
|
@ -926,10 +933,30 @@ local function init()
|
|||
local has_syntax = hunk.lang and config.highlights.treesitter.enabled
|
||||
local needs_vim = not hunk.lang and hunk.ft and config.highlights.vim.enabled
|
||||
if has_syntax or needs_vim then
|
||||
local max_lines = has_syntax and config.highlights.treesitter.max_lines
|
||||
or config.highlights.vim.max_lines
|
||||
local hl_count = hunk._hl_line_count or #hunk.lines
|
||||
if hl_count > max_lines then
|
||||
table.insert(skipped_warnings, { idx = i, hl_count = hl_count, max_lines = max_lines })
|
||||
end
|
||||
table.insert(deferred_syntax, hunk)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not entry.warned_max_lines and #skipped_warnings > 0 and config.highlights.warn_max_lines then
|
||||
entry.warned_max_lines = true
|
||||
vim.schedule(function()
|
||||
for _, info in ipairs(skipped_warnings) do
|
||||
vim.notify(
|
||||
('[diffs.nvim]: skipping hunk %d - too many lines (%d > %d).'
|
||||
.. ' increase `vim.g.diffs.highlights.{vim,treesitter}.max_lines`'
|
||||
.. ' or set `vim.g.diffs.highlights.warn_max_lines = false`'
|
||||
.. ' to disable this warning'):format(info.idx, info.hl_count, info.max_lines),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if #deferred_syntax > 0 then
|
||||
local tick = entry.tick
|
||||
dbg('deferred syntax scheduled: %d hunks tick=%d', #deferred_syntax, tick)
|
||||
|
|
@ -1108,6 +1135,7 @@ local function process_pending_clear(bufnr)
|
|||
if entry and entry.pending_clear then
|
||||
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||
entry.highlighted = {}
|
||||
entry.warned_max_lines = {}
|
||||
entry.pending_clear = false
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ describe('highlight', function()
|
|||
local lines = { '@@ -1,100 +1,101 @@' }
|
||||
local hunk_lines = {}
|
||||
for i = 1, 600 do
|
||||
table.insert(lines, ' line ' .. i)
|
||||
table.insert(hunk_lines, ' line ' .. i)
|
||||
table.insert(lines, '+line ' .. i)
|
||||
table.insert(hunk_lines, '+line ' .. i)
|
||||
end
|
||||
|
||||
local bufnr = create_buffer(lines)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue