https://github.com/ahkohd/difft.nvim
A Neovim frontend for Difftastic
https://github.com/ahkohd/difft.nvim
neovim neovim-plugin nvim nvim-plugin
Last synced: 2 days ago
JSON representation
A Neovim frontend for Difftastic
- Host: GitHub
- URL: https://github.com/ahkohd/difft.nvim
- Owner: ahkohd
- License: mit
- Created: 2025-10-16T13:55:53.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2025-11-02T21:37:52.000Z (4 months ago)
- Last Synced: 2025-11-02T23:18:25.404Z (4 months ago)
- Topics: neovim, neovim-plugin, nvim, nvim-plugin
- Language: Lua
- Homepage:
- Size: 120 KB
- Stars: 58
- Watchers: 0
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- my-awesome-github-stars - ahkohd/difft.nvim - A Neovim frontend for Difftastic (Lua)
README
# difft.nvim
A Neovim frontend for [Difftastic](https://difftastic.wilfred.me.uk/).
[](https://neovim.io)
[](http://www.lua.org)
https://github.com/user-attachments/assets/04070894-71ec-4051-92ce-d3140827370d
## Features
- Parse and display difftastic output with full ANSI color support
- Navigate between file changes with keybindings
- Jump to changed files directly from diff view
- Customizable window layouts (buffer, float, ivy-style)
- File header customization support
## Requirements
- Neovim 0.10+
- [Difftastic](https://github.com/Wilfred/difftastic) (external diff tool)
> [!IMPORTANT]
> `difft.nvim` requires ANSI color codes to render colored diffs.
> You **must** use the `--color=always` flag when calling `difft`, otherwise no colors will be visible in the diff output.
```bash
# Example difft command format
difft --color=always $left $right
# For git, use:
GIT_EXTERNAL_DIFF='difft --color=always' git diff
```
For more difftastic options and usage examples, see the [documentation](https://difftastic.wilfred.me.uk/usage.html).
## Installation
### lazy.nvim
```lua
return {
"ahkohd/difft.nvim",
config = function()
require("difft").setup()
end,
}
```
## Quick Setup
```lua
--luacheck: globals Difft
return {
"ahkohd/difft.nvim",
keys = {
{
"d",
function()
if Difft.is_visible() then
Difft.hide()
else
Difft.diff()
end
end,
desc = "Toggle Difft",
},
},
config = function()
require("difft").setup({
command = "GIT_EXTERNAL_DIFF='difft --color=always' git diff", -- or "jj diff --no-pager"
layout = "float", -- nil (buffer), "float", or "ivy_taller"
})
end,
}
```
## Example Setup
```lua
--luacheck: globals Difft
return {
"ahkohd/difft.nvim",
keys = {
{
"d",
function()
if Difft.is_visible() then
Difft.hide()
else
Difft.diff()
end
end,
desc = "Toggle Difft",
},
},
config = function()
require("difft").setup({
layout = "ivy_taller",
no_diff_message = "All clean! No changes detected.",
loading_message = "Loading diff...",
window = {
number = false,
relativenumber = false,
border = "rounded",
},
--- Custom header content with webdev icons
header = {
content = function(filename, step, _language)
local devicons = require("nvim-web-devicons")
local basename = vim.fn.fnamemodify(filename, ":t")
local icon, hl = devicons.get_icon(basename)
-- Get the bg from FloatTitle (what DifftFileHeader links to)
local header_hl = vim.api.nvim_get_hl(0, { name = "FloatTitle", link = false })
-- Create custom highlight with devicon fg + header bg
local icon_hl = hl
if hl and header_hl.bg then
local devicon_colors = vim.api.nvim_get_hl(0, { name = hl })
if devicon_colors.fg then
local custom_hl_name = "DifftIcon_" .. hl
vim.api.nvim_set_hl(0, custom_hl_name, {
fg = devicon_colors.fg,
bg = header_hl.bg,
})
icon_hl = custom_hl_name
end
end
local result = {}
table.insert(result, { " " })
table.insert(result, { icon and (icon .. " ") or "", icon_hl })
table.insert(result, { filename })
table.insert(result, { " " })
if step then
table.insert(result, { "• " })
table.insert(result, { tostring(step.current) })
table.insert(result, { "/" })
table.insert(result, { tostring(step.of) })
table.insert(result, { " " })
end
return result
end,
highlight = {
link = "FloatTitle",
full_width = true,
},
},
})
end,
}
```
## Configuration
### Layout Options
```lua
layout = nil -- Open in current buffer
layout = "float" -- Centered floating window
layout = "ivy_taller" -- Bottom window (ivy-style)
```
### Window Options
```lua
window = {
width = 0.9, -- Float window width (0-1)
height = 0.8, -- Float window height (0-1)
title = " Difft ", -- Window title
number = false, -- Show line numbers
relativenumber = false,
border = "rounded", -- Border style: "none", "single", "double", "rounded", "solid", "shadow", or custom array
}
```
### Keymaps
```lua
keymaps = {
next = "", -- Next file change
prev = "", -- Previous file change
close = "q", -- Close diff window (float only)
refresh = "r", -- Refresh diff
first = "gg", -- First file change
last = "G", -- Last file change
}
```
### Jump Configuration
```lua
jump = {
enabled = true, -- Enable file jumping
[""] = "edit", -- Open file in current window
[""] = "vsplit", -- Open file in vertical split
[""] = "split", -- Open file in horizontal split
[""] = "tabedit", -- Open file in new tab
}
```
### Header Customization
#### Simple Header
```lua
header = {
content = function(filename, step, language)
if step then
return string.format("[%d/%d] %s (%s)", step.current, step.of, filename, language)
end
return string.format("%s (%s)", filename, language)
end,
highlight = {
link = "FloatTitle",
full_width = true,
},
}
```
#### Header with Icons
```lua
header = {
content = function(filename, step, language)
local devicons = require("nvim-web-devicons")
local basename = vim.fn.fnamemodify(filename, ":t")
local icon, hl = devicons.get_icon(basename)
local result = {}
table.insert(result, { " " })
table.insert(result, { icon and (icon .. " ") or "", hl })
table.insert(result, { filename })
if step then
table.insert(result, { " • " })
table.insert(result, { tostring(step.current) })
table.insert(result, { "/" })
table.insert(result, { tostring(step.of) })
end
return result
end,
highlight = {
link = "FloatTitle",
full_width = true,
},
}
```
#### Header Highlight Options
```lua
-- Link to existing highlight group
highlight = {
link = "FloatTitle",
full_width = false,
}
-- Custom colors
highlight = {
fg = "#ffffff",
bg = "#5c6370",
full_width = true,
}
-- Link colors separately
highlight = {
fg = { link = "Statement" },
bg = { link = "Visual" },
full_width = false,
}
```
### Diff Highlights
Customize the highlight groups used for diff colors. Supports both **string** (group name) and **table** (color object) values:
**String values (highlight group names):**
```lua
diff = {
highlights = {
add = "DifftAdd", -- Additions (green) - ANSI codes 32, 92
delete = "DifftDelete", -- Deletions (red) - ANSI codes 31, 91
change = "DifftChange", -- Changes (yellow) - ANSI codes 33, 93
info = "DifftInfo", -- Info (blue/cyan) - ANSI codes 34, 94, 36, 96
hint = "DifftHint", -- Hints (magenta) - ANSI codes 35, 95
dim = "DifftDim", -- Dim text (gray/white) - ANSI codes 30, 90, 37, 97
},
}
```
**Table values (color objects):**
```lua
diff = {
highlights = {
-- Direct colors
add = {fg = "#00ff00"},
delete = {fg = "#ff0000", bg = "#300000"},
-- Link to existing group
change = {link = "WarningMsg"},
-- Mix and match
info = "DifftInfo", -- String
hint = {fg = "#c678dd"}, -- Color object
},
}
```
The ANSI bold, italic, and dim styles still layer on top of your custom color settings.
**Example:** GitHub-style diff colors, familiar and easy on the eyes:
```lua
require("difft").setup({
diff = {
highlights = {
add = { bg = "#d6f5d6", fg = "#1a4d1a" },
delete = { bg = "#ffe5e5", fg = "#6b1f1f" },
},
},
})
```
**Example:** Match your diffs to Devicon colors:
```lua
require("difft").setup({
diff = {
highlights = {
add = "DevIconBashrc", -- Green from bashrc icon
delete = "DevIconGulpfile", -- Red from gulpfile icon
},
},
})
```
### Other Options
```lua
command = "GIT_EXTERNAL_DIFF='difft --color=always' git diff" -- Diff command to execute
auto_jump = true -- Jump to first change on open
no_diff_message = "No changes found"
loading_message = "Loading diff..."
```
## Usage
### Basic Commands
```lua
-- Open diff
require("difft").diff()
-- Open with custom command
require("difft").diff({ cmd = "git diff" })
-- Close diff
require("difft").close()
-- Hide diff (float only, keeps buffer)
require("difft").hide()
-- Refresh current diff
require("difft").refresh()
-- Check if diff exists
if require("difft").exists() then
-- ...
end
-- Check if diff is visible
if require("difft").is_visible() then
-- ...
end
```
### Global API
The plugin also exposes a global `Difft` table:
```lua
Difft.diff()
Difft.close()
Difft.hide()
Difft.refresh()
Difft.exists()
Difft.is_visible()
```
### Example Keybinding
```lua
vim.keymap.set("n", "d", function()
if Difft.is_visible() then
Difft.hide()
else
Difft.diff()
end
end, { desc = "Toggle difft" })
```
## Navigation
When viewing a diff:
- `` / `` - Navigate between file changes
- `gg` / `G` - Jump to first/last change
- `` - Open file at cursor (jump to changed line)
- `` / `` / `` - Open file in split/tab
- `r` - Refresh diff
- `q` - Close diff (floating windows only)
## Highlight Groups
The plugin uses **terminal colors** by default, so it automatically matches your colorscheme
without any configuration.
**Color precedence** (from lowest to highest priority):
1. **ANSI defaults** - Standard terminal colors (red, green, yellow, etc.).
2. **Terminal colours** - `vim.g.terminal_color_N` - Set by most colorschemes.
3. **Theme-defined** - `Difft*` highlight groups.
4. **User config** - `diff.highlights` setting.
### Highlight Groups
**Diff Content:**
- `DifftAdd` - Added lines (uses `terminal_color_2` / green)
- `DifftDelete` - Deleted lines (uses `terminal_color_1` / red)
- `DifftChange` - Changed lines (uses `terminal_color_3` / yellow)
**ANSI Colors:**
- `DifftInfo` - Info text (uses `terminal_color_6` / cyan)
- `DifftHint` - Hint text (uses `terminal_color_5` / magenta)
- `DifftDim` - Dim text (uses `terminal_color_8` / gray)
See [Diff Highlights](#diff-highlights) for customization options.
To provide difft-specific colors for themes, define `Difft*` groups in your colorscheme.
### File Headers
- `DifftFileHeader` - File headers (uses `terminal_color_7` / white)
- `DifftFileHeaderBg` - Header background for full-width mode (transparent by default)
## Testing
Run tests with:
```bash
nvim -l tests/run.lua
```
See [tests/README.md](./tests/README.md) for details.