From a0b0e54aa69f5859eea09d4b9f8e7945e3536f51 Mon Sep 17 00:00:00 2001 From: Pym Date: Sun, 16 Feb 2025 22:54:09 +0100 Subject: [PATCH] Fix clickable tilde paths in terminal - Improve path handling in Surface.zig to correctly expand standalone tilde (~) to $HOME - Update URL regex in config/url.zig This change ensures that: - Standalone tilde (~) is clickable and opens the home directory - Paths starting with ~/ correctly expand to the home directory - Other path patterns remain unchanged Add test cases for tilde path handling in url.zig --- src/Surface.zig | 16 ++++++++++++++- src/config/url.zig | 17 +++++++++++++++- src/renderer/link.zig | 46 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 98c344927..d1a517b65 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3275,7 +3275,21 @@ fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool { .trim = false, }); defer self.alloc.free(str); - try internal_os.open(self.alloc, .unknown, str); + + // Handle paths starting with ~ + if (str.len > 0 and str[0] == '~') { + const home = std.posix.getenv("HOME") orelse ""; + const path = if (str.len == 1) + try std.fmt.allocPrint(self.alloc, "{s}", .{home}) + else if (str[1] == '/') + try std.fmt.allocPrint(self.alloc, "{s}{s}", .{ home, str[1..] }) + else + try std.fmt.allocPrint(self.alloc, "{s}", .{str}); + defer self.alloc.free(path); + try internal_os.open(self.alloc, .unknown, path); + } else { + try internal_os.open(self.alloc, .unknown, str); + } }, ._open_osc8 => { diff --git a/src/config/url.zig b/src/config/url.zig index 78f9816fd..2ccabc894 100644 --- a/src/config/url.zig +++ b/src/config/url.zig @@ -26,7 +26,7 @@ pub const regex = "(?:" ++ url_schemes ++ \\)(?: ++ ipv6_url_pattern ++ - \\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(? 0 and path[0] == '~') { + const home = std.posix.getenv("HOME") orelse ""; + if (path.len > 1 and path[1] == '/') { + const result = try std.fmt.allocPrint(alloc, "{s}{s}", .{ home, path[1..] }); + return result; + } else if (path.len == 1) { + const result = try std.fmt.allocPrint(alloc, "{s}", .{home}); + return result; + } + } + return path; + } + /// Returns the slice of links from the configuration. pub fn fromConfig( alloc: Allocator, @@ -112,7 +135,6 @@ pub const Set = struct { mouse_pin: terminal.Pin, mouse_mods: inputpkg.Mods, ) !void { - _ = alloc; // If the right mods aren't pressed, then we can't match. if (!mouse_mods.equal(inputpkg.ctrlOrSuper(.{}))) return; @@ -129,15 +151,18 @@ pub const Set = struct { }; const link = page.hyperlink_set.get(page.memory, link_id); + // Convert paths starting with `~` to absolute paths + const absoluteUri = try interpretHomeDirectory(alloc, link.uri.offset.ptr(page.memory)[0..link.uri.len]); + // If our link has an implicit ID (no ID set explicitly via OSC8) // then we use an alternate matching technique that iterates forward // and backward until it finds boundaries. if (link.id == .implicit) { - const uri = link.uri.offset.ptr(page.memory)[0..link.uri.len]; return try self.matchSetFromOSC8Implicit( + alloc, matches, mouse_pin, - uri, + absoluteUri, ); } @@ -203,6 +228,7 @@ pub const Set = struct { /// around the mouse pin. fn matchSetFromOSC8Implicit( self: *const Set, + alloc: std.mem.Allocator, matches: *std.ArrayList(terminal.Selection), mouse_pin: terminal.Pin, uri: []const u8, @@ -231,9 +257,12 @@ pub const Set = struct { // If this link has an explicit ID then we found a boundary if (link.id != .implicit) break; - // If this link has a different URI then we found a boundary + // Convert paths starting with `~` to absolute paths const cell_uri = link.uri.offset.ptr(page.memory)[0..link.uri.len]; - if (!std.mem.eql(u8, uri, cell_uri)) break; + const absoluteCellUri = try interpretHomeDirectory(alloc, cell_uri); + + // If this link has a different URI then we found a boundary + if (!std.mem.eql(u8, uri, absoluteCellUri)) break; sel.startPtr().* = cell_pin; } @@ -257,9 +286,12 @@ pub const Set = struct { // If this link has an explicit ID then we found a boundary if (link.id != .implicit) break; - // If this link has a different URI then we found a boundary + // Convert paths starting with `~` to absolute paths const cell_uri = link.uri.offset.ptr(page.memory)[0..link.uri.len]; - if (!std.mem.eql(u8, uri, cell_uri)) break; + const absoluteCellUri = try interpretHomeDirectory(alloc, cell_uri); + + // If this link has a different URI then we found a boundary + if (!std.mem.eql(u8, uri, absoluteCellUri)) break; sel.endPtr().* = cell_pin; }