Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

differ

Note

finding the tool is helpful or not if it turns out good then maybe a improvment will appear right now this is pretty much llm generated code and i havnt really reviewed.

Code review in Neovim, so you don't have to open a browser.

Not a git client. It diffs, and it tries to do that very well.

Requires Neovim 0.10+ and git. Built against Neovim 0.13 and git 2.x.


Status

Milestone 3 of 8. Working today:

  • :Differ [revspec] — the project-wide diff, one continuous scroll
  • true old/new line numbers in the gutter
  • jump from any diff line into the real file at the real line
  • hunk and file navigation
  • sticky file header while scrolling

Not built yet: the sidebar file tree, the two-pane file diff, PR review.

Using it

:Differ                  " working copy — tracked changes and untracked files
:Differ main             " only YOUR changes since you forked from main
:Differ HEAD~3           " against a commit
:Differ main..HEAD       " explicit two-dot, tip to tip
:Differ main...feature   " explicit three-dot between two refs
:Differ pr               " pick from your open pull requests
:Differ pr 1234          " review that pull request
:Differ pr all           " include closed and merged ones
:Differ pr author:@me    " any gh search query
:Differ prune            " delete the refs past PR reviews left behind

A bare argument means three-dot: :Differ main shows what you changed, not what your teammates merged into main after you branched. That's what GitHub shows for a pull request, and it's what keeps a review readable while the base branch moves under you. Type the dots yourself if you want tip-to-tip.

In the diff:

key does
<CR> open this file at this line, in your own window
]h / [h next / previous hunk
]f / [f next / previous file
dv two-pane diff of this file
<Tab> jump to the file tree
v mark this file viewed
q close the review

In the file tree:

key does
<CR> show this file and move focus to the diff
o show it, keep focus in the tree
<CR> on a directory collapse / expand it
dv two-pane diff of this file
v mark viewed
/ filter files by name
<Esc> clear the filter
<Tab> jump back to the diff

<CR> is the point of the whole thing. The line numbers are real, so you land in the actual file with LSP attached and go-to-definition live — which is the one thing a browser can't do.

Pull requests

:Differ pr lists your open PRs; <CR> reviews one. Needs the GitHub CLI (gh) authenticated — it's a hard requirement, not a soft fallback.

A PR diff is mechanically just a three-dot range, but knowing it's a PR buys something a branch diff can't know: the exact commits the PR spans. gh reports them as baseRefOid / headRefOid, and differ pins the review to those. That matters because your local branch may have moved on from what you pushed — reviewing local refs would show changes no reviewer on GitHub can see.

The PR head is fetched into refs/differ/pr/<n> (a default clone doesn't fetch refs/pull/*), and the pinned SHAs are saved under stdpath("data") so reopening reviews the same thing.

Install

lazy.nvim:

{
  "you/differ",
  cmd = "Differ",
  config = function() require("differ").setup({}) end,
}

setup({}) is optional; the defaults are fine.

Word-level diff

When a hunk replaces N lines with N lines, differ pairs them up and marks only the bytes that actually differ — so changing 3000 to 5000 highlights the number, not the whole line. Unequal runs are left alone: those are insertions or removals, where no line-to-line correspondence exists to diff against.

Syntax highlighting

The code is highlighted, with one deliberate exception: deleted lines stay diff-coloured.

That isn't laziness. A unified diff interleaves old and new lines, so the text in the buffer isn't valid source in any language — you can't just attach a parser to it. What is valid is the new side of each file: its added and context lines, in order, reconstruct the file as it exists after the change. differ rebuilds that text per file, parses it alone, and maps each captured range back to the line that produced it. A deleted line belongs to the old file and has no place in that reconstruction.

Set syntax = false to turn it off. It is budgeted (syntax_budget, default 20000 extmarks) so a huge review degrades to plain diff colouring rather than stalling.

How it works

The review is one buffer with every file's diff concatenated, not a pair of synchronized panes. A single buffer has nothing to keep aligned, which is what makes whole-file additions and deletions render correctly.

Getting there means parsing git diff into a real model — files, hunks, and a line-by-line record of which old and new line each row corresponds to. That model is what pays for the gutter, the jump, the counts, and the navigation.

Two things about git's output are worth knowing, because both are silent traps and both have regression tests in tests/run.lua:

A deleted line can look exactly like a file header. Remove a Lua doc comment and the diff contains ---- doc comment — a - marker in front of --- doc comment. Scanning for ^--- to find file boundaries finds it, and truncates the file list with no error. So file boundaries come from diff --git and nothing else.

--numstat and --name-status frame renames differently. --name-status writes R100\0old\0new, while --numstat writes 0\t0\t\0old\0new — an empty path field with two extra NUL fields after it. Splitting one record per NUL field reads that empty path as a filename and misattributes every file after the first rename. Two formats, two parsers.

Every git call is run with --no-ext-diff (you may have delta configured) and -c core.quotepath=false (or non-ASCII paths come back backslash-quoted and break -z framing).

Tests

nvim --headless --clean -c "set rtp+=." -c "luafile tests/run.lua" -c "qa!"

The parsers are pure functions and covered directly. tests/xcheck.lua additionally validates the parser against real git output — for any repo and range, every file's computed additions and deletions must equal what git --numstat reports:

REPO=/path/to/repo RANGE=HEAD~40...HEAD \
  nvim --headless --clean -c "luafile tests/xcheck.lua" -c "qa!"

Hand-written fixtures only prove the parser matches your idea of the format. This proves it matches git.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages