# Diffs in a pager: what delta does, what pardes could Proposals, 2026-10-01, none decided. They come from reading delta, diff-so-fancy and difftastic (all in `~/05-genizah/`) against pardes as of `3815a0bf`, the commit that made `pardes -` a terminal's `PAGER` and `GIT_PAGER`. The rule kept throughout: the pane's text stays the plain text git printed, and whatever is added is something a look or an exec can act on. ## How the others read a diff **delta** reads stdin a line at a time and keeps each line twice: the raw bytes and a copy with the ANSI stripped (`delta.rs:235`). Every match runs on the stripped copy, so git's colour does not change the parse. The raw copy is kept for two cases where colour is the information: a hunk line whose colour is not git's plain red or green (`--color-moved`), and every line under `--word-diff` or `--color-words`, which pass through as git coloured them (`hunk.rs:136-171`). A state machine (`delta.rs:20-38`) moves on `commit`, `diff`, `---`/`+++`, `@@`, and the `-`/`+`/space prefixes, with further states for merge conflicts, submodules, `git blame`, `git grep` and `git show REV:path`. Which git command it is reading it learns from its parent process's command line (`utils/process.rs`), down to scanning the pids next to its own. Within a line, it pairs each removed line with the first added line within 0.6 edit distance, tokenises both on `\w+` and aligns them (`edits.rs`, `align.rs`). Its OSC 8 links name only the new side's `file:line`. `--navigate` writes `Δ` and `•` before file and hunk headers and puts a search for them into a copy of less's history file, so that `n` finds them. **diff-so-fancy** matches git's colour codes where they appear, colouring for itself where there are none. It rewrites the diff for show: file headers become a box, `@@ -a,b +c,d @@` becomes `@ file:N @`, and the `+`/`-` markers go, leaving old against new to colour. Within a line it uses git's diff-highlight, which pairs lines only in a block with as many added as removed lines, and marks what lies between a common prefix and a common suffix. Behind `OSC1717=V1` it emits a per-line escape carrying the line's kind, its old and new numbers and its path. It is the only one of the three whose output gives every line its place in both files. **difftastic** reads no diff. Git runs it as the diff program (`GIT_EXTERNAL_DIFF`) and hands it both whole files. It parses each with tree-sitter and finds the cheapest set of changes with Dijkstra, falling back to a line diff past 1 MB, 3M graph vertices or any parse error. What it prints is not a patch: change is shown only by colour, and its JSON is unstable and counts lines from 0. ## What pardes does now `pardes -` reads stdin, strips terminal escapes (`stripEscapes`, `main.zig`), and puts the text in a `/+Pager` pane. Git starts its pager at the top of the work tree even when run from a subdirectory (checked), so `` is already the base git's repository-relative paths need. A pane that is a diff is read by `src/diff.zig`. It is already at delta's level, or past it: - A hunk's end comes from its `@@` counts, as patch reads it, so a removed `--- x` line is never taken for a header. delta only counts this way for a plain `diff -u`. - `syntax.highlightDiff` colours each hunk's old side and new side each as one piece in the file's own language, so a string or a comment that spans lines colours as it does in the file. delta runs one highlighter through the removed lines and then the added ones. - git's other prefixes (`c/`, `i/`, `w/`, `--no-prefix`) are understood, and so are rows a terminal wrapped. - A look on any line goes to the place it names (`look.zig:1573`): a file header to the file, an `@@` or a hunk line to `path:N`. delta's links are the nearest thing it has. ## Gaps 1. **A +Pager pane is never a diff.** Whether a pane is coloured and looked at as a diff is decided by its name ending `.diff` or `.patch` (`panes.zig:451`); only a finished command pane is checked by content (`looksLikeDiff`, `File.zig:1545`). So `git log -p` in a terminal pages to plain text, with no colour and no look on its lines. (From reading the code, not yet run.) 2. **Some of what git says is only colour, and stripping loses it.** With `git diff --color-words`, `foo(1)` changed to `bar(1)` arrives as `let x = foo(1);bar(1);` (checked). `--color-moved` loses its moved blocks the same way. 3. **No changed words within a line**, which is most of what people use delta for. 4. **Nothing for commits or blame.** A `git grep` line, `path:N:text`, is a look already. ## Proposals ### A. Read +Pager as a diff when it is one Either `colorAlgo` checks a +Pager pane's content with `looksLikeDiff`, or `pardes -` names the pane `+Pager.diff` when what it read is a diff. The name has the merit that a 9P client, or a reader of the tag, sees what the pane is. Either is a few lines, and everything in `diff.zig`, the painter and the look comes along. Leaning to the content check, so that a name always means where the text came from. ### B. Turn colour that carries meaning into git's own text While stripping, a red or green run inside a hunk line under word diff becomes `[-foo(1)-]` or `{+bar(1)+}`: exactly what `git diff --word-diff=plain` prints. The pane stays plain text, searchable and editable, and says what the colour said. `--color-moved` has no text form in git; it can be kept as a style layer beside the text, dropped at the first edit, or let go. ### C. Changed words within a line, drawn only Pair removed lines with added ones and align their tokens as delta does (edit distance, then an alignment table), and set an emph bit in the style bytes beside `diff_added`/`diff_removed`. The text is untouched. A change made only of whitespace, an indent or a gap, is not marked (the patch carried in our own delta build, `local-patches` in the genizah). Later, the tokens could be tree-sitter's leaves in place of `\w+`, which gets much of difftastic's quality without its graph search. ### D. Moving through a diff by address, not by marks delta's marks exist because less cannot search by structure. In pardes `:/^@@/` and `:/^diff /` are text, and a look of them moves. They can sit in a diff pane's tag. Or an `Index` exec writes a +Index pane with one line per file or hunk, each a look: @p12:345 src/look.zig +12 -3 ### E. A diff you can act on This is what a pager that only prints cannot do. - **Stage, Unstage, Revert** in the tag: the hunk under the cursor, with its file header and its `@@` counts recounted, written to `git apply --cached`, `git apply --cached -R`, or `git apply -R`. - **Apply** for the whole pane: edit the diff, then apply it, which is `git add -p`'s `e` for a whole buffer. - **The old side.** A look on a removed line goes today to the new line now standing where it was. The `index e1f909f..0136052` line names the old blob, so a look on a removed line can open `git cat-file -p e1f909f` at its old line instead. - **Commits.** A look on `commit abc1234`, or on a blame line's hash, runs `git show abc1234` into another +Pager, as acme's plumber would. These can be helpers over 9P (a script reads the pane's body and dot, runs git, writes back), in keeping with the roadmap's helper step, rather than code in the core. ### F. Further off - **pardes as git's diff program**, where difftastic sits: it is handed both whole files and still prints an ordinary unified diff, aligned by syntax tree. Not worth it until C with tree-sitter tokens falls short. - **Reading OSC 1717**, so that a diff from delta or diff-so-fancy, whose text is too rewritten to parse again, can still be looked at line by line. ## Open questions 1. A: check the content, or name the pane `.diff`? 2. B: word colour to `[-…-]{+…+}` text, a style layer beside the text, or both? 3. E: in the core, or as helpers over 9P? Suggested order: A, then the text half of B, then E's commit and old-side looks, then C.