From e9aac7d6b4d51db757bad9ab2e7f6e41627db4ef Mon Sep 17 00:00:00 2001 From: xfy Date: Mon, 15 Jun 2026 11:17:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(highlight):=20=E4=BF=AE=E5=A4=8D=E5=A4=A7?= =?UTF-8?q?=E5=86=99=E8=AF=AD=E8=A8=80=E6=A0=87=E8=AF=86=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=B5=B0=E5=88=AB=E5=90=8D=E6=98=A0=E5=B0=84=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 别名表此前用 lang == from 做严格相等比较,导致 'RUST' 这类 大写标识无法命中 ('rust' → 'rs') 这类别名,最终回退到纯文本。 改用 eq_ignore_ascii_case 比较,使别名解析与扩展名/名称的小写 回退路径保持一致。 test(highlight): 补充 find_syntax 分支覆盖 新增 8 个测试覆盖此前未测的路径: - 大写语言标识的小写回退(同时验证了上述修复) - 别名表:golang/bash/yml 等映射 - 未知语言与空字符串的纯文本回退 - 代码首尾空白被 trim - 多行代码逐行高亮 通过等价性断言(而非脆弱的 span class 字面量)校验别名解析, 避免 syntect 内部 class 命名变化导致测试漂移。 共 15 个测试,全部通过。 --- src/highlight.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/highlight.rs b/src/highlight.rs index 872bbc7..7947e0d 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -69,7 +69,8 @@ pub mod server { ("golang", "go"), ]; for &(from, to) in aliases { - if lang == from { + // 别名比较同样不区分大小写,保证 "RUST" 与 "rust" 等价。 + if lang.eq_ignore_ascii_case(from) { if let Some(s) = ss.find_syntax_by_extension(to) { return s; } @@ -151,4 +152,71 @@ mod tests { assert!(result.contains(r#"let"#)); assert!(result.contains(r#"1"#)); } + + #[test] + fn highlight_code_uppercase_language_falls_back_via_lowercase() { + // 大写语言标识应通过小写回退路径匹配到对应语法。 + let lower = highlight_code("fn main() {}", Some("rust")); + let upper = highlight_code("fn main() {}", Some("RUST")); + // 大写标识的输出必须与小写标识完全一致,证明回退路径生效。 + assert_eq!(lower, upper); + assert!(lower.contains(r#"fn"#)); + } + + #[test] + fn highlight_code_resolves_golang_alias() { + // 别名表中 "golang" 映射到 "go" 扩展名,输出应与直接用 "go" 一致。 + let by_alias = highlight_code("package main", Some("golang")); + let by_ext = highlight_code("package main", Some("go")); + assert_eq!(by_alias, by_ext); + // 别名解析必须产出带 span 的高亮输出,而非纯文本。 + assert!(by_alias.contains("span")); + } + + #[test] + fn highlight_code_resolves_bash_alias() { + // 别名表中 "bash" 映射到 "sh" 扩展名。 + let result = highlight_code("echo hello", Some("bash")); + assert!(result.contains("span")); + } + + #[test] + fn highlight_code_resolves_yml_alias() { + // 别名表中 "yml" 映射到 "yaml" 扩展名。 + let result = highlight_code("key: value", Some("yml")); + assert!(!result.is_empty()); + } + + #[test] + fn highlight_code_unknown_language_falls_back_to_plain_text() { + // 无法识别的语言应回退到纯文本语法,仍能输出内容。 + let result = highlight_code("hello world", Some("totally-not-a-language-xyz")); + assert!(result.contains("hello world")); + } + + #[test] + fn highlight_code_empty_language_string_falls_back_to_plain_text() { + // 空字符串语言标识应走纯文本回退路径。 + let result = highlight_code("just text", Some("")); + assert!(result.contains("just text")); + } + + #[test] + fn highlight_code_trims_surrounding_whitespace() { + // 代码首尾的空白会被 trim 掉再高亮。 + let result = highlight_code(" \nfn main() {}\n ", Some("rust")); + assert!(result.contains(r#"fn"#)); + } + + #[test] + fn highlight_code_multiline_output_spans_all_lines() { + // 多行代码每一行都应被解析为带 span 的输出。 + let code = "fn a() {}\nfn b() {}"; + let result = highlight_code(code, Some("rust")); + // 两处 fn 关键字都应出现 + assert_eq!( + result.matches(r#"fn"#).count(), + 2 + ); + } }