Skip to content

A Neovim plugin that displays difftastic's structural diffs in a side-by-side view with syntax highlighting.

License

Notifications You must be signed in to change notification settings

clabby/difftastic.nvim

Repository files navigation

difftastic.nvim

A Neovim plugin that displays difftastic's structural diffs in a side-by-side view with syntax highlighting.

difftastic.nvim

Features

  • Side-by-side diff view with synchronized scrolling
  • Hierarchical file tree sidebar with directory collapsing
  • Syntax highlighting for the source language
  • Filler lines to visually indicate alignment gaps
  • Support for both jj and git version control
  • Optional snacks.nvim picker for selecting a revision/commit

Installation

Requirements

Warning

This plugin requires difftastic with aligned_lines support in JSON output. This feature is available in main, though hasn't yet landed in a release.

To build difftastic from source:

# Clone with 'jj'
jj git clone git@github.com:Wilfred/difftastic.git --colocate

# Or, clone with 'git'
git clone git@github.com:Wilfred/difftastic.git

# Install 'difft' with the 'aligned_lines' feature
cd difftastic && cargo install --path .

lazy.nvim (recommended)

{
    "clabby/difftastic.nvim",
    dependencies = {
        "MunifTanjim/nui.nvim",
        -- optional: only needed for :DifftPick
        "folke/snacks.nvim",
    },
    config = function()
        require("difftastic-nvim").setup({
            download = true, -- Auto-download pre-built binary
            snacks_picker = {
                enabled = true,
            },
        })
    end,
}

Building from source

If you prefer to build locally or pre-built binaries aren't available for your platform:

{
    "clabby/difftastic.nvim",
    dependencies = { "MunifTanjim/nui.nvim" },
    config = function()
        require("difftastic-nvim").setup()
    end,
}

Requires a Rust toolchain. The plugin automatically builds from source on first use if the library isn't found.

Usage

Commands

Command Description
:Difft Open diff view for unstaged changes (git) or uncommitted changes (jj)
:Difft --staged Open diff view for staged changes (git only)
:Difft <ref> Open diff view for a jj revset or git commit/range
:DifftPick Pick a jj revision or git commit using snacks.nvim (with preview)
:DifftPickRange Pick end revision, then pick a parent revision as range start
:DifftClose Close the diff view
:DifftUpdate Update to latest release (requires download = true)

Examples (jj)

" Diff uncommitted changes (working copy vs @)
:Difft

" Diff the current change
:Difft @

" Diff the parent of the current change
:Difft @-

" Diff a change-id prefix (equivalent to jj diff -r w)
:Difft w

" Diff a specific revision
:Difft abc123

Examples (git)

" Diff unstaged changes (working tree vs index)
:Difft

" Diff staged changes (index vs HEAD)
:Difft --staged

" Diff the last commit
:Difft HEAD

" Diff a specific commit
:Difft abc123

" Diff a commit range
:Difft main..HEAD

Keybindings

All keybindings are buffer-local and configurable via setup(). Defaults:

Key Action
]f Next file
[f Previous file
]c Next hunk
[c Previous hunk
<Tab> Toggle focus between file tree and diff
<CR> Open file under cursor (in file tree)
gf Go to file at cursor position (opens in previous tab or new tab)
q Close diff view

The gf keymap works from the right pane (new/working version) and jumps to the corresponding line and column in an editable buffer. If on a filler line, it jumps to the nearest non-filler line.

Filler lines (╱╱╱) indicate where content exists on one side but not the other.

Configuration

require("difftastic-nvim").setup({
    download = false,              -- Auto-download pre-built binary (default: false)
    vcs = "jj",                    -- "jj" (default) or "git"
    highlight_mode = "treesitter", -- "treesitter" (default) or "difftastic"
    hunk_wrap_file = true,          -- Next hunk at last hunk goes to next file
    scroll_to_first_hunk = true,  -- Auto-scroll to first hunk after opening a file (default: true)
    snacks_picker = {
        enabled = false,          -- opt-in snacks.nvim integration (default: false)
        limit = 200,              -- number of revisions/commits to list in :DifftPick
        jj_log_revset = nil,      -- optional: jj revset for picker log (nil = omit -r and use jj default)
    },
    keymaps = {
        next_file = "]f",
        prev_file = "[f",
        next_hunk = "]c",
        prev_hunk = "[c",
        close = "q",
        focus_tree = "<Tab>",
        focus_diff = "<Tab>",
        select = "<CR>",
        goto_file = "gf",
    },
    tree = {
        width = 40,
        icons = {
            enable = true,    -- use nvim-web-devicons if available
            dir_open = "",
            dir_closed = "",
        },
    },
    highlights = {
        -- Override any highlight group (see Highlight Groups below)
        -- DifftAdded = { bg = "#2d4a3e" },
    },
})

All options are optional. Only specify what you want to override.

Highlight Modes

The highlight_mode option controls how syntax highlighting is applied:

  • treesitter (default): Full syntax highlighting via Neovim's treesitter. Changes are shown with background colors.
  • difftastic: Minimal highlighting like the CLI. No syntax colors; changes are shown with foreground colors (green/red) to make diffs more prominent.

Highlight Groups

Highlights automatically inherit from your colorscheme's semantic groups (Added, Removed, Directory, Normal) and update when you switch themes. Background colors are derived by blending the foreground color with your Normal background at 25% opacity.

Treesitter mode (background colors):

Group Default Description
DifftAdded Derived from Added Added lines background
DifftRemoved Derived from Removed Removed lines background

Difftastic mode (foreground colors):

Group Default Description
DifftAddedFg Links to Added Added text
DifftRemovedFg Links to Removed Removed text
DifftAddedInlineFg Added + bold Inline added text
DifftRemovedInlineFg Removed + bold Inline removed text

Tree:

Group Default Description
DifftDirectory Links to Directory Directory names
DifftFileAdded Links to Added Added files
DifftFileDeleted Links to Removed Deleted files
DifftTreeCurrent Derived from Normal Current file highlight

Picker:

Group Default Description
DifftPickerPreviewHover Derived from Normal Hovered jj revision lines in picker preview
DifftPickerJjIconCurrent Links to Added @ icon in jj picker list
DifftPickerJjIconImmutable Links to Removed icon in jj picker list
DifftPickerJjIconNormal Links to Directory icon in jj picker list
DifftPickerJjDesc Derived from Normal Description text in jj picker list
DifftPickerJjRevset Links to Identifier Change/revset id in jj picker list
DifftPickerJjAge Links to Comment Age field in jj picker list

Other:

Group Default Description
DifftFiller Derived from Normal Filler lines for alignment gaps

License

MIT

About

A Neovim plugin that displays difftastic's structural diffs in a side-by-side view with syntax highlighting.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published