A Neovim plugin that provides VSCode-style side-by-side diff rendering with two-tier highlighting.
2025-11-24.02.25.13.mov
Demo: Quick walkthrough of diff features
- Two-tier highlighting system:
- Light backgrounds for entire modified lines (green for insertions, red for deletions)
- Deep/dark character-level highlights showing exact changes within lines
- Side-by-side diff view in a new tab with synchronized scrolling
- Git integration: Compare between any git revision (HEAD, commits, branches, tags)
- Same implementation as VSCode's diff engine, providing identical visual highlighting for most scenarios
- Fast C-based diff computation using FFI with multi-core parallelization (OpenMP)
- Async git operations - non-blocking file retrieval from git
- Neovim >= 0.7.0 (for Lua FFI support; 0.10+ recommended for vim.system)
- Git (for git diff features)
curlorwget(for automatic binary download)nui.nvim(for explorer UI)
No compiler required! The plugin automatically downloads pre-built binaries from GitHub releases.
Minimal installation:
{
"esmuellert/vscode-diff.nvim",
dependencies = { "MunifTanjim/nui.nvim" },
cmd = "CodeDiff",
}Note: The plugin automatically adapts to your colorscheme's background (dark/light). It uses
DiffAddandDiffDeletefor line-level diffs, and auto-adjusts brightness for character-level highlights (1.4x brighter for dark themes, 0.92x darker for light themes). See Highlight Groups for customization.
With custom configuration:
{
"esmuellert/vscode-diff.nvim",
dependencies = { "MunifTanjim/nui.nvim" },
cmd = "CodeDiff",
config = function()
require("vscode-diff").setup({
-- Highlight configuration
highlights = {
-- Line-level: accepts highlight group names or hex colors (e.g., "#2ea043")
line_insert = "DiffAdd", -- Line-level insertions
line_delete = "DiffDelete", -- Line-level deletions
-- Character-level: accepts highlight group names or hex colors
-- If specified, these override char_brightness calculation
char_insert = nil, -- Character-level insertions (nil = auto-derive)
char_delete = nil, -- Character-level deletions (nil = auto-derive)
-- Brightness multiplier (only used when char_insert/char_delete are nil)
-- nil = auto-detect based on background (1.4 for dark, 0.92 for light)
char_brightness = nil, -- Auto-adjust based on your colorscheme
},
-- Diff view behavior
diff = {
disable_inlay_hints = true, -- Disable inlay hints in diff windows for cleaner view
max_computation_time_ms = 5000, -- Maximum time for diff computation (VSCode default)
},
-- Keymaps in diff view
keymaps = {
view = {
quit = "q", -- Close diff tab
toggle_explorer = "<leader>b", -- Toggle explorer visibility (explorer mode only)
next_hunk = "]c", -- Jump to next change
prev_hunk = "[c", -- Jump to previous change
next_file = "]f", -- Next file in explorer mode
prev_file = "[f", -- Previous file in explorer mode
},
explorer = {
select = "<CR>", -- Open diff for selected file
hover = "K", -- Show file diff preview
refresh = "R", -- Refresh git status
},
},
})
end,
}The C library will be downloaded automatically on first use. No build step needed!
The plugin automatically manages the C library installation:
Automatic Updates:
- The library is automatically downloaded on first use
- When you update the plugin to a new version, the library is automatically updated to match
- No manual intervention required!
Manual Installation Commands:
" Install/update the library manually
:CodeDiff install
" Force reinstall (useful for troubleshooting)
:CodeDiff install!Version Management:
The installer reads the VERSION file to download the matching library version from GitHub releases. This ensures compatibility between the Lua code and C library.
If you prefer to install manually without a plugin manager:
- Clone the repository:
git clone https://github.com/esmuellert/vscode-diff.nvim ~/.local/share/nvim/vscode-diff.nvim- Add to your Neovim runtime path in
init.lua:
vim.opt.rtp:append("~/.local/share/nvim/vscode-diff.nvim")- Install the C library:
The plugin requires a C library binary in the plugin root directory. The plugin auto-detects these filenames:
libvscode_diff.soorlibvscode_diff_<version>.so(Linux/BSD)libvscode_diff.dyliborlibvscode_diff_<version>.dylib(macOS)libvscode_diff.dllorlibvscode_diff_<version>.dll(Windows)
Option A: Download from GitHub releases (recommended)
Download the appropriate binary from the GitHub releases page and place it in the plugin root directory. Rename it to match the expected format: libvscode_diff.so/.dylib/.dll or libvscode_diff_<version>.so/.dylib/.dll. Linux users: If your system lacks OpenMP, also download libgomp_linux_{arch}_{version}.so.1 and rename it to libgomp.so.1 in the same directory.
Option B: Build from source
Build requirements: C compiler (GCC/Clang/MSVC/MinGW) or CMake 3.15+
Using build scripts (no CMake required):
# Linux/macOS/BSD
./build.sh
# Windows
build.cmdOr using CMake:
cmake -B build
cmake --build buildBoth methods automatically place the library in the plugin root directory.
The :CodeDiff command supports multiple modes:
Open an interactive file explorer showing changed files:
" Show git status in explorer (default)
:CodeDiff
" Show changes for specific revision in explorer
:CodeDiff HEAD~5
" Compare against a branch
:CodeDiff main
" Compare against a specific commit
:CodeDiff abc123
" Compare two revisions (e.g. HEAD vs main)
:CodeDiff main HEADCompare the current buffer with a git revision:
" Compare with last commit
:CodeDiff file HEAD
" Compare with previous commit
:CodeDiff file HEAD~1
" Compare with specific commit
:CodeDiff file abc123
" Compare with branch
:CodeDiff file main
" Compare with tag
:CodeDiff file v1.0.0
" Compare two revisions for current file
:CodeDiff file main HEADRequirements:
- Current buffer must be saved to a file
- File must be in a git repository
- Git revision must exist
Behavior:
- Left buffer: Git version (at specified revision) - readonly
- Right buffer: Current buffer content - readonly
- Opens in a new tab automatically
- Async operation - won't block Neovim
Compare two arbitrary files side-by-side:
:CodeDiff file file_a.txt file_b.txt-- Primary user API - setup configuration
require("vscode-diff").setup({
highlights = {
line_insert = "DiffAdd",
line_delete = "DiffDelete",
char_brightness = 1.4,
},
})
-- Advanced usage - direct access to internal modules
local diff = require("vscode-diff.diff")
local render = require("vscode-diff.render")
local git = require("vscode-diff.git")
-- Example 1: Compute diff between two sets of lines
local lines_a = {"line 1", "line 2"}
local lines_b = {"line 1", "modified line 2"}
local lines_diff = diff.compute_diff(lines_a, lines_b)
-- Example 2: Get file content from git (async)
git.get_file_content("HEAD~1", "/path/to/repo", "relative/path.lua", function(err, lines)
if err then
vim.notify(err, vim.log.levels.ERROR)
return
end
-- Use lines...
end)
-- Example 3: Get git root for a file (async)
git.get_git_root("/path/to/file.lua", function(err, git_root)
if not err then
-- File is in a git repository
end
end)-
C Module (
libvscode-diff/): Fast diff computation and render plan generation- Myers diff algorithm
- Character-level refinement for highlighting
- Matches VSCode's
rangeMapping.tsdata structures
-
Lua FFI Layer (
lua/vscode-diff/diff.lua): Bridge between C and Lua- FFI declarations matching C structs
- Type conversions between C and Lua
-
Render Module (
lua/vscode-diff/render/): Neovim buffer rendering- VSCode-style highlight groups
- Virtual line insertion for alignment
- Side-by-side window management
- Git status explorer
The plugin handles syntax highlighting differently based on buffer type:
Working files (editable):
- Behaves like normal buffers with standard highlighting
- Inlay hints disabled by default (incompatible with diff highlights)
- All LSP features available
Git history files (read-only):
- Virtual buffers stored in memory, discarded when tab closes
- TreeSitter highlighting applied automatically (if installed)
- LSP not attached (most features meaningless for historical files)
- Semantic token highlighting fetched via LSP request when available
The plugin defines highlight groups matching VSCode's diff colors:
CodeDiffLineInsert- Light green background for inserted linesCodeDiffLineDelete- Light red background for deleted linesCodeDiffCharInsert- Deep/dark green for inserted charactersCodeDiffCharDelete- Deep/dark red for deleted charactersCodeDiffFiller- Gray foreground for filler line slashes (β±β±β±)
πΈ Visual Examples (click to collapse)
Dawnfox Light - Default configuration with auto-detected brightness (char_brightness = 0.92 for light themes):
Catppuccin Mocha - Default configuration with auto-detected brightness (char_brightness = 1.4 for dark themes):
Kanagawa Lotus - Default configuration with auto-detected brightness (char_brightness = 0.92 for light themes):
Default behavior:
- Uses your colorscheme's
DiffAddandDiffDeletefor line-level highlights - Character-level highlights are auto-adjusted based on
vim.o.background:- Dark themes (
background = "dark"): Brightness multiplied by1.4(40% brighter) - Light themes (
background = "light"): Brightness multiplied by0.92(8% darker)
- Dark themes (
- This auto-detection works out-of-box for most colorschemes
- You can override with explicit
char_brightnessvalue if needed
Customization examples:
-- Use hex colors directly
highlights = {
line_insert = "#1d3042",
line_delete = "#351d2b",
char_brightness = 1.5, -- Override auto-detection with explicit value
}
-- Override character colors explicitly
highlights = {
line_insert = "DiffAdd",
line_delete = "DiffDelete",
char_insert = "#3fb950",
char_delete = "#ff7b72",
}
-- Mix highlight groups and hex colors
highlights = {
line_insert = "String",
char_delete = "#ff0000",
}make clean && makeRun all tests:
make test # Run all tests (C + Lua integration)Run specific test suites:
make test-c # C unit tests only
make test-lua # Lua integration tests onlyFor more details on the test structure, see tests/README.md.
vscode-diff.nvim/
βββ libvscode-diff/ # C diff engine
β βββ src/ # C implementation
β βββ include/ # C headers
β βββ tests/ # C unit tests
βββ lua/vscode-diff/ # Lua modules
β βββ init.lua # Main API
β βββ config.lua # Configuration
β βββ diff.lua # FFI interface
β βββ git.lua # Git operations
β βββ commands.lua # Command handlers
β βββ installer.lua # Binary installer
β βββ render/ # Rendering modules
β βββ core.lua # Diff rendering
β βββ view.lua # View management
β βββ explorer.lua # Git status explorer
β βββ highlights.lua # Highlight setup
βββ plugin/ # Plugin entry point
β βββ vscode-diff.lua # Auto-loaded on startup
βββ tests/ # Test suite (plenary.nvim)
β βββ README.md # Test documentation
βββ docs/ # Production docs
βββ dev-docs/ # Development docs
βββ Makefile # Build automation
βββ README.md # This file
- C-based diff computation with VSCode-identical algorithm
- Two-tier highlighting (line + character level)
- Side-by-side view with synchronized scrolling
- Git integration (async operations, status explorer, revision comparison)
- Auto-refresh on buffer changes (live diff updates)
- Syntax highlighting preservation (LSP semantic tokens + TreeSitter)
- Read-only buffers with virtual filler lines for alignment
- Flexible highlight configuration (colorscheme-aware)
- Integration tests (C + Lua with plenary.nvim)
- Inline diff mode (single buffer view)
- Fold support for large diffs
This plugin follows VSCode's diff rendering architecture:
- Data structures: Based on
src/vs/editor/common/diff/rangeMapping.ts - Decorations: Based on
src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts - Styling: Based on
src/vs/editor/browser/widget/diffEditor/style.css
MIT
Contributions are welcome! Please ensure:
- C tests pass (
make test) - Lua tests pass
- Code follows existing style
- Updates to README if adding features



