diffs.nvim/doc/diffs.nvim.txt
Barrett Ruth da7b76555a docs: add incomplete syntax context as known limitation
Treesitter parses diff hunks in isolation without surrounding code
context, which can cause incorrect highlighting when hunks show partial
blocks (e.g., adding lines inside `return { ... }` without seeing the
`return`). Document this as a known limitation in README and vimdoc.
2026-02-04 13:16:22 -05:00

300 lines
14 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
- Syntax highlighting in |:Gdiffsplit| / |:Gvdiffsplit| side-by-side diffs
- 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 = 200,
},
},
}
<
*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.
*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.
*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()|.
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()|.
==============================================================================
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()|
- Background extmarks (`DiffsAdd`/`DiffsDelete`) at priority 198
- `Normal` extmarks at priority 199 clear underlying diff foreground
- Syntax highlights are applied as extmarks at priority 200
- 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`.
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)
==============================================================================
vim:tw=78:ts=8:ft=help:norl: