Support diff renderers that emit OSC 1717 diff line metadata - #6029
Open
stefanhaller wants to merge 7 commits into
Open
stefanhaller wants to merge 7 commits into
stefanhaller wants to merge 7 commits into
Conversation
stefanhaller
added this pull request to stack #6027
September 19, 2026 20:00
stefanhaller
force-pushed
the
support-osc-1717-diff-metadata
branch
from
September 20, 2026 17:51
661842c to
49b0c3f
Compare
stefanhaller
force-pushed
the
support-osc-1717-diff-metadata
branch
from
September 21, 2026 12:44
49b0c3f to
ed08dd6
Compare
The OSC parser dispatched on a single character, so only the single-digit OSC 8 could ever be recognized; the diff-line metadata protocol we are about to read uses OSC 1717. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
A diff renderer that restructures the diff — into columns, or with the +/- markers replaced by colour — leaves us no way to tell which line of which file a rendered row came from, which is what acting on the row requires. The OSC 1717 protocol has the renderer say so directly: it prefixes each line it renders with a record naming the file and the line's position in the old and new versions of it. Attach each record to the cells it precedes, so that a row's records survive wrapping and the columns of a side-by-side rendering, and hand them to readers together with the row's text: the two have to describe the same buffer, and a re-render can rebuild it between two reads. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Before the diff, a conforming renderer emits one OSC 1717 record that carries only the version — its way of announcing that it speaks the protocol at all, without a host having to inspect what it renders. It describes no line, so keeping it would give the first line of the diff a record that says nothing about it. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wherever two diff lines end up on one rendered line, a renderer emits their records back to back: the deletion and the addition of a modification collapsed into a single column, or a banner announcing a file and its first hunk at once. A changed line that is empty is rendered as its record alone. Attaching a record only to the cells it precedes loses all of these — the last record of a run wins, and an empty changed line becomes a line we can say nothing about at all. Give such a record a cell of its own instead. It renders nothing, so the diff looks the same, but the line keeps every record it was given. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
A diff line's identity is about to become recoverable from a second source, a diff renderer's own records, and a renderer states the path however it likes — absolute paths included. The field can't promise repo-relative any more. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Where a renderer states which line of which file it is rendering, take that as the line's identity. The renderer knows, and for a rendering that no longer looks like a diff nothing else does. Reading the rendered text stays the way for the renderings that keep a diff's structure, and for the renderers that say nothing at all. Which of the two is used is settled for the rendering as a whole, not row by row. A rendering with records is not parsed at all, not even for the rows the renderer says nothing about. Its text is no unified diff, and a row of it can read like one. Under delta, a commit that adds a test whose input is a diff shows the test's "diff --git a/img.png b/img.png" line as a row of its own; parsed, that row opens a file section that runs to the end of the rendering, and every row delta puts between hunks and files comes out as a header of a file called img.png. So the untagged rows of such a rendering stay unresolved, as the protocol has it (spec section 6.4). Header records are accepted too. This lets a renderer point at a file with no content lines, such as a pure rename, a mode change or a binary file: for those files the header is the only row in the diff. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A renderer that speaks the protocol emits nothing unless it is asked to, so that its output stays plain wherever it is used outside lazygit. The variable names the versions we understand. git is one of the renderers we ask. It has no pager to spawn, and so no terminal to spawn one in, but it still renders the diff itself — for the word-diff formats, whose markup nothing else could resolve — so the request has to be made before we decide a pty isn't needed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
stefanhaller
force-pushed
the
support-osc-1717-diff-metadata
branch
from
September 21, 2026 13:46
ed08dd6 to
debcd1f
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second PR in the stack of PRs towards staging hunks directly from the main view. This one is stacked on #6026.
Parsing the rendered diff back (#6026) only works for renderings that stay faithful to a unified diff. If a renderer puts the line numbers in a gutter, or lays the diff out side by side, the +/- marker moves off the start of each body line and the parse refuses the result. Those are exactly the renderers people like using.
OSC 1717 is a small terminal protocol that lets a diff renderer state, inline in its own output, which file and line each row of its rendering stands for. The spec is at https://github.com/stefanhaller/diff-line-metadata-spec. This PR implements lazygit's side of it. Nothing changes for users yet; the consumers arrive in the following PRs.
What it does:
OSC1717=V1in the environment of every renderer invocation, and of git itself. If a renderer understands the variable, it answers with a handshake record and then emits records. If it doesn't, nothing changes and lazygit works exactly as before.There is also a preparatory refactor in the escape-sequence parser. It dispatched on a single character, so only the single-digit OSC 8 could be recognized; it now accumulates the number.
Reference emitters exist as open draft PRs against delta (#2181), difftastic (#1014) and diff-so-fancy (#538), and as an unproposed patch to git for its word-diff formats. None of them is merged.