Skip to content

fix(addon-serialize): serialize the real underline style of hyperlink cells - #6206

Open
Tongering wants to merge 1 commit into
xtermjs:masterfrom
Tongering:fix/addon-serialize-hyperlink-underline
Open

Tongering wants to merge 1 commit into
xtermjs:masterfrom
Tongering:fix/addon-serialize-hyperlink-underline

Conversation

@Tongering

Copy link
Copy Markdown

Refs #4531, #6189 — both of those are about the OSC 8 sequences themselves not being serialized, which this PR does not address. What it fixes is the underline corruption that shows up on the same cells: the serializer treats the hyperlink render hint as a real attribute. Adding OSC 8 serialization is a follow-up.

Problem

The ANSI and HTML serializers read the underline attributes straight off IBufferCell, but ExtendedAttrs.underlineStyle reports DASHED for every hyperlink cell so the renderers draw links with a dashed underline. That hint is not part of the cell's real attributes, and it leaks into serialization in two ways:

  • A hyperlink cell that has no underline is written as SGR 4, so replaying a snapshot grows an underline the application never asked for.
  • A hyperlink cell that does have one is written as SGR 4:5 whatever its real style, so SGR 4 / 4:2 / 4:3 / 4:4 all come back as dashed.

With link(x) = OSC 8 ;; … ST + x + OSC 8 ;; ST:

input before after
A + link(B) A\x1b[4mB\x1b[0m AB
A + link(SGR 4 + B) A\x1b[4:5mB\x1b[0m A\x1b[4mB\x1b[0m
A + link(SGR 4:3 + B) A\x1b[4:5mB\x1b[0m A\x1b[4:3mB\x1b[0m

serializeAsHTML has the same problem: it emits text-decoration: underline dashed for an underlined link, and a decoration that was never requested for a bare link.

Two adjacent cells of the same hyperlink that differ only in their real underline style were also collapsed into one, because _diffStyle starts with a fast path that delegates to CellData.attributesEquals, which compares the same hint-glazed getters:

input before after
link(SGR 4:2 + AA + SGR 4:4 + BB) \x1b[4:5mAABB\x1b[0m \x1b[4:2mAA\x1b[4:4mBB\x1b[0m

Fix

Read the real underline state through a copy of the extended attributes with the link id cleared, and use it wherever the serializer previously used IBufferCell.isUnderline() or getUnderlineStyle(). Cells that are not part of a hyperlink take the untouched path and keep using the CellData.attributesEquals fast path, so their output is unchanged.

Note on CellData.attributesEquals

It has the same latent problem for hyperlink cells. Since it is only used by this addon I worked around it here rather than changing core; happy to move it into CellData instead if that is preferred.

Verification

  • 7 new unit tests (5 ANSI, 2 HTML); all 7 fail on master and pass with the fix.
  • npm run test-unit -- addons/addon-serialize/out-esbuild/SerializeAddon.test.js → 51 passing.
  • npm run lint → Found 0 warnings and 0 errors.
  • Additionally checked with a local 207-case harness (style × colour matrix, attribute combinations, realistic CLI output, boundary cases) using the raw stored style bits as the reference: 934/934 hyperlink cells round-trip their real underline style, and 374,414 non-hyperlink cells are unchanged. The harness is not part of this PR.

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.

1 participant