Skip to content

Support OSC-1717 metadata#2181

Open
stefanhaller wants to merge 3 commits into
dandavison:mainfrom
stefanhaller:osc-1717-metadata
Open

Support OSC-1717 metadata#2181
stefanhaller wants to merge 3 commits into
dandavison:mainfrom
stefanhaller:osc-1717-metadata

Conversation

@stefanhaller

@stefanhaller stefanhaller commented Jul 2, 2026

Copy link
Copy Markdown

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.

Closes #2180.

stefanhaller and others added 3 commits July 2, 2026 19:56
A host that renders delta'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.
delta's default rendering drops 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 delta emits the highest mutually-understood one
(currently always v1 because that's the only version that exists, but
the mechanism is extensible).

The metadata rides on a dedicated, purely-additive emitter rather than
reusing LineNumbersData: delta only maintains its line-number counters
when --line-numbers is enabled, and forcing that on would also render a
gutter and change wrap-width math. The emitter tracks its own counters
(seeded from the hunk header, mirroring
LineNumbersData::initialize_hunk) and only injects OSC bytes, leaving
all styling and layout untouched.

This commmit covers the normal unified single-column path only;
side-by-side mode is going to be supported in the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The unified emitter funnels every content line through
Painter::paint_lines, but side-by-side renders each visual row as two
panel halves painted directly via Painter::paint_line, so the unified
emit point never fired for it. Attach the OSC per cell instead: the left
panel's cell carries the minus line's identity, the right panel's the
plus line's, and a context line -- shown in both panels -- carries the
same record before each half.

The line-number counters in side-by-side are advanced per aligned row
(with increment flags and post-hoc fixups), not in the unified "all
minus, then all plus" order that osc_for_line's arithmetic assumes. So
rather than call osc_for_line inline at each panel paint, precompute the
per-line OSC strings for the whole minus block and then the whole plus
block -- the unified order -- and look them up by the same index that
selects each line's state. The emitted records stay byte-identical to
the single-column emitter, and the shared counters still land where the
next hunk block expects them.

Side-by-side is also the only mode that wraps a long line, into several
output rows with Hunk*Wrapped states. delta emits each wrapped row as a
distinct output line, so a host sees them as distinct lines and needs
the identity on each -- otherwise acting on a continuation row, or
treating the wrapped line as one block when navigating, breaks. Each
wrapped row therefore re-emits the record of the primary line it
continues, without advancing the counters (so the next line's numbers
stay correct); empty counterpart cells still get no OSC.

Context lines were previously not routed through the emitter at all in
side-by-side (paint_zero_lines_side_by_side never saw it), which would
have desynced the counters; thread it through there too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A host can already learn delta'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
consuming the diff, whenever a version is negotiated. It's
content-independent, so a host can probe delta on an empty diff and get
a conclusive answer.

See the diff-line-metadata OSC spec, §4.4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OSC-1717 support for better integration in lazygit

1 participant