Skip to content

Fix hover hint underline alignment and wrapped URL matching - #1884

Merged
raphamorim merged 4 commits into
mainfrom
hover-hint-cell-anchored
Aug 20, 2026
Merged

Fix hover hint underline alignment and wrapped URL matching#1884
raphamorim merged 4 commits into
mainfrom
hover-hint-cell-anchored

Conversation

@raphamorim

Copy link
Copy Markdown
Owner

The hover underline for URL hints could render shifted to the right of the actual URL. The regex hover path used oniguruma byte offsets directly as cell columns, so any multi-byte character left of the match on the row (box-drawing prefixes, CJK, emoji) dragged the underline right by one cell per extra UTF-8 byte. The same code searched a single visual row at a time, so soft-wrapped URLs matched truncated or not at all.

Matching now runs over the logical (unwrapped) line under the pointer, extracted together with a byte-to-cell map: every byte of the extracted text records its source cell, and match offsets resolve through the map, so bounds cannot drift regardless of encoding. Wide-char spacers are skipped, blank cells read as spaces, zero-width marks are emitted with their base character, and a match ending on a wide character extends over its spacer so the underline covers the full glyph. Wrapped URLs now resolve whole from any of their rows, and clicking opens the full URL.

Performance work on the probe path, since it runs on mouse movement:

  • Hint regexes are compiled once and cached; previously the URL pattern was recompiled on every mouse move.
  • A probe of the same viewport cell with the same modifiers is skipped entirely, without taking the terminal lock (the cell is derived from geometry alone). A shown highlight always reprobes so text changing under the underline stays fresh; wheel scroll and highlight clears reset the dedup.
  • The logical line is extracted once per probe and shared across all hint rules.
  • Reprobes that find the same match no longer re-mark full terminal damage per pixel while hovering a link.
  • Searches carry a per-search retry budget (via onig_sys, which the onig wrapper does not expose), so pathological content cannot stall the event loop through backtracking.
  • The wrap scan is bounded to 2048 cells on each side of the hovered row. Only matches containing the hovered cell matter, so nothing hoverable is lost for any sane URL length. Measured in release mode: a typical probe is ~12us; a 201-row wrapped wall of word characters, where the URL pattern's lookaheads rescan ahead at every position, drops from ~88ms to ~6ms.

Covered by new unit tests over a mock grid: exact cell bounds on plain ASCII, multi-byte prefixes, wide-char prefixes, soft-wrapped URLs from either row, a URL deep inside a huge wrapped line, and tail post-processing rules. Also adds a librio test pinning url_at alignment for the same scenario.

@raphamorim
raphamorim merged commit 86adfd1 into main Aug 20, 2026
7 checks passed
@raphamorim
raphamorim deleted the hover-hint-cell-anchored branch August 20, 2026 13:34
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