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.
Problem: treesitter parses each diff hunk in isolation, so incomplete
syntax constructs at hunk boundaries (e.g., a function definition with
no body) produce ERROR nodes and drop captures.
Solution: read N lines from the on-disk file before/after each hunk and
prepend/append them as unmapped padding lines. The line_map guard in
highlight_treesitter skips extmarks for unmapped lines, so padding
provides syntax context without visual output. Controlled by
highlights.context (default 25, 0 to disable). Also applies to the vim
syntax fallback path via a leading_offset filter.
Problem: highlight_treesitter concatenated all hunk lines (context, -,
+) into a single string. Mixed old/new code produced invalid syntax
(e.g. two return statements), causing treesitter error recovery to drop
captures on lines after the syntax error.
Solution: split hunk lines into two versions — new (context + added)
and old (context + deleted) — each parsed independently. Use a line_map
to resolve treesitter row indices to buffer lines, with the old version
only mapping deleted lines to avoid duplicate extmarks on context.
Also fixes three related issues exposed by the improved TS coverage:
- Replace Normal extmark with DiffsClear (explicit fg from Normal.fg).
Normal in extmarks doesn't reliably override vim :syntax foreground.
- Reorder priority stack to DiffsClear(198) < syntax(199) < line
bg(200) < char bg(201). TS capture groups can carry colorscheme
backgrounds that would override diff line backgrounds at higher
priority.
- Gate DiffsClear on per-line coverage tracking. Only clear fugitive
syntax fg on lines where TS/vim actually produced captures, preventing
force-clearing on lines where error recovery drops captures.
Problem: running :e on a :Gdiff buffer cleared all content because
diffs:// buffers had no BufReadCmd handler. Neovim tried to read the
buffer name as a file path, found nothing on disk, and emptied the
buffer. This affected all three buffer creation paths (gdiff,
gdiff_file, gdiff_section).
Solution: register a BufReadCmd autocmd for diffs://* that parses the
URL and regenerates diff content from git. Change buffer options from
nofile/wipe to nowrite/delete (matching fugitive's approach) so
buffer-local autocmds and variables survive across unload/reload
cycles. Store old filepath as buffer variable for rename support.
'default' inherits algorithm and linematch from diffopt, 'vscode' uses
the FFI library. Removes the need for diffs.nvim to duplicate settings
that users already control globally.
line_hl_group bg occupies a separate rendering channel from hl_group in
Neovim's extmark system, causing character-level bg-only highlights to be
invisible regardless of priority. Switching to hl_group + hl_eol ensures
all backgrounds compete in the same channel.
Also reorders priorities (Normal 198 < line bg 199 < syntax 200 < char
bg 201), bumps char-level blend alpha from 0.4 to 0.7 for visibility,
and adds debug logging throughout the intra pipeline.
Line-level backgrounds (DiffsAdd/DiffsDelete) now get a second tier:
changed characters within modified lines receive an intense background
overlay (DiffsAddText/DiffsDeleteText at 70% alpha vs 40% for lines).
Treesitter foreground colors show through since the extmarks only set bg.
diff.lua extracts contiguous -/+ change groups from hunk lines and diffs
each group byte-by-byte using vim.diff(). An optional libvscodediff FFI
backend (lib.lua) auto-downloads the .so from codediff.nvim releases and
falls back to native if unavailable.
New config: highlights.intra.{enabled, algorithm, max_lines}. Gated by
max_lines (default 200) to avoid stalling on huge hunks. Priority 201
sits above treesitter (200) so the character bg always wins.
Closes#60
When a file has no extension but contains a shebang (e.g., `#!/bin/bash`),
filetype detection now reads the first 10 lines from disk and uses
`vim.filetype.match({ filename, contents })` for content-based detection.
This enables syntax highlighting for files like `build` scripts that rely
on shebang detection, even when the file isn't open in a buffer.
Detection order:
1. Existing buffer's filetype (already implemented in #69)
2. File content (shebang/modeline) - NEW
3. Filename extension only
Also adds `filetype on` to test helpers to ensure `vim.g.ft_ignore_pat`
is set, which is required for shell detection.
When pressing `du`/`dU` from a hunk line in the fugitive status buffer
(after expanding with `=`), the unified diff now opens at the
corresponding line instead of line 1.
Implementation:
- `fugitive.get_hunk_position()` returns @@ header and offset when on a hunk line
- `commands.find_hunk_line()` finds matching @@ header in diff buffer
- `commands.gdiff_file()` accepts optional `hunk_position` and jumps after opening
Also updates @phanen's README credit for the previous two fixes.
Closes#65
Files detected via shebang or modeline (e.g., `build` with `#!/bin/bash`)
weren't getting syntax highlighting because `vim.filetype.match()` only
does filename-based detection.
Now `get_ft_from_filename()` first checks if a buffer already exists for
the file and uses its detected filetype. This requires knowing the repo
root to construct the full path, so:
- `parse_buffer()` reads `b:diffs_repo_root` or `b:git_dir` (fugitive)
- `commands.lua` sets `b:diffs_repo_root` on diff buffers it creates
Co-authored-by: phanen <phanen@qq.com>
Previously, hunks were discarded entirely if vim.filetype.match()
returned nil. This meant files with unrecognized extensions got no
highlighting at all - not even the basic green/red backgrounds for
added/deleted lines.
Remove the (current_lang or current_ft) condition from flush_hunk()
so all hunks are collected. highlight_hunk() already handles the
case where ft/lang are nil by skipping syntax highlighting but still
applying background colors.
Co-authored-by: phanen <phanen@qq.com>
Parse both old and new filenames from rename lines (R old -> new).
When diffing staged renames, use old filename as base to correctly
show content changes rather than treating the file as entirely new.
Also adds comprehensive tests for filename edge cases:
- Double extensions, hyphens, underscores, dotfiles
- Deep nested paths, complex renames
- Documents known limitation with filenames containing ' -> '
Section headers (Staged/Unstaged) now show all diffs in that section,
matching fugitive's behavior. Untracked files show as all-added diffs.
Deleted files show as all-removed diffs.
Also handles edge cases:
- Empty new/old content for deleted/new files
- Section header detection returns is_header flag
Compares current buffer against any git revision (default HEAD), opens result
with full diffs.nvim syntax highlighting. Follows fugitive convention:
:Gdiff/:Gvdiff open vertical split, :Ghdiff opens horizontal split.
Apply treesitter highlighting to diff metadata lines (diff --git, index,
---, +++) using the diff language parser. Header info is attached only
to the first hunk of each file to avoid duplicate highlighting.
Based on PR #52 by @phanen with fixes:
- header_lines now only contains diff metadata, not hunk content
- header info attached only to first hunk per file
- removed arbitrary hunk count restriction