Support OSC-1717 metadata#538
Support OSC-1717 metadata#538stefanhaller wants to merge 6 commits intoso-fancy:nextso-fancy/diff-so-fancy:nextfrom stefanhaller:osc-1717-metadatastefanhaller/diff-so-fancy:osc-1717-metadataCopy head branch name to clipboard
Conversation
A host that renders diff-so-fancy's output (e.g. lazygit) needs to map a rendered diff row back to its patch-space identity -- file, line type, and new/old line numbers -- to act on the line the user points at. diff-so-fancy strips the +/- markers and conveys the side by color, so that identity cannot be recovered from the painted text; the pager, which still has it at render time, has to state it. Gate the emission on the OSC1717_METADATA environment variable so output is byte-for-byte unchanged outside such a host -- harmless in a raw terminal, less, or tmux. The host advertises the protocol versions it understands and diff-so-fancy emits the highest mutually-understood one (just V1 today). See the protocol in diff-line-metadata-osc-spec.md; this is the third reference implementation after delta and difftastic. The emitter tracks its own old/new line counters, seeded from each hunk header, and classifies every content line by its leading +/- indicator -- read before the existing code strips that indicator and rewrites the line. sanitize_display strips OSC sequences, so the record is prepended to the already-sanitized line rather than embedded in it. The path prefers the new-file side, falling back to the old side for a deletion (whose new side is /dev/null), which also recovers the path for a noprefix deletion that never reaches $last_file_seen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A host can already learn diff-so-fancy's per-line diff metadata, but only from the records emitted before content lines — so a diff with no content (a binary file, or the empty diff a host would use to probe) emits nothing, and "speaks the protocol" is indistinguishable from "unsupported pager". Emit a version-only OSC 1717 record (no further fields) once, before processing, whenever a version is negotiated. It's content-independent, so a host can probe diff-so-fancy on an empty diff and get a conclusive answer. The osc_records test helper now skips this handshake record so the per-line assertions are unchanged; two new tests cover the handshake (first, and present on an empty diff). See diff-line-metadata-osc-spec.md §4.4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I'd definitely like to start working on and testing this new feature. I'd like to target a feature branch instead of |
|
I applied this Please look at: https://github.com/so-fancy/diff-so-fancy/tree/osc1717 I did massage some of your code (which was quite well written) to match my coding conventions. All the unit tests still pass. |
|
I'm not sure what you mean by "target a feature branch". Your new I pushed a fixup commit to this branch to carry over your changes into this PR; please check if I got them all. I'd prefer to keep working here; you have permission to push changes to this branch too. |
Annotate the structural rows too, so a host can anchor file and hunk navigation on the headers themselves and can see files that emit no content records at all: every row of a ruled file-header block carries the file's `f` record (spec §6.4), and the row announcing a hunk carries that hunk's `h` record with the hunk's first new-file line -- straight from the freshly seeded counters, so it is the @@ new start, not the first changed line the rendered header displays. `f` never carries line numbers (the spec fixes each header type's payload; a streaming renderer does not know the first hunk's line when it draws the file header). That is what makes headers available for files with no content lines, which now stay visible to the host: - a pure rename or copy tags its ruled block, - a binary file tags its "(binary)" block, - a mode-only change tags its single announcement row -- gated on a lookahead so that a mode change on a file that also has content does not produce a second, differing header block (the ruled header that follows is that file's header), - a combined/merge diff still has no annotated content (its multiple old sides do not fit the line-number model), but its file header now carries `f`, keeping conflicted files reachable. With shortHeaders on, no hunk rows are rendered at all, so there is no row for an `h` to tag; hosts fall back to content records. With OSC1717 unset, output remains byte-for-byte identical.
…ations Emit per-line diff metadata as an OSC sequence for host applications A host that renders diff-so-fancy's output (e.g. lazygit) needs to map a rendered diff row back to its patch-space identity -- file, line type, and new/old line numbers -- to act on the line the user points at. diff-so-fancy strips the +/- markers and conveys the side by color, so that identity cannot be recovered from the painted text; the pager, which still has it at render time, has to state it. Gate the emission on the OSC1717 environment variable so output is byte-for-byte unchanged outside such a host -- harmless in a raw terminal, less, or tmux. The host advertises the protocol versions it understands and diff-so-fancy emits the highest mutually-understood one (just V1 today). See the protocol in diff-line-metadata-osc-spec.md; this is the third reference implementation after delta and difftastic. The emitter tracks its own old/new line counters, seeded from each hunk header, and classifies every content line by its leading +/- indicator -- read before the existing code strips that indicator and rewrites the line. sanitize_display strips OSC sequences, so the record is prepended to the already-sanitized line rather than embedded in it. The path prefers the new-file side, falling back to the old side for a deletion (whose new side is /dev/null), which also recovers the path for a noprefix deletion that never reaches $last_file_seen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This adds support for the OSC-1717 spec described in jesseduffield/lazygit#5731; see also jesseduffield/lazygit#5732 for more context.
The spec is still in draft mode, so we may want to hold off merging this until we got more feedback on it. On the other hand, we want pagers to be ready by the time lazygit releases the feature, that's why I'm opening this for review already.