fix(links): peel a CJK label off a path glued behind it (#1053)

An agent reporting a file it wrote prints `原型:/tmp/a.html` or
`原型:/tmp/a.html` with no space. The label rule only accepted ASCII
labels behind an ASCII colon, so the whole token was read as a relative
path and the click reported it missing under the pane's directory.

Accept any alphabetic label, one character long when it is not ASCII,
behind either colon. A single ASCII letter stays a Windows drive.
This commit is contained in:
l0ng-ai
2026-10-01 00:17:43 +08:00
committed by GitHub
parent 7e918f3a89
commit 4e4789a045
+21 -4
View File
@@ -1278,12 +1278,17 @@ fn left_cuts(chars: &[char]) -> Vec<usize> {
// follows has to be spelled like a path too, or `branch:main` and
// `remote:origin` become links the moment the pane's directory holds
// a `main/` or an `origin/`.
if let Some(i) = rest.iter().position(|&c| c == ':')
&& i >= 2
&& rest[0].is_ascii_alphabetic()
//
// CJK prose writes the label with no space and either colon:
// `原型:/tmp/a.html`, `原型:/tmp/a.html`. A one-character label is
// only a drive when it is an ASCII letter, so `图:/tmp/a.png` still
// counts as a label.
if let Some(i) = rest.iter().position(|&c| matches!(c, ':' | ':'))
&& (i >= 2 || (i == 1 && !rest[0].is_ascii()))
&& rest[0].is_alphabetic()
&& rest[..i]
.iter()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '+'))
.all(|c| c.is_alphanumeric() || matches!(c, '_' | '-' | '+'))
&& rest[i + 1..]
.iter()
.any(|c| matches!(c, '/' | '\\' | '.' | '~'))
@@ -2132,6 +2137,18 @@ mod tests {
assert_file_link(&line, col, Path::new("/"), &path, Some(42), None);
}
/// An agent reporting the file it wrote puts a CJK label and a colon
/// straight in front of the path, full-width or not.
#[test]
fn link_at_peels_a_cjk_label_off_an_absolute_path() {
let path = temp_file("cjk-label/mock.html");
for label in ["原型:", "原型:", "图:", "图:"] {
let line = format!("交互 HTML {label}{}", path.display());
let col = line.chars().position(|c| c == '/').unwrap() + 3;
assert_file_link(&line, col, Path::new("/"), &path, None, None);
}
}
#[test]
fn link_at_trims_wrappers_and_trailing_punctuation_around_file_paths() {
let path = temp_file("wrapped/src/lib.rs");