mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
Fix clicking bare relative file paths
Related to #1972 The URL regex for file path detection requires paths to start with `../`, `./`, or `/`. For bare relative paths like `"src/config/url.zig"`, the regex could only match starting at the first `/`, producing `"/config/url.zig"` as a result — always dropping the first part of the path. Fix: added a third top-level alternative to the regex. This matches bare relative paths where: 1. The first component is word characters (possibly with dots/dashes): `[\w][\w\-.]*\/` 2. The remaining path must contain a dot (via positive lookahead) — this requires a file extension to avoid false positives on text like input/output 3. Add a `(?<!\w)\/` instead of `\/` in the existing prefix group — the standalone `/` prefix now requires that `/` is not preceded by a word character. This prevents `"input/output"` from falsely matching `"/output"` Test cases added: - src/config/url.zig → matches fully - app/folder/file.rb:1 → matches with line number - modified: src/config/url.zig → matches only the path part - lib/ghostty/terminal.zig:42:10 → matches with line:col - some-pkg/src/file.txt more text → stops before trailing text - input/output and foo/bar → correctly do not match (no file extension) The issue was nailed down here: https://github.com/ghostty-org/ghostty/issues/1972#issuecomment-3845717672
This commit is contained in:
@@ -26,7 +26,7 @@ pub const regex =
|
||||
"(?:" ++ url_schemes ++
|
||||
\\)(?:
|
||||
++ ipv6_url_pattern ++
|
||||
\\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(?<![,.])|(?:\.\.\/|\.\/|\/)(?:(?=[\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]*[\/.])*(?: +(?= *$))?|(?![\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]+)*(?: +(?= *$))?)
|
||||
\\|[\w\-.~:/?#@!$&*+,;=%]+(?:[\(\[]\w*[\)\]])?)+(?<![,.])|(?:\.\.\/|\.\/|(?<!\w)\/)(?:(?=[\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]*[\/.])*(?: +(?= *$))?|(?![\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]+)*(?: +(?= *$))?)|[\w][\w\-.]*\/(?=[\w\-.~:\/?#@!$&*+,;=%]*\.)[\w\-.~:\/?#@!$&*+,;=%]+(?: [\w\-.~:\/?#@!$&*+,;=%]*[\/.])*(?: +(?= *$))?
|
||||
;
|
||||
const url_schemes =
|
||||
\\https?://|mailto:|ftp://|file:|ssh:|git://|ssh://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:
|
||||
@@ -270,6 +270,27 @@ test "url regex" {
|
||||
.input = "/tmp/test folder/file.txt",
|
||||
.expect = "/tmp/test folder/file.txt",
|
||||
},
|
||||
// Bare relative file paths (no ./ or ../ prefix)
|
||||
.{
|
||||
.input = "src/config/url.zig",
|
||||
.expect = "src/config/url.zig",
|
||||
},
|
||||
.{
|
||||
.input = "app/folder/file.rb:1",
|
||||
.expect = "app/folder/file.rb:1",
|
||||
},
|
||||
.{
|
||||
.input = "modified: src/config/url.zig",
|
||||
.expect = "src/config/url.zig",
|
||||
},
|
||||
.{
|
||||
.input = "lib/ghostty/terminal.zig:42:10",
|
||||
.expect = "lib/ghostty/terminal.zig:42:10",
|
||||
},
|
||||
.{
|
||||
.input = "some-pkg/src/file.txt more text",
|
||||
.expect = "some-pkg/src/file.txt",
|
||||
},
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
@@ -284,4 +305,17 @@ test "url regex" {
|
||||
const match = case.input[@intCast(reg.starts()[0])..@intCast(reg.ends()[0])];
|
||||
try testing.expectEqualStrings(case.expect, match);
|
||||
}
|
||||
|
||||
// Bare relative paths without any dot should not match as file paths
|
||||
const no_match_cases = [_][]const u8{
|
||||
"input/output",
|
||||
"foo/bar",
|
||||
};
|
||||
for (no_match_cases) |input| {
|
||||
var result = re.search(input, .{});
|
||||
if (result) |*reg| {
|
||||
reg.deinit();
|
||||
return error.TestUnexpectedResult;
|
||||
} else |_| {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user