Fix cmd-click opening of relative/local paths (#9921)
This PR fixes an issue #9563 where relative file paths were not being resolved against the terminal’s current working directory before opening. #### Verification Tested with directories containing: ``` /tmp/test/test ❯ du -h . 0B ./spaces-end 0B ./with dot. 0B ./space middle 8.0K . ``` Parent directory resolution also works as expected: ``` /tmp/test/test ❯ du -h .. 0B ../test/spaces-end 0B ../test/with dot. 0B ../test/space middle 8.0K ../test 16K .. ``` @mitchellh In your original description you mentioned that “Links should work for all situations as they do in iTerm2.” I noticed that, for example, when running `ls`, the paths are not clickable, while they are clickable in iTerm2. If you think this case should also be handled, I can open a separate PR for it once this one is accepted.pull/9089/head
commit
29c0f982c3
|
|
@ -2037,6 +2037,23 @@ pub fn pwd(
|
||||||
return try alloc.dupe(u8, terminal_pwd);
|
return try alloc.dupe(u8, terminal_pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves a relative file path to an absolute path using the terminal's pwd.
|
||||||
|
fn resolvePathForOpening(
|
||||||
|
self: *Surface,
|
||||||
|
path: []const u8,
|
||||||
|
) Allocator.Error!?[]const u8 {
|
||||||
|
if (!std.fs.path.isAbsolute(path)) {
|
||||||
|
const terminal_pwd = self.io.terminal.getPwd() orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolved = try std.fs.path.resolve(self.alloc, &.{ terminal_pwd, path });
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the x/y coordinate of where the IME (Input Method Editor)
|
/// Returns the x/y coordinate of where the IME (Input Method Editor)
|
||||||
/// keyboard should be rendered.
|
/// keyboard should be rendered.
|
||||||
pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
pub fn imePoint(self: *const Surface) apprt.IMEPos {
|
||||||
|
|
@ -4265,7 +4282,12 @@ fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool {
|
||||||
.trim = false,
|
.trim = false,
|
||||||
});
|
});
|
||||||
defer self.alloc.free(str);
|
defer self.alloc.free(str);
|
||||||
try self.openUrl(.{ .kind = .unknown, .url = str });
|
|
||||||
|
const resolved_path = try self.resolvePathForOpening(str);
|
||||||
|
defer if (resolved_path) |p| self.alloc.free(p);
|
||||||
|
|
||||||
|
const url_to_open = resolved_path orelse str;
|
||||||
|
try self.openUrl(.{ .kind = .unknown, .url = url_to_open });
|
||||||
},
|
},
|
||||||
|
|
||||||
._open_osc8 => {
|
._open_osc8 => {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ pub const regex =
|
||||||
"(?:" ++ url_schemes ++
|
"(?:" ++ url_schemes ++
|
||||||
\\)(?:
|
\\)(?:
|
||||||
++ ipv6_url_pattern ++
|
++ ipv6_url_pattern ++
|
||||||
\\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(?<![,.])|(?:\.\.\/|\.\/|\/)[\w\-.~:\/?#@!$&*+,;=%]+(?:\/[\w\-.~:\/?#@!$&*+,;=%]*)*
|
\\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(?<![,.])|(?:\.\.\/|\.\/|\/)(?:(?=[\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]*[\/.])*(?: +(?= *$))?|(?![\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]+)*(?: +(?= *$))?)
|
||||||
;
|
;
|
||||||
const url_schemes =
|
const url_schemes =
|
||||||
\\https?://|mailto:|ftp://|file:|ssh:|git://|ssh://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:
|
\\https?://|mailto:|ftp://|file:|ssh:|git://|ssh://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:
|
||||||
|
|
@ -194,7 +194,7 @@ test "url regex" {
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.input = "../example.py ",
|
.input = "../example.py ",
|
||||||
.expect = "../example.py",
|
.expect = "../example.py ",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.input = "first time ../example.py contributor ",
|
.input = "first time ../example.py contributor ",
|
||||||
|
|
@ -253,6 +253,23 @@ test "url regex" {
|
||||||
.input = "IPv6 in markdown [link](http://[2001:db8::1]/docs)",
|
.input = "IPv6 in markdown [link](http://[2001:db8::1]/docs)",
|
||||||
.expect = "http://[2001:db8::1]/docs",
|
.expect = "http://[2001:db8::1]/docs",
|
||||||
},
|
},
|
||||||
|
// File paths with spaces
|
||||||
|
.{
|
||||||
|
.input = "./spaces-end. ",
|
||||||
|
.expect = "./spaces-end. ",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.input = "./space middle",
|
||||||
|
.expect = "./space middle",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.input = "../test folder/file.txt",
|
||||||
|
.expect = "../test folder/file.txt",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.input = "/tmp/test folder/file.txt",
|
||||||
|
.expect = "/tmp/test folder/file.txt",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (cases) |case| {
|
for (cases) |case| {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue