diff --git a/doc/diffs.nvim.txt b/doc/diffs.nvim.txt index b65e3a6..c0290d9 100644 --- a/doc/diffs.nvim.txt +++ b/doc/diffs.nvim.txt @@ -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. diff --git a/lua/diffs/highlight.lua b/lua/diffs/highlight.lua index 434702d..5b6c1d7 100644 --- a/lua/diffs/highlight.lua +++ b/lua/diffs/highlight.lua @@ -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 diff --git a/lua/diffs/init.lua b/lua/diffs/init.lua index 1f41725..0cbbb18 100644 --- a/lua/diffs/init.lua +++ b/lua/diffs/init.lua @@ -26,6 +26,7 @@ ---@field gutter boolean ---@field blend_alpha? number ---@field overrides? 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 ---@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 diff --git a/spec/highlight_spec.lua b/spec/highlight_spec.lua index 34101ea..22b884c 100644 --- a/spec/highlight_spec.lua +++ b/spec/highlight_spec.lua @@ -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)