Add OSC 8 hyperlink support (+ fix ST-terminated OSC and a broken main build) - #3
Open
codymullins wants to merge 2 commits into
Open
Add OSC 8 hyperlink support (+ fix ST-terminated OSC and a broken main build)#3codymullins wants to merge 2 commits into
codymullins wants to merge 2 commits into
Conversation
… catch main has not compiled since f4f7964: the Cleanup method lost its closing brace, and a catch (DirectoryNotFoundException) was added after catch (IOException), which already catches it (subtype -> unreachable). Restore the method to its working shape.
Terminals mark links with OSC 8 (ESC ] 8 ; params ; URI ST). Track them through the emulator so a UI can hover/click them. - Cell gains a HyperlinkId (a small id into a per-terminal URI table, kept cheap to copy through scrollback and reflow; 0 = no link). - Terminal carries a hyperlink pen and an id->URI table, deduped by the OSC 8 id= param (or URI when absent) so a link wrapped across rows, or repeated, reuses one id and the table stays bounded. - VtHandler parses OSC 8; the OSC payload cap grows to 4096 for long URIs. - TerminalScreenSnapshot exposes the per-cell id, the URI map, and a HyperlinkAt(col,row) helper. Also fixes a pre-existing bug: ST-terminated OSC (ESC \) never dispatched because the 'anywhere' ESC transition jumped to the Escape state without finalizing the OSC string. Only the BEL terminator worked, so OSC titles, cwd, and now hyperlinks set via ST were silently dropped. The ESC transition now dispatches a pending OSC first. Adds HyperlinkTests covering parsing, id grouping, dedup, long URIs, RIS reset, and the snapshot surface.
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request adds OSC 8 hyperlink support end-to-end in the terminal emulator (parse → pen/state → per-cell storage → snapshot surface), while also fixing ST-terminated OSC dispatch and restoring a broken build in ZshPromptShim.Cleanup.
Changes:
- Add per-cell hyperlink tracking via a small
ushortHyperlinkIdand a per-terminal id→URI table. - Extend the VT parser/handler to dispatch ST-terminated OSC correctly and to parse
OSC 8hyperlink sequences (with a larger OSC payload cap). - Expose hyperlink information through
TerminalScreenSnapshotand add focused unit tests for the new behavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Dotty.Terminal/Terminal.cs | Stores hyperlink pen state and manages the hyperlink id→URI table; writes hyperlink ids into cells. |
| Dotty.Terminal/Rendering/TerminalScreenSnapshot.cs | Adds hyperlink table + HyperlinkAt helper to snapshots and includes per-cell HyperlinkId in render cells. |
| Dotty.Terminal/Parser/VtStateMachine.cs | Increases OSC payload buffer and fixes ST-terminated OSC dispatch behavior. |
| Dotty.Terminal/Parser/VtHandler.cs | Adds OSC 8 parsing and applies hyperlink open/close to the terminal pen state. |
| Dotty.Terminal/Hosting/ZshPromptShim.cs | Fixes build break in Cleanup by restoring a missing closing brace / removing unreachable catch. |
| Dotty.Terminal/Cell.cs | Adds HyperlinkId field to the cell and resets it appropriately. |
| Dotty.Terminal.Tests/HyperlinkTests.cs | Adds test coverage for hyperlink parsing, dedup/grouping, reset behavior, and snapshot surface. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Group by explicit id when present so a wrapped/multi-span link is one | ||
| // entry; otherwise group by URI so repeats of the same link don't grow | ||
| // the table without bound. | ||
| var key = string.IsNullOrEmpty(id) ? "u " + uri : "i " + id; |
Comment on lines
+760
to
+767
| linkId = _nextHyperlinkId; | ||
| // ushort ids top out at 65535 — vanishingly unlikely in a session, but | ||
| // stop allocating rather than wrap to 0 ("no link") or collide. | ||
| if (_nextHyperlinkId != ushort.MaxValue) | ||
| _nextHyperlinkId++; | ||
| _hyperlinkKeys[key] = linkId; | ||
| _hyperlinks[linkId] = uri; | ||
| } |
Comment on lines
+772
to
+773
| /// <summary>OSC 8 hyperlink table: cell <see cref="Cell.HyperlinkId"/> → URI.</summary> | ||
| public IReadOnlyDictionary<ushort, string> Hyperlinks => _hyperlinks; |
Comment on lines
+64
to
+71
| private static IReadOnlyDictionary<ushort, string> SnapshotHyperlinks( | ||
| IReadOnlyDictionary<ushort, string> live) => | ||
| live.Count == 0 | ||
| ? EmptyHyperlinks | ||
| : new Dictionary<ushort, string>(live); | ||
|
|
||
| private static readonly IReadOnlyDictionary<ushort, string> EmptyHyperlinks = | ||
| new Dictionary<ushort, string>(); |
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.
What
Adds OSC 8 hyperlink support to the terminal emulator, so a UI layer can detect, highlight, and open links that programs mark up (e.g.
ls --hyperlink,gh).Programs wrap links as
ESC ] 8 ; params ; URI ST … text … ESC ] 8 ; ; ST. This tracks that state through the grid exactly like the existing pen tracks colors and attributes.Changes
Cell.HyperlinkId— a smallushortid into a per-terminal URI table (0= no link). Storing an id rather than the URI string keepsCella cheap value type to copy through scrollback and reflow.Terminal— a hyperlink pen plus an id→URI table, deduped by the OSC 8id=param (or by URI when none is given) so a link wrapped across rows — or repeated — reuses one id and the table stays bounded. Cleared on RIS.VtHandler— parsesOSC 8, extracting theid=param. The OSC payload cap grows from 512 to 4096 bytes for long URIs.TerminalScreenSnapshot— carries the per-cellHyperlinkId, theHyperlinksid→URI map, and aHyperlinkAt(col, row)helper.Pre-existing bug fix (included)
Two commits, so they can be split if preferred:
ZshPromptShim.Cleanupbuild fix —mainhas not compiled sincef4f7964: the method lost its closing brace, and acatch (DirectoryNotFoundException)was added aftercatch (IOException)(a supertype), making it unreachable. Restored to its working shape. Without this the branch doesn't build.ESCtransition jumped to the Escape state without finalizing an open OSC string, so only the BEL terminator ever dispatched. OSC titles, cwd, and hyperlinks set viaST(ESC \) were silently dropped. TheESCtransition now dispatches a pending OSC first. (Folded into the OSC 8 commit since the feature needs it.)Tests
New
HyperlinkTestscover parsing,id=grouping across rows, dedup, long URIs within the cap, malformed sequences, RIS reset, and the snapshot surface. Full suite: 223 passed.API note
TerminalRenderCelland theTerminalScreenSnapshotrecord gain a field each (additive; property reads are unaffected, positional construction updated).