chore(copy): check:copy 拦「其它」,并查中文模板插值片段开头的半角标点与 ${a}(${b}) 半角括号
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
9f55a5b506
commit
e9e20e7e79
1 file changed
+30
-1
@@ -45,6 +45,8 @@ function findIssues(text) {
|
||||
push("括号内有中文应用全角括号", new RegExp(`\\([^()]*[${CJK}][^()]*\\)`, "g"));
|
||||
push("引号应为「」", /[“”]/g);
|
||||
if (termScope) push("处理状态写「整理」不写「入库」(见 glossary)", TERM_RUKU);
|
||||
// 插值紧邻的半角括号:`${date}(${rel})` 在中文句子里,括号里多半是中文
|
||||
push("插值旁的半角括号应为全角(括号内有中文时)", new RegExp(`${PH}\\(${PH}\\)`, "g"));
|
||||
for (const m of text.matchAll(BANNED_WORDS)) out.push({ rule: `用词「${m[0]}」(见 human-copy)`, index: m.index });
|
||||
return out;
|
||||
}
|
||||
@@ -68,6 +70,7 @@ const BANNED_WORDS = new RegExp(
|
||||
"释放潜能",
|
||||
"尽在掌握",
|
||||
"成功地",
|
||||
"其它",
|
||||
"恭喜",
|
||||
"太棒了",
|
||||
"哎呀",
|
||||
@@ -197,6 +200,8 @@ function collect(ast, src) {
|
||||
const parts = node.quasis.map((q) => ({ start: q.start, end: q.end, text: src.slice(q.start, q.end) }));
|
||||
if (!parts.some((p) => looksLikeRegex(p.text))) groups.push(parts);
|
||||
}
|
||||
if (node.quasis.some((q) => CJK_RE.test(q.value.raw)))
|
||||
for (const e of node.expressions) for (const f of fragmentsOf(e)) checkFragment(f, src);
|
||||
for (const e of node.expressions) walk(e, node, "expressions");
|
||||
return;
|
||||
}
|
||||
@@ -225,6 +230,24 @@ function collect(ast, src) {
|
||||
return groups;
|
||||
}
|
||||
|
||||
/** 插值表达式里会被拼进外层中文模板的字面量(`${x ? `,…` : ""}` 里的 `,…`) */
|
||||
function fragmentsOf(e) {
|
||||
if (!e) return [];
|
||||
if (e.type === "StringLiteral" || e.type === "TemplateLiteral") return [e];
|
||||
if (e.type === "ConditionalExpression") return [...fragmentsOf(e.consequent), ...fragmentsOf(e.alternate)];
|
||||
if (e.type === "LogicalExpression") return [...fragmentsOf(e.left), ...fragmentsOf(e.right)];
|
||||
if (e.type === "TSAsExpression" || e.type === "ParenthesizedExpression") return fragmentsOf(e.expression);
|
||||
return [];
|
||||
}
|
||||
|
||||
/** 外层是中文模板时,插值片段开头的半角 , ; : 会紧贴中文 */
|
||||
let fragIssues = [];
|
||||
function checkFragment(f, src) {
|
||||
const text = f.type === "StringLiteral" ? src.slice(f.start + 1, f.end - 1) : src.slice(f.quasis[0].start, f.quasis[0].end);
|
||||
const m = /^\s*[,;:](?!\/\/)/.exec(text);
|
||||
if (m) fragIssues.push({ start: f.start + 1, rule: "中文模板的插值片段以半角标点开头", excerpt: text.slice(0, 20) });
|
||||
}
|
||||
|
||||
function lineOf(src, idx) {
|
||||
let line = 1;
|
||||
for (let i = 0; i < idx; i++) if (src.charCodeAt(i) === 10) line++;
|
||||
@@ -252,7 +275,13 @@ function processFile(file) {
|
||||
const lines = src.split("\n");
|
||||
const issues = [];
|
||||
const edits = [];
|
||||
for (const g of collect(ast, src)) {
|
||||
fragIssues = [];
|
||||
const groups = collect(ast, src);
|
||||
for (const fi of fragIssues) {
|
||||
const line = lineOf(src, fi.start);
|
||||
if (!allowed(lines, line)) issues.push({ line, rule: fi.rule, excerpt: fi.excerpt });
|
||||
}
|
||||
for (const g of groups) {
|
||||
const texts = g.filter(Boolean);
|
||||
if (texts.length === 0) continue;
|
||||
const composed = joinWithPH(g);
|
||||
|
||||
Reference in new issue
Block a user