Skip to content

Fixes #4892. OSC 8 hyperlink rendering does not clean up#5254

Merged
tig merged 2 commits into
gui-cs:developfrom
harder:fix/4892-osc8-link-cleanup
May 8, 2026
Merged

Fixes #4892. OSC 8 hyperlink rendering does not clean up#5254
tig merged 2 commits into
gui-cs:developfrom
harder:fix/4892-osc8-link-cleanup

Conversation

@harder
Copy link
Copy Markdown
Collaborator

@harder harder commented May 8, 2026

Fixes #4892

Summary

Two distinct paths leak OSC 8 hyperlink state into adjacent rendering, producing the visual artifact reported in #4892 (text near a Link rendered as if it were part of the hyperlink).

1. Stale entries in the cell URL map (OutputBufferImpl.SetAttributeAndDirty)

SetAttributeAndDirty previously only wrote to _urlMap when CurrentUrl was non-empty and never cleared existing entries. When a cell that previously carried a URL was overdrawn by content with no CurrentUrl set — for example a parent view filling its content area, or a Link whose displayed text shrank - the cell became dirty but the stale URL persisted in _urlMap. On the next render that cell was wrapped in OSC 8 sequences, so unrelated content rendered as a hyperlink.

Fix: when CurrentUrl is null/empty, remove the entry for that cell from _urlMap.

2. OSC 8 close skipped at row boundary (OutputBase.Write)

When dirty cells with a URL are flushed mid-row because a clean cell follows, WriteToConsole writes the OSC 8 start sequence + cell content to the terminal and clears outputStringBuilder, but _lastUrl stays set. If the row ends without another flush, the existing if (outputStringBuilder.Length <= 0) { continue; } early-continue skipped the end-of-row OSC 8 close. The hyperlink stayed open in the terminal across the row boundary, so subsequent rows rendered as part of the link.

Fix: at end of row, if _lastUrl is still set, emit the OSC 8 close even when nothing else is buffered. Legacy-console path is unchanged (no OSC 8 emitted).

Tests

Three regression tests added in Tests/UnitTestsParallelizable/Drivers/Output/OutputBaseTests.cs:

All 16,847 parallelizable tests and 73 non-parallelizable tests pass.

To pull down this PR locally:

git remote add copilot https://github.com/harder/Terminal.Gui.git
git fetch copilot fix/4892-osc8-link-cleanup
git checkout copilot/fix/4892-osc8-link-cleanup

Two paths leak OSC 8 hyperlink state into adjacent rendering:

1. In `OutputBufferImpl.SetAttributeAndDirty`, when a cell that previously
   carried a URL is overdrawn by content with no `CurrentUrl` set (e.g. a
   parent view filling its content area, or a Link whose displayed text
   shrank), the cell becomes dirty but the stale entry stays in `_urlMap`.
   The next render wraps that cell in OSC 8, so unrelated content appears
   hyperlinked. Now the entry is removed when `CurrentUrl` is null/empty.

2. In `OutputBase.Write`, when dirty cells with a URL are flushed mid-row
   because a clean cell follows, the OSC 8 start sequence is emitted to
   the terminal but `outputStringBuilder` is cleared while `_lastUrl`
   stays set. If the row ends without another flush the existing
   `outputStringBuilder.Length <= 0` early-`continue` skips the OSC 8
   close, leaving the hyperlink open across the row boundary so later
   rows render as part of the link. Now the close is emitted at end of
   row whenever `_lastUrl` is set.

Adds three regression tests in `OutputBaseTests`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@harder harder requested a review from tig as a code owner May 8, 2026 00:09
Copy link
Copy Markdown
Member

@tig tig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent. Love the tests!

@tig tig merged commit 3cd3c0f into gui-cs:develop May 8, 2026
10 of 11 checks passed
@harder harder deleted the fix/4892-osc8-link-cleanup branch May 8, 2026 02:23
@BDisp
Copy link
Copy Markdown
Collaborator

BDisp commented May 8, 2026

@harder I'm still seeing hyperlinks in the TextView after deleting all the text. Is this really fully fixed?

@harder
Copy link
Copy Markdown
Collaborator Author

harder commented May 8, 2026

@harder I'm still seeing hyperlinks in the TextView after deleting all the text. Is this really fully fixed?

@BDisp I haven't been able to repro that so far, using the UICatalog. Can you send repro steps for what you are seeing?

@BDisp
Copy link
Copy Markdown
Collaborator

BDisp commented May 8, 2026

@BDisp I haven't been able to repro that so far, using the UICatalog. Can you send repro steps for what you are seeing?

Of course. Please open the UICatalog Editor scenario. You will see several URL links. Try deleting all the text and moving the mouse along the lines, and you will see that the underlines of the URL links will appear. Or without delete simply navigating through the pages or scrolling the gear up and down and you will also see the underlines appear. At least, this is true when using Windows Terminal.

@harder
Copy link
Copy Markdown
Collaborator Author

harder commented May 8, 2026

Thank you! That was a big help. I see the issue with underlines persisting intermittently when scrolling the mousesheel up or down on my MacOS warp terminal. And with Windows Terminal on my Windows laptop, I can repro both issues.

It looks like these are related to the same OSC 8 rendering code as what was fixed in this PR, but separate bugs that already existed and I didn't notice when testing earlier.

I'm creating a new issue and PR now!

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.

OSC8 Link rendering does not clean up Add resizing support to the core There is a problem with the high-intensity colors, they are not showing up

3 participants