Skip to content

Conversation

@gsLocker
Copy link

Enables clicking on tilde-based paths like ~/.config/file.txt. When clicked, the path is expanded to the user's home directory and opened.

Related to #1972

This addresses the maintainer feedback from PR #5813 by reusing the existing os.expandHome() function rather than duplicating tilde expansion logic. The implementation is cross-platform (Linux/macOS/FreeBSD) from the start.

Implementation:

  • Modified URL regex to detect ~/path patterns (excludes bare ~ and ~user)
  • Removed :,. from file path character class to avoid underlining punctuation
  • Added negative lookbehind (?<!.) to prevent trailing period matches
  • Reuse os.expandHome() for tilde expansion in os/open.zig
  • Added safety trimming for edge case punctuation handling
  • Added test cases for positive and negative scenarios

The regex matches ~/path but not bare ~ or ~user/path (which would require different expansion logic). Trailing punctuation like colons and periods are excluded from matching to avoid visual clutter in common output like "Error at ~/.bashrc:42".

Enables clicking on tilde-based paths like ~/.config/file.txt. When
clicked, the path is expanded to the user's home directory and opened.

Related to ghostty-org#1972

This addresses the maintainer feedback from PR ghostty-org#5813 by reusing the
existing os.expandHome() function rather than duplicating tilde expansion
logic. The implementation is cross-platform (Linux/macOS/FreeBSD) from
the start.

Implementation:
- Modified URL regex to detect ~/path patterns (excludes bare ~ and ~user)
- Removed :,. from file path character class to avoid underlining punctuation
- Added negative lookbehind (?<!\.) to prevent trailing period matches
- Reuse os.expandHome() for tilde expansion in os/open.zig
- Added safety trimming for edge case punctuation handling
- Added test cases for positive and negative scenarios

The regex matches ~/path but not bare ~ or ~user/path (which would require
different expansion logic). Trailing punctuation like colons and periods
are excluded from matching to avoid visual clutter in common output like
"Error at ~/.bashrc:42".
Copy link
Contributor

@mitchellh mitchellh left a comment

Choose a reason for hiding this comment

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

I have to do a bit more review but one note.

The fallback open helper in os/open.zig is not used by all platforms. Each OS implements its own native opening mechanism (NSWorkspace on macOS, DBus on Linux, etc.), so URL processing like tilde expansion must happen before these platform handlers are called.

This moves tilde path expansion from os/open.zig to Surface.zig where URLs are initially detected and matched. Expansion now occurs in processLinks() when links are clicked and in mouseRefreshLinks() when hover previews are shown.

This ensures all OS-specific open handlers receive already-expanded paths and maintains proper separation of concerns in the URL handling pipeline.
@gsLocker gsLocker requested a review from a team as a code owner October 19, 2025 16:28
@gsLocker gsLocker requested a review from mitchellh October 19, 2025 18:46
Copy link
Contributor

@mitchellh mitchellh left a comment

Choose a reason for hiding this comment

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

Repeating the hostname expansion is also surely not right. Is this AI-assisted?

Tilde paths like ~/.config/file.txt weren't clickable before. This adds
support for Ctrl/Cmd+clicking them, expanding ~ to your home directory
when opened.

The expansion happens in the link matching layer so all the platform
handlers (NSWorkspace, DBus, etc.) get already-expanded paths. Also
added a fast path for hover/click detection that only scans the line
under the cursor instead of building a match set for the entire viewport.

Line/column numbers (like :42 or :42:10) are stripped from paths before
opening to handle compiler error output correctly.
The tilde expansion and URL preparation logic was duplicated across 4
different functions. This refactoring consolidates it into a single
`prepareUrl()` helper function that:

- Preserves OSC8 link integrity (no trimming of authoritative links)
- Trims trailing punctuation/line numbers only from regex-detected links
- Expands tilde paths for both link types
- Returns an allocated copy with clear ownership

Changes:
- Add prepareUrl() helper that combines trimming and expansion
- Replace all expandAndDupeUrl() calls with prepareUrl()
- Fix matchSetFromOSC8Implicit to use passed prepared_url parameter
- Remove redundant expandAndDupeUrl function

@mitchellh This eliminates the code duplication you identified. However,
I notice we still perform expansion multiple times for the same URL
(e.g., once during hover preview, again on click). Should I implement
caching to avoid these redundant expansion operations, or is the current
approach acceptable?

The expansion now happens in the link matching layer as requested,
just centralized in one helper rather than scattered across functions.
@gsLocker gsLocker requested a review from a team as a code owner October 22, 2025 13:48
@gsLocker
Copy link
Author

Repeating the hostname expansion is also surely not right. Is this AI-assisted?

@mitchellh yeah you were right, having the same expansion code copy-pasted 4 times was me being lazy and not checking my agent's work.

I consolidated it all into one prepareUrl() helper that handles both OSC8 and regex links properly (OSC8 stays untouched, regex gets trimmed)

one thing - we're still expanding the same URL multiple times when you hover then click. want me to add caching? didn't wanna overcomplicate if it's not needed

@gsLocker gsLocker requested a review from mitchellh October 25, 2025 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants