Problem: when fugitive collapses a diff section, Neovim repositions
extmarks from deleted lines onto surviving lines. `carry_forward_highlighted`
matched hunks by content only, carrying forward the "highlighted" flag
for stable hunks and leaving stale extmarks from removed hunks uncleaned.
Solution: add `#entry.hunks == #hunks` guard to the carry-forward
precondition. When hunk count changes, skip carry-forward and set
`pending_clear = true` so `on_buf` wipes all extmarks before
`on_win` re-highlights visible hunks.
Problem: single-row extmarks (`end_row = buf_line, end_col = len`) do
not trigger `hl_eol` — Neovim requires `end_row > start_row`. Line
backgrounds stopped at the last character instead of extending to the
window edge.
Solution: use `end_row = buf_line + 1, end_col = 0` for line bg
extmarks. Replace per-hunk `nvim_buf_clear_namespace` with
`clear_ns_by_start` that queries extmarks by start position only,
so adjacent hunks' trailing `end_row` is never killed.
Problem: `line_hl_group` bg unconditionally overrides `hl_group` bg
regardless of priority (neovim/neovim#31151). `DiffsAddText`/
`DiffsDeleteText` at p201 were invisible under `DiffsAdd`/`DiffsDelete`
`line_hl_group` at p200 because they operate on separate stacking layers.
Solution: replace `line_hl_group` with `hl_group` + `hl_eol` +
`end_col` for line backgrounds, putting them on the same layer as
intra-line highlights so priority governs stacking. Use single-row
extmarks (`end_row = buf_line`) to avoid the adjacent-hunk clearing
bug. Split `number_hl_group` into a separate extmark. Use raw
`DiffAdd.bg`/`DiffDelete.bg` for `DiffsAddText`/`DiffsDeleteText`
instead of alpha-blending toward Normal.bg, which produced
indistinguishable colors on dark themes.
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.