Skip to content

Fix terminal path detection inside parentheses#52222

Merged
Veykril merged 5 commits intozed-industries:mainfrom
butvinm:fix-terminal-hyperlinks
Apr 21, 2026
Merged

Fix terminal path detection inside parentheses#52222
Veykril merged 5 commits intozed-industries:mainfrom
butvinm:fix-terminal-hyperlinks

Conversation

@butvinm
Copy link
Copy Markdown
Contributor

@butvinm butvinm commented Mar 23, 2026

Summary

Paths inside parentheses without a preceding space (e.g. Update(.claude/skills/sandbox/SKILL.md) or Write(/test/cool.rs) from Claude Code output) were not clickable in the terminal. The ( character was allowed as a middle character in the default path hyperlink regex, causing the entire Update(.path/here to be matched as a single invalid path.

Changes:

  • Remove ( from the middle-chars alternation ([:(]:) in the default path hyperlink regex, so ( always acts as a path boundary — consistent with it already being excluded from first and last character sets. Preserves upstream's space exclusion after :.
  • Iterate all regex matches in the line instead of only the first, so the correct path (which may be the second match after a prefix like Update) is found. This also simplifies the code by removing the separate hovered-word search logic.

Known tradeoff:

Filenames with parentheses in the middle (e.g. docker-compose.prod(copy).yml) are no longer matched as a single path. This is uncommon in terminal output contexts (compiler errors, stack traces, tool output) and is documented with a should_panic test.

What doesn't break:

  • (/path/file.js:321:13)( at word start is excluded by the first-char rule (unchanged)
  • Node.js stack traces like at fn (/path/file.js:10:5) — space before ( makes it a separate word
  • All 38 terminal hyperlink tests pass

Related: #18556, #23774

Release Notes:

  • Fixed terminal path detection for paths inside parentheses without preceding space (e.g. Update(path) or Write(path) patterns from CLI tool output)

@cla-bot
Copy link
Copy Markdown

cla-bot Bot commented Mar 23, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @butvinm on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Mar 23, 2026
@butvinm butvinm changed the title terminal: Fix path detection inside parentheses Fix terminal path detection inside parentheses Mar 23, 2026
@butvinm
Copy link
Copy Markdown
Contributor Author

butvinm commented Mar 23, 2026

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Mar 23, 2026
@cla-bot
Copy link
Copy Markdown

cla-bot Bot commented Mar 23, 2026

The cla-bot has been summoned, and re-checked this pull request!

@butvinm butvinm marked this pull request as ready for review March 23, 2026 20:54
@zed-codeowner-coordinator zed-codeowner-coordinator Bot requested review from a team, Veykril and kubkon and removed request for a team March 23, 2026 20:54
Comment on lines +973 to +982
#[test]
#[should_panic(expected = "Path = «copy).yml»")]
// Filenames with parentheses in the middle (e.g. `file(copy).txt`) are not
// matched as a single path because `(` is treated as a path boundary. This is
// a tradeoff to support the more common `Tool(path)` pattern (e.g. Claude Code
// output like `Update(.claude/SKILL.md)`) where `(` is a delimiter, not part
// of the filename.
fn parens_in_filename_not_matched() {
test_path!("‹«docker-compose.prod(👉copy).yml»›");
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't really an acceptable tradeoff. Some folks tend to have usernames with parentheses inside which breaks especially on Windows systems

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I overlooked that it might be a common use case.

I implemented detection of such file paths following sanitize_url_punctuation logic. Now paths like /tmp/test(copy).txt are clickable.

Copy link
Copy Markdown
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

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

Thanks!

@Veykril Veykril enabled auto-merge (squash) March 24, 2026 07:57
butvinm and others added 3 commits March 24, 2026 22:11
Paths inside parentheses without a preceding space (e.g. `Update(path)`
or `Write(path)` from Claude Code output) were not clickable because
`(` was allowed as a middle character in the path regex, causing the
entire `Update(.path/here` to be matched as a single (invalid) path.

Two changes:
- Remove `(` from the middle-chars alternation in the default path
  hyperlink regex so `(` always acts as a path boundary (consistent
  with it already being excluded from first/last chars). Preserve
  upstream's space exclusion after `:`.
- Iterate all regex matches in the line instead of only the first,
  so the correct path (the second match) is found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Filenames with parentheses in the middle (e.g. `file(copy).txt`) are no
longer matched as a single path because `(` is now treated as a path
boundary. Document this known limitation with a should_panic test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Paths inside parentheses without a preceding space (e.g. `Update(path)`
from Claude Code output) were not clickable because the regex matched
the entire `Update(.path/here` as a single invalid path.

Instead of removing `(` from the regex (which would break filenames
with parentheses like `file(copy).txt` or Windows usernames), add a
balanced-parentheses post-processing step: when the matched path
contains more `(` than `)`, strip the prefix up to the first unmatched
`(`. This mirrors `sanitize_url_punctuation` which strips unbalanced
trailing `)` from URLs.

Also iterate all regex matches in the line instead of only the first,
so paths that appear after other words are found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
auto-merge was automatically disabled March 24, 2026 19:11

Head branch was pushed to by a user without write access

@butvinm butvinm force-pushed the fix-terminal-hyperlinks branch from d429c11 to 7561759 Compare March 24, 2026 19:11
@zelenenka zelenenka removed the Size M label Apr 9, 2026
@butvinm
Copy link
Copy Markdown
Contributor Author

butvinm commented Apr 9, 2026

@kubkon would you have a chance to look at this PR?

Copy link
Copy Markdown
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

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

Thanks!

@Veykril Veykril enabled auto-merge (squash) April 21, 2026 07:36
@Veykril Veykril merged commit d5fd199 into zed-industries:main Apr 21, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants