From 5657f302a6a52e56ebe43756502f7a214e58b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Tue, 17 Mar 2026 18:29:44 -0600 Subject: [PATCH] refactor: fix unicode regex discrepancy and standardize function naming (#1031) Add missing \u1680 (Ogham space mark) to UNICODE_SPACES in path-utils.ts and loader.ts. Make edit-diff.ts import the shared constant from path-utils.ts instead of maintaining an inline copy. Rename hashlineParseText to parseHashlineText to follow the parseX() convention used across the codebase. Co-authored-by: Claude Opus 4.6 (1M context) --- .../src/core/extensions/loader.ts | 2 +- .../pi-coding-agent/src/core/tools/edit-diff.ts | 4 ++-- .../src/core/tools/hashline-edit.ts | 8 ++++---- .../src/core/tools/hashline.test.ts | 16 ++++++++-------- .../pi-coding-agent/src/core/tools/hashline.ts | 2 +- packages/pi-coding-agent/src/core/tools/index.ts | 2 +- .../pi-coding-agent/src/core/tools/path-utils.ts | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/pi-coding-agent/src/core/extensions/loader.ts b/packages/pi-coding-agent/src/core/extensions/loader.ts index 90ff9b4fc..c3c4f2a1b 100644 --- a/packages/pi-coding-agent/src/core/extensions/loader.ts +++ b/packages/pi-coding-agent/src/core/extensions/loader.ts @@ -105,7 +105,7 @@ function getAliases(): Record { return _aliases; } -const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g; +const UNICODE_SPACES = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g; function normalizeUnicodeSpaces(str: string): string { return str.replace(UNICODE_SPACES, " "); diff --git a/packages/pi-coding-agent/src/core/tools/edit-diff.ts b/packages/pi-coding-agent/src/core/tools/edit-diff.ts index b0dce1beb..a5a7f0c2b 100644 --- a/packages/pi-coding-agent/src/core/tools/edit-diff.ts +++ b/packages/pi-coding-agent/src/core/tools/edit-diff.ts @@ -9,7 +9,7 @@ import { constants } from "fs"; import { access, readFile } from "fs/promises"; -import { resolveToCwd } from "./path-utils.js"; +import { resolveToCwd, UNICODE_SPACES } from "./path-utils.js"; export function detectLineEnding(content: string): "\r\n" | "\n" { const crlfIdx = content.indexOf("\r\n"); @@ -41,7 +41,7 @@ export function normalizeForFuzzyMatch(text: string): string { .replace(/[“”]/g, '"') .replace(/[‘’]/g, "'") .replace(/[‐‑‒–—−]/g, "-") - .replace(/[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g, " ") + .replace(UNICODE_SPACES, " ") .split("\n") .map((line) => line.replace(/[ \t]+$/g, "")) .join("\n"); diff --git a/packages/pi-coding-agent/src/core/tools/hashline-edit.ts b/packages/pi-coding-agent/src/core/tools/hashline-edit.ts index c423f805f..137eb9305 100644 --- a/packages/pi-coding-agent/src/core/tools/hashline-edit.ts +++ b/packages/pi-coding-agent/src/core/tools/hashline-edit.ts @@ -20,7 +20,7 @@ import { applyHashlineEdits, computeLineHash, type HashlineEdit, - hashlineParseText, + parseHashlineText, parseTag, } from "./hashline.js"; import { resolveToCwd } from "./path-utils.js"; @@ -99,7 +99,7 @@ function tryParseTag(raw: string): Anchor | undefined { function resolveEditAnchors(edits: HashlineEditItem[]): HashlineEdit[] { const result: HashlineEdit[] = []; for (const edit of edits) { - const lines = hashlineParseText(edit.lines); + const lines = parseHashlineText(edit.lines); const tag = edit.pos ? tryParseTag(edit.pos) : undefined; const end = edit.end ? tryParseTag(edit.end) : undefined; @@ -207,9 +207,9 @@ export function createHashlineEditTool(cwd: string, options?: HashlineEditToolOp for (const edit of edits) { if ((edit.op === "append" || edit.op === "prepend") && !edit.pos && !edit.end) { if (edit.op === "prepend") { - lines.unshift(...hashlineParseText(edit.lines)); + lines.unshift(...parseHashlineText(edit.lines)); } else { - lines.push(...hashlineParseText(edit.lines)); + lines.push(...parseHashlineText(edit.lines)); } } else { throw new Error(`File not found: ${path}`); diff --git a/packages/pi-coding-agent/src/core/tools/hashline.test.ts b/packages/pi-coding-agent/src/core/tools/hashline.test.ts index 4ab41331a..a4df96d3a 100644 --- a/packages/pi-coding-agent/src/core/tools/hashline.test.ts +++ b/packages/pi-coding-agent/src/core/tools/hashline.test.ts @@ -8,7 +8,7 @@ import { validateLineRef, applyHashlineEdits, HashlineMismatchError, - hashlineParseText, + parseHashlineText, stripNewLinePrefixes, type HashlineEdit, type Anchor, @@ -395,31 +395,31 @@ describe("stripNewLinePrefixes", () => { }); // ═══════════════════════════════════════════════════════════════════════════ -// hashlineParseText +// parseHashlineText // ═══════════════════════════════════════════════════════════════════════════ -describe("hashlineParseText", () => { +describe("parseHashlineText", () => { it("returns empty array for null", () => { - assert.deepEqual(hashlineParseText(null), []); + assert.deepEqual(parseHashlineText(null), []); }); it("returns array input as-is when no strip heuristic applies", () => { const input = ["- [x] done", "- [ ] todo"]; - assert.equal(hashlineParseText(input), input); + assert.equal(parseHashlineText(input), input); }); it("splits string on newline and preserves Markdown list '-' prefix", () => { - const result = hashlineParseText("- item one\n- item two\n- item three"); + const result = parseHashlineText("- item one\n- item two\n- item three"); assert.deepEqual(result, ["- item one", "- item two", "- item three"]); }); it("strips '+' diff markers from string input", () => { - const result = hashlineParseText("+line one\n+line two"); + const result = parseHashlineText("+line one\n+line two"); assert.deepEqual(result, ["line one", "line two"]); }); it("still strips trailing empty from string split", () => { - assert.deepEqual(hashlineParseText("foo\n"), ["foo"]); + assert.deepEqual(parseHashlineText("foo\n"), ["foo"]); }); }); diff --git a/packages/pi-coding-agent/src/core/tools/hashline.ts b/packages/pi-coding-agent/src/core/tools/hashline.ts index 7f3908e8c..f9aa90e2b 100644 --- a/packages/pi-coding-agent/src/core/tools/hashline.ts +++ b/packages/pi-coding-agent/src/core/tools/hashline.ts @@ -230,7 +230,7 @@ export function stripNewLinePrefixes(lines: string[]): string[] { * Parse edit content — handles string, array, or null input. * Strips hashline prefixes and diff markers from model output. */ -export function hashlineParseText(edit: string[] | string | null): string[] { +export function parseHashlineText(edit: string[] | string | null): string[] { if (edit === null) return []; if (typeof edit === "string") { const normalizedEdit = edit.endsWith("\n") ? edit.slice(0, -1) : edit; diff --git a/packages/pi-coding-agent/src/core/tools/index.ts b/packages/pi-coding-agent/src/core/tools/index.ts index c321b3211..d54ac2a9c 100644 --- a/packages/pi-coding-agent/src/core/tools/index.ts +++ b/packages/pi-coding-agent/src/core/tools/index.ts @@ -99,7 +99,7 @@ export { formatLineTag, type HashlineEdit, HashlineMismatchError, - hashlineParseText, + parseHashlineText, type HashMismatch, parseTag, stripNewLinePrefixes, diff --git a/packages/pi-coding-agent/src/core/tools/path-utils.ts b/packages/pi-coding-agent/src/core/tools/path-utils.ts index baf2d812a..8c01d3ad2 100644 --- a/packages/pi-coding-agent/src/core/tools/path-utils.ts +++ b/packages/pi-coding-agent/src/core/tools/path-utils.ts @@ -2,7 +2,7 @@ import { accessSync, constants } from "node:fs"; import * as os from "node:os"; import { isAbsolute, resolve as resolvePath } from "node:path"; -const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g; +export const UNICODE_SPACES = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g; const NARROW_NO_BREAK_SPACE = "\u202F"; function normalizeUnicodeSpaces(str: string): string { return str.replace(UNICODE_SPACES, " ");