diffs.nvim/doc/diffs.nvim.txt
Barrett Ruth 10af59a70d feat(config): replace algorithm 'auto'/'native' with 'default'/'vscode'
'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.
2026-02-06 21:23:40 -05:00

439 lines
20 KiB
Text

*diffs.nvim.txt* Syntax highlighting for diffs in Neovim
Author: Barrett Ruth <br.barrettruth@gmail.com>
License: MIT
==============================================================================
INTRODUCTION *diffs.nvim*
diffs.nvim adds syntax highlighting to diff views. It overlays language-aware
highlights on top of default diff highlighting in vim-fugitive and Neovim's
built-in diff mode.
Features: ~
- Syntax highlighting in |:Git| summary diffs and commit detail views
- Diff header highlighting (`diff --git`, `index`, `---`, `+++`)
- Syntax highlighting in |:Gdiffsplit| / |:Gvdiffsplit| side-by-side diffs
- |:Gdiff| command for unified diff against any git revision
- Background-only diff colors for any `&diff` buffer (vimdiff, diffthis, etc.)
- Vim syntax fallback for languages without a treesitter parser
- Blended diff background colors that preserve syntax visibility
- Optional diff prefix (`+`/`-`/` `) concealment
- Gutter (line number) highlighting
==============================================================================
REQUIREMENTS *diffs-requirements*
- Neovim 0.9.0+
- vim-fugitive (https://github.com/tpope/vim-fugitive) (optional, for unified
diff syntax highlighting in |:Git| and commit views)
- Treesitter parsers for languages you want highlighted
Note: The diff mode feature (background-only colors for |:diffthis|, vimdiff,
etc.) works without vim-fugitive.
==============================================================================
SETUP *diffs-setup*
Using lazy.nvim: >lua
{
'barrettruth/diffs.nvim',
dependencies = { 'tpope/vim-fugitive' },
}
<
The plugin works automatically with no configuration required. For
customization, see |diffs-config|.
==============================================================================
CONFIGURATION *diffs-config*
Configuration is done via `vim.g.diffs`. Set this before the plugin loads:
>lua
vim.g.diffs = {
debug = false,
debounce_ms = 0,
hide_prefix = false,
highlights = {
background = true,
gutter = true,
treesitter = {
enabled = true,
max_lines = 500,
},
vim = {
enabled = false,
max_lines = 500,
},
intra = {
enabled = true,
algorithm = 'default',
max_lines = 500,
},
},
fugitive = {
horizontal = 'du',
vertical = 'dU',
},
}
<
*diffs.Config*
Fields: ~
{debug} (boolean, default: false)
Enable debug logging to |:messages| with
`[diffs]` prefix.
{debounce_ms} (integer, default: 0)
Debounce delay in milliseconds for re-highlighting
after buffer changes. Lower values feel snappier
but use more CPU.
{hide_prefix} (boolean, default: false)
Hide diff prefixes (`+`/`-`/` `) using virtual
text overlay. Makes code appear without the
leading diff character. When `highlights.background`
is also enabled, the overlay inherits the line's
background color.
{highlights} (table, default: see below)
Controls which highlight features are enabled.
See |diffs.Highlights| for fields.
{fugitive} (table, default: see below)
Fugitive status buffer keymap options.
See |diffs.FugitiveConfig| for fields.
*diffs.Highlights*
Highlights table fields: ~
{background} (boolean, default: true)
Apply background highlighting to `+`/`-` lines
using `DiffsAdd`/`DiffsDelete` groups (derived
from `DiffAdd`/`DiffDelete` backgrounds).
{gutter} (boolean, default: true)
Highlight line numbers with matching colors.
Only visible if line numbers are enabled.
{treesitter} (table, default: see below)
Treesitter highlighting options.
See |diffs.TreesitterConfig| for fields.
{vim} (table, default: see below)
Vim syntax highlighting options (experimental).
See |diffs.VimConfig| for fields.
{intra} (table, default: see below)
Character-level (intra-line) diff highlighting.
See |diffs.IntraConfig| for fields.
*diffs.TreesitterConfig*
Treesitter config fields: ~
{enabled} (boolean, default: true)
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.
*diffs.VimConfig*
Vim config fields: ~
{enabled} (boolean, default: false)
Use vim syntax highlighting as fallback when no
treesitter parser is available for a language.
Creates a scratch buffer, sets the filetype, and
queries |synID()| per character to extract
highlight groups. Slower than treesitter but
covers languages without a TS parser installed.
{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()|.
*diffs.IntraConfig*
Intra config fields: ~
{enabled} (boolean, default: true)
Enable character-level diff highlighting within
changed lines. When a line changes from `local x = 1`
to `local x = 2`, only the `1`/`2` characters get
an intense background overlay while the rest of the
line keeps the softer line-level background.
{algorithm} (string, default: 'default')
Diff algorithm for character-level analysis.
`'default'`: use |vim.diff()| with settings
inherited from |'diffopt'| (`algorithm` and
`linematch`). `'vscode'`: use libvscodediff FFI
(falls back to default if not available).
{max_lines} (integer, default: 500)
Skip character-level highlighting for hunks larger
than this many lines.
Note: Header context (e.g., `@@ -10,3 +10,4 @@ func()`) is always
highlighted with treesitter when a parser is available.
Language detection uses Neovim's built-in |vim.filetype.match()| and
|vim.treesitter.language.get_lang()|. To customize filetype detection
or register treesitter parsers for custom filetypes, use
|vim.filetype.add()| and |vim.treesitter.language.register()|.
==============================================================================
COMMANDS *diffs-commands*
:Gdiff [revision] *:Gdiff*
Open a unified diff of the current file against a git revision. Displays
in a horizontal split below the current window.
The diff buffer shows `+`/`-` lines with full syntax highlighting for the
code language, plus diff header highlighting for `diff --git`, `---`,
`+++`, and `@@` lines.
Parameters: ~
{revision} (string, optional) Git revision to diff against.
Defaults to HEAD.
Examples: >vim
:Gdiff " diff against HEAD
:Gdiff main " diff against main branch
:Gdiff HEAD~3 " diff against 3 commits ago
:Gdiff abc123 " diff against specific commit
<
:Gvdiff [revision] *:Gvdiff*
Like |:Gdiff| but opens in a vertical split.
:Ghdiff [revision] *:Ghdiff*
Like |:Gdiff| but explicitly opens in a horizontal split.
==============================================================================
FUGITIVE STATUS KEYMAPS *diffs-fugitive*
When inside a vim-fugitive |:Git| status buffer, diffs.nvim provides keymaps
to open unified diffs for files or entire sections.
Keymaps: ~
*diffs-du* *diffs-dU*
du Open unified diff in a horizontal split.
dU Open unified diff in a vertical split.
These keymaps work on:
- File lines (e.g., `M src/foo.lua`) - opens diff for that file
- Section headers (e.g., `Staged (3)`) - opens diff for all files in section
- Hunk/context lines below a file - opens diff for the parent file
Behavior by file status: ~
Status Section Base Current Result ~
M Unstaged index working tree unstaged changes
M Staged HEAD index staged changes
A Staged (empty) index file as all-added
D Staged HEAD (empty) file as all-removed
R Staged HEAD:oldname index:newname content diff
? Untracked (empty) working tree file as all-added
On section headers, the keymap runs `git diff` (or `git diff --cached` for
staged) and displays all changes in that section as a single unified diff.
Untracked section headers show a warning since there is no meaningful diff.
Configuration: ~
*diffs.FugitiveConfig*
>lua
vim.g.diffs = {
fugitive = {
horizontal = 'du', -- keymap for horizontal split, false to disable
vertical = 'dU', -- keymap for vertical split, false to disable
},
}
<
Fields: ~
{horizontal} (string|false, default: 'du')
Keymap for unified diff in horizontal split.
Set to `false` to disable.
{vertical} (string|false, default: 'dU')
Keymap for unified diff in vertical split.
Set to `false` to disable.
==============================================================================
API *diffs-api*
attach({bufnr}) *diffs.attach()*
Manually attach highlighting to a buffer. Called automatically for
fugitive buffers via the `FileType fugitive` autocmd.
Parameters: ~
{bufnr} (integer, optional) Buffer number. Defaults to current buffer.
refresh({bufnr}) *diffs.refresh()*
Manually refresh highlighting for a buffer. Useful after external changes
or for debugging.
Parameters: ~
{bufnr} (integer, optional) Buffer number. Defaults to current buffer.
==============================================================================
IMPLEMENTATION *diffs-implementation*
Summary / commit detail views: ~
1. `FileType fugitive` or `FileType git` (for `fugitive://` buffers)
triggers |diffs.attach()|
2. The buffer is parsed to detect file headers (`M path/to/file`,
`diff --git a/... b/...`) and hunk headers (`@@ -10,3 +10,4 @@`)
3. For each hunk:
- Language is detected from the filename using |vim.filetype.match()|
- Diff prefixes (`+`/`-`/` `) are stripped from code lines
- Code is parsed with |vim.treesitter.get_string_parser()|
- If no treesitter parser and `vim.enabled`: vim syntax fallback via
scratch buffer and |synID()|
- `Normal` extmarks at priority 198 clear underlying diff foreground
- Background extmarks (`DiffsAdd`/`DiffsDelete`) at priority 199
- Syntax highlights are applied as extmarks at priority 200
- Character-level diff extmarks (`DiffsAddText`/`DiffsDeleteText`) at
priority 201 overlay changed characters with an intense background
- Conceal extmarks hide diff prefixes when `hide_prefix` is enabled
4. Re-highlighting occurs on `TextChanged` (debounced) and `Syntax` events
Diff mode views: ~
1. `OptionSet diff` detects when any window enters diff mode
2. All `&diff` windows in the tabpage receive a window-local 'winhighlight'
override that remaps `DiffAdd`/`DiffDelete`/`DiffChange`/`DiffText` to
background-only variants, allowing existing treesitter highlighting to
show through the diff colors
==============================================================================
KNOWN LIMITATIONS *diffs-limitations*
Incomplete Syntax Context ~
*diffs-syntax-context*
Treesitter parses each diff hunk in isolation without surrounding code
context. When a hunk shows lines added to an existing block (e.g., adding a
plugin inside `return { ... }`), the parser doesn't see the `return`
statement and may produce incorrect or unusual highlighting.
This is inherent to parsing code fragments. No diff tooling solves this
problem without significant complexity—the parser simply doesn't have enough
information to understand the full syntactic structure.
Syntax Highlighting Flash ~
*diffs-flash*
When opening a fugitive buffer, there is an unavoidable visual "flash" where
the buffer briefly shows fugitive's default diff highlighting before
diffs.nvim applies treesitter highlights.
This occurs because diffs.nvim hooks into the `FileType fugitive` event,
which fires after vim-fugitive has already painted the buffer. Even with
`debounce_ms = 0`, the re-painting goes through Neovim's event loop.
To minimize the flash, use a low debounce value: >lua
vim.g.diffs = {
debounce_ms = 0,
}
<
Conflicting Diff Plugins ~
*diffs-plugin-conflicts*
diffs.nvim may not interact well with other plugins that modify diff
highlighting or the sign column in diff views. Known plugins that may
conflict:
- diffview.nvim (sindrets/diffview.nvim)
Provides its own diff highlighting and conflict resolution UI.
When using diffview.nvim for viewing diffs, you may want to disable
diffs.nvim's diff mode attachment or use one plugin exclusively.
- mini.diff (echasnovski/mini.diff)
Visualizes buffer differences with its own highlighting system.
May override or conflict with diffs.nvim's background highlighting.
- gitsigns.nvim (lewis6991/gitsigns.nvim)
Generally compatible for sign column decorations, but both plugins
modifying line highlights may produce unexpected results.
- git-conflict.nvim (akinsho/git-conflict.nvim)
Provides conflict marker highlighting that may overlap with
diffs.nvim's highlighting in conflict scenarios.
If you experience visual conflicts, try disabling the conflicting plugin's
diff-related features.
==============================================================================
HIGHLIGHT GROUPS *diffs-highlights*
diffs.nvim defines custom highlight groups. Fugitive unified diff groups use
`default = true`, so colorschemes can override them. Diff mode groups are
always derived from the corresponding `Diff*` groups.
Fugitive unified diff highlights: ~
*DiffsAdd*
DiffsAdd Background for `+` lines. Derived by blending
`DiffAdd` background with `Normal` at 40% alpha.
*DiffsDelete*
DiffsDelete Background for `-` lines. Derived by blending
`DiffDelete` background with `Normal` at 40% alpha.
*DiffsAddNr*
DiffsAddNr Line number for `+` lines. Foreground from
`diffAdded`, background from `DiffsAdd`.
*DiffsDeleteNr*
DiffsDeleteNr Line number for `-` lines. Foreground from
`diffRemoved`, background from `DiffsDelete`.
*DiffsAddText*
DiffsAddText Character-level background for changed characters
within `+` lines. Derived by blending `diffAdded`
foreground with `Normal` background at 70% alpha.
Only sets `bg`, so treesitter foreground colors show
through.
*DiffsDeleteText*
DiffsDeleteText Character-level background for changed characters
within `-` lines. Derived by blending `diffRemoved`
foreground with `Normal` background at 70% alpha.
Diff mode window highlights: ~
These are used for |winhighlight| remapping in `&diff` windows.
*DiffsDiffAdd*
DiffsDiffAdd Background-only. Derived from `DiffAdd` bg.
Treesitter provides foreground syntax highlighting.
*DiffsDiffDelete*
DiffsDiffDelete Foreground and background from `DiffDelete`.
Used for filler lines (`/////`) which have no real
code content to highlight.
*DiffsDiffChange*
DiffsDiffChange Background-only. Derived from `DiffChange` bg.
Treesitter provides foreground syntax highlighting.
*DiffsDiffText*
DiffsDiffText Background-only. Derived from `DiffText` bg.
Treesitter provides foreground syntax highlighting.
To customize these in your colorscheme: >lua
vim.api.nvim_set_hl(0, 'DiffsAdd', { bg = '#2e4a3a' })
vim.api.nvim_set_hl(0, 'DiffsDiffDelete', { link = 'DiffDelete' })
<
==============================================================================
HEALTH CHECK *diffs-health*
Run |:checkhealth| diffs to verify your setup.
Checks performed:
- Neovim version >= 0.9.0
- vim-fugitive is installed (optional)
- libvscode_diff shared library is available (optional)
==============================================================================
ACKNOWLEDGEMENTS *diffs-acknowledgements*
- vim-fugitive (https://github.com/tpope/vim-fugitive)
- codediff.nvim (https://github.com/esmuellert/codediff.nvim)
- diffview.nvim (https://github.com/sindrets/diffview.nvim)
- @phanen (https://github.com/phanen) - diff header highlighting
==============================================================================
vim:tw=78:ts=8:ft=help:norl: