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.
Problem: colorschemes with `transparent_mode = true` set Normal
with no bg. `compute_highlight_groups` fell back to `0x1a1a1a`
and blended all highlight groups against it, producing wrong
colors. The `ColorScheme` autocmd also failed to overwrite stale
values because every `nvim_set_hl` call used `default = true`.
Solution: detect transparent Normal via `not normal.bg`. When
transparent, pass `bg = normal.bg` (nil) to `DiffsClear` and
use colorscheme DiffAdd/DiffDelete colors directly without
blending. Reset `hl_retry_pending` on `ColorScheme` so the
retry mechanism re-arms after a theme switch.
## Problem
Integration keys (`fugitive`, `neogit`, `gitsigns`, `committia`,
`telescope`)
live at the top level of `vim.g.diffs`, cluttering the config namespace.
## Solution
Move them under `vim.g.diffs.integrations.*`. Old top-level keys still
work but
emit `vim.deprecate` targeting v0.3.2. `compute_filetypes` and
`plugin/diffs.lua`
fall back to legacy keys for pre-`init()` callers.
Also includes `83c17ac` which fixes an invalid hex hash in the combined
diff test
fixture.
## Problem
Combined diff test fixtures used `ghi9012` as a result hash, but `g`,
`h`, `i` are not hex digits (`%x` matches `[0-9a-fA-F]`). The `%x+`
pattern in `highlight.lua` correctly rejected this, so the manual
`@constant.diff` extmark for the result hash was never set. The existing
assertion passed anyway because the diff grammar's captures on parent
hashes (`abc1234`, `def5678`) satisfied the row-level check.
## Solution
Replace `ghi9012` with valid hex `a6b9012` in all fixtures. Tighten the
result hash assertion to verify exact column range (cols 23-30) so it
cannot be satisfied by parent hash captures.
Closes#168
Closes#169.
## Problem
Telescope never sets `filetype=diff` on preview buffers — it calls
`vim.treesitter.start(bufnr, "diff")` directly, so diffs.nvim's
`FileType` autocmd never fires.
## Solution
Add a `telescope` config toggle (same pattern as
neogit/gitsigns/committia) and a `User TelescopePreviewerLoaded` autocmd
that calls `attach()` on the preview buffer. Disabled by default; enable
with `telescope = true`.
Also adds a `diffs-telescope` vimdoc section documenting the integration
and the upstream first-line preview bug
(nvim-telescope/telescope.nvim#3626).
Includes committia.vim integration from `feat/committia`.
## Problem
committia.vim's diff pane (`ft=git`, buffer name `__committia_diff__`)
is rejected by the `ft=git` guard in the `FileType` callback, preventing
diffs.nvim from highlighting it.
## Solution
Add a `committia` config toggle following the same pattern as
`neogit`/`gitsigns`. When enabled, the `ft=git` guard also allows
committia's `__committia_diff__` buffer through.
Closes#161
## Problem
The vimdoc had several issues: integration sections (fugitive, neogit,
gitsigns) were scattered as top-level entries with no grouping, the
intro implied diffs.nvim works automatically with fugitive/neogit out of
the box, the neogit section described a highlight override workaround
that was already removed, and `extra_filetypes` didn't mention picker
support.
## Solution
- Rewrite intro to document default behavior: `gitcommit` highlighting,
conflict detection, `&diff` winhighlight — everything else is opt-in
- Remove vim-fugitive as a dependency in setup
- Add `|diffs-integrations|` parent section grouping fugitive, neogit,
and gitsigns
- Add fugitive intro paragraph mentioning `:Gdiff`
- Trim neogit section (remove stale highlight override docs)
- Trim gitsigns section (remove implementation details)
- Add `committia` config field docs
- Expand `extra_filetypes` docs to mention telescope, snacks, and
fzf-lua
## Problem
Integration docs (fugitive, neogit, gitsigns) were scattered as
top-level sections with no grouping, making it hard to find all
supported plugin integrations in one place.
## Solution
Add `|diffs-integrations|` parent section that groups all integration
subsections under a single TOC entry. Fugitive, neogit, and gitsigns are
now subsections (using `---` separators) under the new `INTEGRATIONS`
heading. The intro paragraph documents the two attachment patterns:
automatic (config toggles like `fugitive = true`) and opt-in
(`extra_filetypes`). Conflict resolution and merge diff resolution
remain as standalone top-level sections. TOC renumbered accordingly.
## Problem
`extra_filetypes = { 'diff' }` enables highlighting in telescope,
snacks, and fzf-lua git preview buffers, but this was not documented
beyond a brief mention of `.diff` files.
## Solution
Add a README FAQ entry and expand the vimdoc `extra_filetypes` field
description to mention specific pickers and which previewer styles are
supported.
## Problem
The `is_fugitive_buffer` guard in the `FileType` callback checked the
buffer name for `fugitive://` without checking whether the `fugitive`
integration was enabled. `ft=git` fugitive buffers got highlighted even
with `fugitive = false` (the default).
## Solution
Check `get_fugitive_config()` before `is_fugitive_buffer()`. When
`fugitive = false` (default), no `ft=git` buffer gets through the guard.
## Problem
gitsigns' `:Gitsigns blame_line` popup shows flat
`GitSignsAddPreview`/`GitSignsDeletePreview` line highlights with basic
word-level inline diffs, but no treesitter syntax or diffs.nvim's
character-level intra-line highlighting.
## Solution
Add `lua/diffs/gitsigns.lua` which patches gitsigns' `Popup.create` and
`Popup.update` to intercept blame popups. Parses `Hunk N of M` sections
from the popup buffer, clears gitsigns' own `gitsigns_popup` namespace
on the diff region, and applies `highlight_hunk` with manual
`@diff.plus`/`@diff.minus` prefix extmarks. Uses a separate
`diffs-gitsigns` namespace to avoid colliding with the main decoration
provider.
Enabled via `vim.g.diffs = { gitsigns = true }`. Wired in
`plugin/diffs.lua` with a `User GitAttach` lazy-load retry for when
gitsigns loads after diffs.nvim. Config plumbing adds
`get_highlight_opts()` as a public getter, replacing the
`debug.getupvalue` hack used by the standalone `blame_hl.nvim` plugin.
Closes#155.
## Problem
Neovim 0.11+ deprecated the table-based `vim.validate({...})` form.
Every
plugin load produces 5 deprecation warnings pointing at `init.lua`
config
validation.
## Solution
Convert all `vim.validate` calls in `init()` to the new positional
`vim.validate(name, value, validator, optional_or_msg)` form. No
behavioral
change — identical validation logic, just the calling convention.
## Problem
Highlight group fallbacks in `compute_highlight_groups` were hardcoded
to
catppuccin mocha colors, producing wrong results for any other
colorscheme
when `Normal.bg` is nil. This happens on transparent terminals or when
the
colorscheme loads after the first diff buffer opens.
## Solution
Replace hardcoded fallbacks with `vim.o.background`-aware neutral
values.
When `Normal.bg` is still absent after initial computation, schedule a
single
deferred retry via `vim.schedule` that recomputes and invalidates all
attached
buffer caches. Document the load-order requirement in the setup section.
## Problem
`highlights.context.enabled` and `highlights.context.lines` were
defined, validated, and range-checked but never read during
highlighting. Hunks inside incomplete constructs (e.g., a table literal
or function body whose opening is beyond the hunk's own context lines)
parsed incorrectly because treesitter had no surrounding code.
## Solution
`compute_hunk_context` in `init.lua` reads the working tree file using
the hunk's `@@ +start,count @@` line numbers to collect up to `lines`
(default 25) surrounding code lines in each direction. Files are read
once via `io.open` and cached across hunks in the same file.
`highlight_treesitter` in `highlight.lua` accepts an optional context
parameter that prepends/appends context lines to the parse string and
offsets capture rows by the prefix count, so extmarks only land on
actual hunk lines. Wired through `highlight_hunk` for the two
code-language treesitter calls (not headers, not `highlight_text`, not
vim syntax).
Closes#148.
## Problem
Languages without a treesitter parser (COBOL, Fortran, etc.) got no
syntax highlighting because \`highlights.vim.enabled\` defaulted to
\`false\`.
## Solution
Flip the default to \`true\`. The vim syntax path is already deferred
via \`vim.schedule\` so it never blocks the first paint. \`max_lines =
200\` stays unchanged — appropriate given the ~30x slower per-hunk cost
vs treesitter.
## Problem
Email-quoted diffs (`> diff --git ...`, `> @@ ...`) from git-send-email
/ email reply workflows produce 0 hunks because the parser matches
patterns against raw lines containing `> ` quote prefixes. Closes#141.
## Solution
Strip the `> ` quote prefix before pattern matching in the parser. Store
`quote_width` on each hunk. In `highlight.lua`, offset all extmark
column positions by `qw` and expand `pw > 1` guards to `qw > 0 or pw >
1` for DiffsClear suppression. Clamp body prefix DiffsClear `end_col` to
the actual buffer line byte length for bare `>` lines (1-byte buffer
lines where `end_col = pw + qw` would exceed bounds and cause
`nvim_buf_set_extmark` to silently fail inside `pcall`).
15 new specs covering parser detection, stripping, false-positive
rejection, and highlight column offsets including the bare `>` clamp
edge case.
## Problem
diffs.nvim blanked `NeogitDiffContextHighlight` globally on attach and
on `ColorScheme` to work around Neogit's `ViewContext` decoration
provider overriding `DiffsAdd`/`DiffsDelete` line backgrounds with
`NeogitDiffContextHighlight` at priority 200.
## Solution
Remove the `override_neogit_context_highlights` workaround.
NeogitOrg/neogit#1907 moves the `neogit_disable_hunk_highlight` check
inside ViewContext's per-line loop, so non-cursor lines skip
`add_line_highlight` entirely.
`vim.b[bufnr].neogit_disable_hunk_highlight = true` (set on attach) is
sufficient.
Closes#135
## Problem
Two-pass rendering (Pass 1: backgrounds + intra-line; Pass 2:
treesitter) caused Pass 2 to re-apply all extmarks that Pass 1 already
set, doubling the extmark count on affected lines.
## Solution
Add `syntax_only` mode to `highlight_hunk`. When `syntax_only = true`,
only treesitter syntax and content `DiffsClear` extmarks are applied —
backgrounds, intra-line, prefix clears, and per-char prefix highlights
are skipped. Pass 2 now uses `syntax_only = true` and no longer calls
`nvim_buf_clear_namespace`, so Pass 1's extmarks persist while Pass 2
layers syntax on top.
Closes#143
## Problem
`highlight_hunk` applied DiffsClear extmarks across 5 scattered sites
with
ad-hoc column arithmetic. This fragmentation produced the 1-column
DiffsClear
gap on email-quoted body context lines (#142 issue 1). A redundant
`highlight_hunk_vim_syntax` function duplicated the inline vim syntax
path,
and the deferred pass in init.lua double-called it, creating duplicate
scratch
buffers and extmarks.
## Solution
Reorganize `highlight_hunk` into two clean phases:
- **Phase 1** — multi-line syntax computation (treesitter, vim syntax,
diff
grammar, header context text). Sets syntax extmarks only, no DiffsClear.
- **Phase 2** — per-line chrome (DiffsClear, backgrounds, gutter,
overlays,
intra-line). All non-syntax extmarks consolidated in one pass.
Hoist `new_code` to function scope (needed by `highlight_text` outside
the
`use_ts` block). Hoist `at_raw_line` so Phase 1d and Phase 2b share one
`nvim_buf_get_lines` call.
Delete `highlight_hunk_vim_syntax` (redundant with inline path). Remove
the
double-call from the deferred pass in init.lua.
Extend body prefix DiffsClear `end_col` from `qw` to `pw + qw`, fixing
the
1-column gap where native treesitter background bled through on context
lines
in email-quoted diffs (#142 issue 1).
### Email-quoted diff support
The parser now strips `> ` (and `>> `, etc.) email quote prefixes before
pattern matching, enabling syntax highlighting for diffs embedded in
email
replies and `git-send-email` / sourcehut-style patch review threads.
Each hunk stores `quote_width` so the highlight pipeline can apply
`DiffsClear` at the correct column offsets to suppress native treesitter
on quoted regions.
Closes#141
### #142 status after this PR
| Sub-issue | Status |
|-----------|--------|
| 1. Col gap on context lines | Fixed |
| 2. Bare `>` context lines | Improved, edge case remains |
| 3. Diff prefix marker fg | Not addressed (follow-up) |
## Problem
The vimdoc has 16 sections but no table of contents, making it hard
to navigate with `:help diffs`.
## Solution
Add a numbered `CONTENTS` section with dot-leader formatting and
`|tag|` links to each existing section, matching the style used in
the project's other plugins.
## Problem
Repeatedly toggling `=` in fugitive left green gutter
(`number_hl_group`)
extmarks on lines between sections. When fugitive collapses a diff
section,
Neovim compresses extmarks from deleted lines onto the next surviving
line
(the `M ...` file entry). Two issues prevented cleanup:
1. `carry_forward_highlighted` returned `{}` (truthy in Lua) when zero
hunks
matched, so `pending_clear` stayed `false` and the compressed extmarks
were never cleared.
2. The `nvim_buf_clear_namespace` call in `on_buf`'s `pending_clear`
path was
removed in 2feb8a8, so even when `pending_clear` was `true` the extmarks
survived.
## Solution
Return `nil` from `carry_forward_highlighted` when no hunks were carried
forward (`next(highlighted) == nil`), so `pending_clear` is correctly
set to
`true`. Restore `nvim_buf_clear_namespace` in `on_buf`'s `pending_clear`
block. Add `process_pending_clear` test helper and spec coverage.
## Problem
Toggling large diffs via fugitive's `=` caused the top of the buffer to
re-render and glitch. `ensure_cache` always created a new cache entry
with
`pending_clear=true` and `highlighted={}`, forcing `on_win` to clear and
re-highlight every visible hunk — including stable ones above the toggle
point that never changed.
## Solution
On reparse, compare old and new hunk lists using a prefix + suffix
matching
strategy. Hunks that match (same filename, line count, and sampled
content)
carry forward their `highlighted` state so `on_win` skips them.
Comparison
is O(1) per hunk. Only runs when the old entry had
`pending_clear=false`;
`invalidate_cache`/`ColorScheme` paths still force full re-highlight.
Closes#131
## Problem
Regressions #119 and #120 showed the test suite had no coverage of the
decoration provider cache pipeline, no end-to-end pipeline tests from
buffer content to extmarks, and no Neogit-specific integration tests.
## Solution
Adds three new spec files (28 new tests, 316 total):
- `spec/decoration_provider_spec.lua` — indirect cache pipeline tests
via `_test` table: `ensure_cache` population, content fingerprint guard,
`pending_clear` semantics, BufWipeout cleanup
- `spec/integration_spec.lua` — full pipeline: diff buffer → `attach` →
extmarks; verifies `DiffsAdd`/`DiffsDelete` on correct lines, treesitter
captures, multi-hunk coverage
- `spec/neogit_integration_spec.lua` — `neogit_disable_hunk_highlight`
behavior, NeogitStatus/NeogitDiffView attach and cache population,
parser neogit filename patterns
Depends on #133.
Closes#122
## Problem
diffs.nvim was blanking 18 Neogit highlight groups globally on attach to
prevent Neogit's `line_hl_group` fg from stomping treesitter syntax. It
also fell back to `getcwd()` plus a subprocess call for repo root
detection on Neogit buffers, and had no mechanism to refresh the hunk
cache when Neogit lazy-loaded new diff sections.
## Solution
Adopts three APIs introduced in NeogitOrg/neogit#1897:
- Sets `vim.b.neogit_disable_hunk_highlight = true` on the Neogit buffer
at attach time. Neogit's `HunkLine` renderer skips all its own highlight
logic when this is set, replacing the need to blank 18 hl groups
globally and the associated ColorScheme re-application.
- Reads `vim.b.neogit_git_dir` in `get_repo_root()` as a reliable
fallback between the existing `b:git_dir` check and the `getcwd()`
subprocess path.
- Registers a buffer-local `User NeogitDiffLoaded` autocmd on attach
that calls `M.refresh()` when Neogit lazy-loads a new diff section,
keeping the hunk cache in sync.
Closes#128
## Problem
The default conflict navigation keymaps `]x`/`[x` are non-standard. Vim
natively uses `]c`/`[c` for diff navigation, so the same keys are far
more
intuitive for conflict jumping.
## Solution
Change the defaults for `conflict.keymaps.next` and
`conflict.keymaps.prev`
to `]c` and `[c`. This is a breaking change for users relying on the
previous
defaults without explicit configuration.
Problem: lua-language-server is not available in the dev shell, making
it impossible to run local type checks.
Solution: add lua-language-server to the devShell packages.
## Problem
Git diff metadata lines like "new file mode 100644" and "deleted file
mode 100644" matched the neogit "new file" and "deleted" filename
patterns in the parser, corrupting the current filename and breaking
syntax highlighting for subsequent hunks.
Closes#120
## Solution
Add negative guards so "new file mode" and "deleted file mode" lines
are skipped before the neogit filename capture runs. The guard must
evaluate before the capture due to Lua's and/or short-circuit semantics
— otherwise the and-operator returns true instead of the captured
string.
Added 16 parser tests covering all neogit filename patterns, all git
diff extended header lines that could collide, and integration scenarios
with mixed neogit status + diff metadata buffers.
## Problem
Users had to pass `enabled = true` or `enabled = false` inside
fugitive/neogit config tables, which was redundant — table presence
already implied the integration should be active.
## Solution
Remove the `enabled` field from the public API. Table presence now
implies enabled, `false` disables, `true` expands to sub-defaults.
The `enabled` field is still accepted for backward compatibility.
Added 20 `compute_filetypes` tests covering all config shapes (true,
false, table, nil, backward-compat enabled field). Updated docs
and type annotations.
## Problem
Users attempt to lazy-load diffs.nvim with `event`, `ft`, `lazy`, or
`keys` options, which interferes with the plugin's own `FileType`
autocmd registered at startup.
## Solution
Add a FAQ entry with a correct lazy.nvim snippet using `init` and a
note explaining that the plugin lazy-loads itself.
## Problem
The neogit commit (3d640c2) switched line background extmarks from
`hl_group`+`hl_eol` to `line_hl_group`. Due to [neovim#31151][1],
`line_hl_group` bg overrides `hl_group` bg regardless of extmark
priority. This made `DiffsAddText`/`DiffsDeleteText` intra-line
highlights invisible beneath line backgrounds — the extmarks were
placed correctly but Neovim rendered the line bg on top.
[1]: https://github.com/neovim/neovim/issues/31151
## Solution
Revert line backgrounds to `hl_group`+`hl_eol` where priority stacking
works correctly. Keep `number_hl_group` in a separate point extmark to
prevent gutter color bleeding to adjacent lines. The Neogit highlight
override (clearing their groups to `{}`) is independent and unaffected.
## Problem
Commit 0f27488 changed fugitive and neogit integrations from enabled by
default
to disabled by default. Users who never explicitly set these keys in
their config
saw no deprecation notice and silently lost integration support. The
existing
deprecation warning only fires for the old `filetypes` key, missing the
far more
common case of users who had no explicit config at all.
## Solution
Add an ephemeral migration check in `init()` that emits a `vim.notify`
warning
at WARN level when `fugitive`, `neogit`, and `diff` (via
`extra_filetypes`) are
all absent from the user's config. This covers the gap between the old
`filetypes`
deprecation and users who relied on implicit defaults. To be removed in
0.3.0.
## TODO
1. docs (vimdoc + readme) - this is a non-trivial feature
2. push luarocks version
## Problem
diffs.nvim only activates on `fugitive`, `git`, and `gitcommit`
filetypes.
Neogit uses its own custom filetypes (`NeogitStatus`,
`NeogitCommitView`,
`NeogitDiffView`) and doesn't set `b:git_dir`, so the plugin never
attaches
and repo root resolution fails for filetype detection within diff hunks.
## Solution
Two changes:
1. **`lua/diffs/init.lua`** — Add the three Neogit filetypes to the
default
`filetypes` list. The `FileType` autocmd in `plugin/diffs.lua` already
handles them correctly since the `is_fugitive_buffer` guard only applies
to the `git` filetype.
2. **`lua/diffs/parser.lua`** — Add a CWD-based fallback in
`get_repo_root()`.
After the existing `b:diffs_repo_root` and `b:git_dir` checks, fall back
to
`vim.fn.getcwd()` via `git.get_repo_root()` (already cached). Without
this,
the parser can't resolve filetypes for files in Neogit buffers.
Neogit's expanded diffs use standard unified diff format, so the parser
handles
them without modification.
Closes#110.