Skip to content

fix(url): extend URL detection across hard-wrap row boundaries#11613

Open
rndjams wants to merge 1 commit into
warpdotdev:masterfrom
rndjams:rndjams/fix-11609-url-hardwrap-join
Open

fix(url): extend URL detection across hard-wrap row boundaries#11613
rndjams wants to merge 1 commit into
warpdotdev:masterfrom
rndjams:rndjams/fix-11609-url-hardwrap-join

Conversation

@rndjams
Copy link
Copy Markdown

@rndjams rndjams commented May 23, 2026

Fixes #11609

Summary

AI TUI tools (Kiro CLI, Claude Code, GSD) write output with explicit \r\n, producing hard-wrapped rows where WRAPLINE is absent. When a URL overflows the terminal width, url_at_point returns only the first-row fragment — CMD+click opens a truncated, invalid URL.

Changes

1. url_at_point continuation probe (grid_handler.rs)

After the forward scan loop completes, probes across the hard-wrap boundary when:

  • URL ends within 4 cols of the right edge (near_right_edge)
  • Row is hard-wrapped (!self.row_wraps()WRAPLINE absent)
  • Next row exists (checked_add overflow-safe)

Guards are all O(1). Common case (short URL, soft-wrap, or URL not near edge) exits immediately at near_right_edge with zero additional allocations.

Continuation is rejected if the first char is uppercase (new-sentence guard) or whitespace (indented-block guard).

2. link_at_range newline stripping (terminal_model.rs)

Strips embedded \n/\r from the concatenated URL text. RFC 3986 URLs cannot contain raw newlines. All existing callers are URL-specific (guarded by GridHighlightedLink::Url). No-op for single-row URLs.

Behavior invariants

  1. URL ending near right edge of hard-wrapped row + valid continuation → full URL returned
  2. Existing soft-wrap detection unaffected (row_wraps() == true → probe does not fire)
  3. Uppercase-start continuation rejected (prevents false join with sentence starts)
  4. Whitespace-start continuation rejected
  5. Hovering on continuation row returns None (backward scan limitation — V1 scope)

Known limitation

URLs whose continuation begins with uppercase (e.g., /OAuth2/callback) are not extended. Deliberate tradeoff to avoid false joins with prose. V2 can relax this when continuation immediately follows /.

V1 scope boundary

Testing

cargo test -p warp --lib grid_handler_tests::test_url_extends_across_hard_wrap
cargo test -p warp --lib grid_handler_tests::test_url_hard_wrap_no_extend

4 new tests covering: extension across boundary, uppercase rejection, whitespace rejection, backward-scan limitation.

Existing test_find_url_line_wrapping (soft-wrap) passes unchanged.

Manual testing

  1. Run printf "See: https://auth.example.com/oauth/v2/authorize?client_id=app&state=abc123\r\n&redirect_uri=https%%3A%%2F%%2Fapp.example.com\r\n" in Warp
  2. Hover first row → full URL highlighted across both rows
  3. CMD+click → opens complete URL
  4. Hover second row → no highlight (V1 limitation, documented)

Some programs (e.g. kiro-cli) emit explicit \r\n, creating hard-wrapped
rows whose WRAPLINE flag is absent. The grapheme cursor in url_at_point
follows only soft wraps (Wrap::Soft), so it stops at hard-wrap row
boundaries and returns a truncated URL fragment when a URL overflows the
terminal width. CMD+click then silently opens the wrong address.

Two-part fix:

1. url_at_point (grid_handler.rs): after the main forward scan, if the
   detected URL ends near the right edge of a hard-wrapped row, scan the
   leading token of the following row. If it looks like a URL fragment
   (no URL separators, does not start with an uppercase letter) extend
   the Link range to include it.

2. link_at_range (terminal_model.rs): strip embedded \r/\n characters
   that appear when the URL range spans a hard-wrapped row boundary.
   RFC 3986 URLs cannot contain raw newlines, so this stripping is
   always correct.

The heuristic is conservative by design:
- Only fires when the URL ends within 4 columns of the right edge
- Requires the row to be hard-wrapped (WRAPLINE flag absent)
- Rejects continuations starting with uppercase (new sentence, not URL)
- Rejects continuations starting with whitespace (indented blocks)

Closes warpdotdev#11609
See also warpdotdev#6541 (OSC 8 hyperlinks — the clean upstream fix that would
supersede this heuristic once implemented)

Tests added:
- test_url_extends_across_hard_wrap_boundary
- test_url_hard_wrap_no_extend_for_uppercase_continuation
- test_url_hard_wrap_no_extend_for_space_continuation
@cla-bot cla-bot Bot added the cla-signed label May 23, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 23, 2026

@rndjams

This PR is not linked to an issue that is marked with ready-to-implement.

Issue-state enforcement details:

Readiness check:

To continue, link this PR to a same-repo issue such as Closes #123 in the PR description, and make sure that issue has ready-to-implement.

Powered by Oz

@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 23, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

@rndjams

This PR is not linked to an issue that is marked with ready-to-implement.

Issue-state enforcement details:

  • Associated same-repo issues checked: #11609

  • Required readiness label: ready-to-implement

Readiness check:

  • #11609: missing ready-to-implement; readiness labels present: none

To continue, link this PR to a same-repo issue such as Closes #123 in the PR description, and make sure that issue has ready-to-implement.

Powered by Oz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

URL soft-wrap: AI TUI output splits URLs across rows — CMD+click opens truncated fragment

1 participant