fix(utils): excluded ASCII replacements inside HTML comments

- Added an HTML comment state tracker to prompt formatting.
- Updated ASCII symbol replacement to skip substitutions inside `<!-- ... -->` comment blocks across lines.
- Added tests that preserved comment text while converting symbols outside comments.
This commit is contained in:
can1357
2026-05-14 06:40:50 +02:00
parent 933058a241
commit 12115e4cdf
2 changed files with 82 additions and 1 deletions
@@ -54,4 +54,28 @@ describe("prompt.format renderPhase", () => {
'|`cat <<\'EOF\' > file`|`write(path="file", content="…")`|\n|`sed -i \'s/old/new/\' file`|`edit(path="file", edits=[…])`|',
);
});
test("does not mutate HTML comment markers when replacing ascii symbols", () => {
const input = "<!-- Hidden continuation steer. role=user, suppressed from visible transcript. -->";
const output = prompt.format(input, {
renderPhase: "pre-render",
replaceAsciiSymbols: true,
});
expect(output).toBe(input);
});
test("replaces arrows outside but not inside HTML comments", () => {
const input = "<!-- -> in comment -->\nvalue -> value";
const output = prompt.format(input, {
renderPhase: "pre-render",
replaceAsciiSymbols: true,
});
expect(output).toBe("<!-- -> in comment -->\nvalue → value");
});
test("handles multi-line HTML comments", () => {
const input = "<!--\nA -> B\n-->";
const output = prompt.format(input, {
renderPhase: "pre-render",
replaceAsciiSymbols: true,
});
expect(output).toBe("<!--\nA -> B\n-->");
});
});
+58 -1
View File
@@ -66,6 +66,13 @@ function compactTableSep(line: string): string {
return `|${normalized.join("|")}|`;
}
const HTML_COMMENT_OPEN = "<!--";
const HTML_COMMENT_CLOSE = "-->";
type HtmlCommentState = {
inHtmlComment: boolean;
};
function replaceCommonAsciiSymbols(line: string): string {
return line
.replace(/\.{3}/g, "…")
@@ -77,6 +84,54 @@ function replaceCommonAsciiSymbols(line: string): string {
.replace(/>=/g, "≥");
}
function replaceCommonAsciiSymbolsOutsideHtmlComments(
line: string,
state: HtmlCommentState,
): string {
if (
!state.inHtmlComment &&
!line.includes(HTML_COMMENT_OPEN) &&
!line.includes(HTML_COMMENT_CLOSE)
) {
return replaceCommonAsciiSymbols(line);
}
let result = "";
let cursor = 0;
while (cursor < line.length) {
if (state.inHtmlComment) {
const closeIndex = line.indexOf(HTML_COMMENT_CLOSE, cursor);
if (closeIndex === -1) {
return result + line.slice(cursor);
}
result += line.slice(cursor, closeIndex + HTML_COMMENT_CLOSE.length);
cursor = closeIndex + HTML_COMMENT_CLOSE.length;
state.inHtmlComment = false;
continue;
}
const openIndex = line.indexOf(HTML_COMMENT_OPEN, cursor);
if (openIndex === -1) {
result += replaceCommonAsciiSymbols(line.slice(cursor));
return result;
}
result += replaceCommonAsciiSymbols(line.slice(cursor, openIndex));
const closeIndex = line.indexOf(HTML_COMMENT_CLOSE, openIndex + HTML_COMMENT_OPEN.length);
if (closeIndex === -1) {
result += line.slice(openIndex);
state.inHtmlComment = true;
return result;
}
result += line.slice(openIndex, closeIndex + HTML_COMMENT_CLOSE.length);
cursor = closeIndex + HTML_COMMENT_CLOSE.length;
}
return result;
}
export function format(content: string, options: PromptFormatOptions = {}): string {
const {
renderPhase = "post-render",
@@ -87,6 +142,8 @@ export function format(content: string, options: PromptFormatOptions = {}): stri
const lines = content.split("\n");
const result: string[] = [];
let inCodeBlock = false;
const htmlCommentState: HtmlCommentState = { inHtmlComment: false };
const topLevelTags: string[] = [];
for (let i = 0; i < lines.length; i++) {
@@ -104,7 +161,7 @@ export function format(content: string, options: PromptFormatOptions = {}): stri
}
if (replaceAsciiSymbols) {
line = replaceCommonAsciiSymbols(line);
line = replaceCommonAsciiSymbolsOutsideHtmlComments(line, htmlCommentState);
}
trimmedStart = line.trimStart();
const trimmed = line.trim();