From 393942c5a98387a08c2ae3164aa32a6cc9326258 Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Mon, 27 Apr 2026 00:36:17 -0600 Subject: [PATCH] fix: only evaluate simple text block escape sequences --- src/printers/helpers.ts | 54 +++++++++---------------- test/unit-test/text-blocks/_input.java | 15 ++++++- test/unit-test/text-blocks/_output.java | 29 ++++++++++--- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/printers/helpers.ts b/src/printers/helpers.ts index bbe9c792..e9bbe9a5 100644 --- a/src/printers/helpers.ts +++ b/src/printers/helpers.ts @@ -351,19 +351,12 @@ export function embedTextBlock(path: NamedNodePath) { textToDoc: (text: string, options: Options) => Promise ) => { const doc = await textToDoc(text, { parser: language }); - return printTextBlock(path, escapeDocForTextBlock(doc)); + return printTextBlock(path, [escapeDocForTextBlock(doc), hardline]); }; } export function textBlockContents(node: NamedNode) { - const lines = node.value - .replace( - /(?<=^|[^\\])((?:\\\\)*)\\u+([0-9a-fA-F]{4})/g, - (_, backslashPairs: string, hex: string) => - backslashPairs + String.fromCharCode(parseInt(hex, 16)) - ) - .split("\n") - .slice(1); + const lines = node.value.split("\n").slice(1); const baseIndent = findBaseIndent(lines); return lines .map(line => line.slice(baseIndent)) @@ -394,39 +387,28 @@ function findEmbeddedLanguage(path: NamedNodePath) { function escapeDocForTextBlock(doc: Doc) { return mapDoc(doc, currentDoc => typeof currentDoc === "string" - ? currentDoc.replace(/\\|"""/g, match => `\\${match}`) + ? currentDoc.replace(/\\|"""/g, match => + match === "\\" ? "\\\\" : '""\\"' + ) : currentDoc ); } function unescapeTextBlockContents(text: string) { - return text.replace( - /\\(?:([bstnfr"'\\])|\n|\r\n?|([0-3][0-7]{0,2}|[0-7]{1,2}))/g, - (_, single, octal) => { - if (single) { - switch (single) { - case "b": - return "\b"; - case "s": - return " "; - case "t": - return "\t"; - case "n": - return "\n"; - case "f": - return "\f"; - case "r": - return "\r"; - default: - return single; - } - } else if (octal) { - return String.fromCharCode(parseInt(octal, 8)); - } else { - return ""; - } + return text.replace(/\\(?:([stnr"'\\])|\n|\r\n?)/g, (_, escaped) => { + switch (escaped) { + case "s": + return " "; + case "t": + return "\t"; + case "n": + return "\n"; + case "r": + return "\r"; + default: + return escaped ?? ""; } - ); + }); } export type NamedNodePrinters = { diff --git a/test/unit-test/text-blocks/_input.java b/test/unit-test/text-blocks/_input.java index f5c166c6..8833657c 100644 --- a/test/unit-test/text-blocks/_input.java +++ b/test/unit-test/text-blocks/_input.java @@ -72,12 +72,12 @@ public void print(%s object) { void json() { // language = json String someJson = """ - {"glossary":{"title": "example glossary"}} + {"glossary":{"title": "example \'glossary\'"}} """; // language=json String config = """ - { "name":"example", + { \t "name":"example", "enabled" :true, "timeout":30} """; @@ -108,6 +108,17 @@ void html() { """; } + void typescript() { + // language=typescript + String typescript = """ + const s = `\"""`; + """; + + // language=typescript + String typescript = """ + const s = ""; // \""""; + } + void unsupported() { // language=unsupported String unsupported = """ diff --git a/test/unit-test/text-blocks/_output.java b/test/unit-test/text-blocks/_output.java index 9043f721..de2abca8 100644 --- a/test/unit-test/text-blocks/_output.java +++ b/test/unit-test/text-blocks/_output.java @@ -65,23 +65,26 @@ public void print(%s object) { String escapes = """ \n\t\r\f\b\s\\ \077 - A"""; + \u0041"""; void json() { // language = json String someJson = """ - { "glossary": { "title": "example glossary" } }"""; + { "glossary": { "title": "example 'glossary'" } } + """; // language=json String config = """ - { "name": "example", "enabled": true, "timeout": 30 }"""; + { "name": "example", "enabled": true, "timeout": 30 } + """; /* language = JSON */ String query = """ { "sql": "SELECT * FROM users WHERE active=1 AND deleted=0", "limit": 10 - }"""; + } + """; } void java() { @@ -92,7 +95,8 @@ class Class { void method() { // comment } - }"""; + } + """; } void html() { @@ -107,7 +111,20 @@ void html() {

My First Heading

My first paragraph.

- """; + + """; + } + + void typescript() { + // language=typescript + String typescript = """ + const s = `""\"`; + """; + + // language=typescript + String typescript = """ + const s = ""; // " + """; } void unsupported() {