Jelajahi Sumber

fix(formatting): 保留中文型号与间隔号

在 pangu 排版前保护中文型号连字符和原始间隔号,避免生成错误空格或日文间隔号。

补充负数、运算符和幂等性回归测试。
darknessomi 2 minggu lalu
induk
melakukan
ba331f2f52
2 mengubah file dengan 46 tambahan dan 1 penghapusan
  1. 24 0
      src/lib/chapter-formatting.test.ts
  2. 22 1
      src/lib/chapter-formatting.ts

+ 24 - 0
src/lib/chapter-formatting.test.ts

@@ -7,6 +7,30 @@ test("inserts spaces between CJK and Latin on normal paragraphs", () => {
   expect(result).toBe("  中文 English 中文")
 })
 
+test("preserves compact CJK equipment model designations", () => {
+  const result = formatChapterWriting("苏-35与道尔-M1、米格-31B和山毛榉-M1")
+
+  expect(result).toBe("  苏-35 与道尔-M1、米格-31B 和山毛榉-M1")
+})
+
+test("preserves the original middle-dot characters", () => {
+  const result = formatChapterWriting("阿卜杜拉·萨利赫与让•皮埃尔、安德烈‧伊万诺夫")
+
+  expect(result).toBe("  阿卜杜拉·萨利赫与让•皮埃尔、安德烈‧伊万诺夫")
+})
+
+test("still spaces hyphen operators and preserves explicit negative numbers", () => {
+  const result = formatChapterWriting(["中文-英文", "气温 -35℃"].join("\n"))
+
+  expect(result).toBe(["  中文 - 英文", "  气温 -35℃"].join("\n"))
+})
+
+test("keeps protected model and middle-dot formatting idempotent", () => {
+  const once = formatChapterWriting("苏-35与阿卜杜拉·萨利赫")
+
+  expect(formatChapterWriting(once)).toBe(once)
+})
+
 test("keeps full-width first-line indent", () => {
   const result = formatChapterWriting("  纯中文段落")
 

+ 22 - 1
src/lib/chapter-formatting.ts

@@ -1,6 +1,27 @@
 import pangu from "pangu"
 import { parseFrontmatter } from "@/lib/frontmatter"
 
+const CJK_MODEL_HYPHEN =
+  /(?<=[\p{Script=Han}])-(?=(?=[A-Z0-9-]*\d)[A-Z0-9]+(?:-[A-Z0-9]+)*)/gu
+const LOSSY_MIDDLE_DOT = /[·•‧]/gu
+const PROTECTED_TEXT_PATTERN = /\uE100(\d+)\uE101/gu
+
+function spacingChapterText(text: string): string {
+  const protectedValues: string[] = []
+  const protect = (value: string): string => {
+    const index = protectedValues.push(value) - 1
+    return `\uE100${index}\uE101`
+  }
+
+  const protectedText = text
+    .replace(LOSSY_MIDDLE_DOT, (value) => protect(value))
+    .replace(CJK_MODEL_HYPHEN, () => protect("-"))
+
+  return pangu
+    .spacingText(protectedText)
+    .replace(PROTECTED_TEXT_PATTERN, (_match, index: string) => protectedValues[Number(index)] ?? "")
+}
+
 function isStructuralMarkdownLine(trimmed: string): boolean {
   return /^(#{1,6}\s|>\s|[-*+]\s|\d+\.\s|\|)/.test(trimmed) || /^\s*[-]{3,}\s*$/.test(trimmed)
 }
@@ -58,7 +79,7 @@ export function formatChapterWriting(markdown: string): string {
       formatted.push("")
     }
     const content = trimmed.replace(/^[  ]+/, "")
-    formatted.push(`  ${pangu.spacingText(content)}`)
+    formatted.push(`  ${spacingChapterText(content)}`)
     pendingBlank = true
     lastKind = "normal"
   }