From eda60572013d046d08e253e5847a0ba588c10339 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 09:49:03 -0300 Subject: Grep's miss says `Grep: text not found`: it is a literal search, and "pattern" suggested a regular expression Still ENOENT through a mount ("not found" is in 9ns's table, and the unit tests' errno-words check holds it so). The guide's quote follows. Co-Authored-By: Claude Opus 5.5 --- docs/diff-pager-proposals.md | 164 +++++++++++++++++++++++++++++++++++++++++++ docs/typ/guide.typ | 2 +- 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 docs/diff-pager-proposals.md (limited to 'docs') diff --git a/docs/diff-pager-proposals.md b/docs/diff-pager-proposals.md new file mode 100644 index 00000000..f75361df --- /dev/null +++ b/docs/diff-pager-proposals.md @@ -0,0 +1,164 @@ +# 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. diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index 6d140da7..077484dc 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -146,7 +146,7 @@ stops at 512 hits, Find at 512 names; the walk stops at 20000 files or hit, or a directory it could not open, is said at the end (`cut at 512 hits`, `N files read only in part (first 256 KiB)`, `walk cut at N entries`, `N directories skipped: permission denied`). A search that finds -nothing and skipped nothing fails, `Grep: pattern not found`, and leaves +nothing and skipped nothing fails, `Grep: text not found`, and leaves the `+Search` as it was. A relative path is looked for where the click was, then where you have -- cgit v1.3