Skip to content

Commit 1c67eea

Browse files
committed
fix: only evaluate simple text block escape sequences
1 parent ad717a5 commit 1c67eea

3 files changed

Lines changed: 54 additions & 44 deletions

File tree

src/printers/helpers.ts

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -351,19 +351,12 @@ export function embedTextBlock(path: NamedNodePath<SyntaxType.StringLiteral>) {
351351
textToDoc: (text: string, options: Options) => Promise<Doc>
352352
) => {
353353
const doc = await textToDoc(text, { parser: language });
354-
return printTextBlock(path, escapeDocForTextBlock(doc));
354+
return printTextBlock(path, [escapeDocForTextBlock(doc), hardline]);
355355
};
356356
}
357357

358358
export function textBlockContents(node: NamedNode<SyntaxType.StringLiteral>) {
359-
const lines = node.value
360-
.replace(
361-
/(?<=^|[^\\])((?:\\\\)*)\\u+([0-9a-fA-F]{4})/g,
362-
(_, backslashPairs: string, hex: string) =>
363-
backslashPairs + String.fromCharCode(parseInt(hex, 16))
364-
)
365-
.split("\n")
366-
.slice(1);
359+
const lines = node.value.split("\n").slice(1);
367360
const baseIndent = findBaseIndent(lines);
368361
return lines
369362
.map(line => line.slice(baseIndent))
@@ -394,39 +387,28 @@ function findEmbeddedLanguage(path: NamedNodePath) {
394387
function escapeDocForTextBlock(doc: Doc) {
395388
return mapDoc(doc, currentDoc =>
396389
typeof currentDoc === "string"
397-
? currentDoc.replace(/\\|"""/g, match => `\\${match}`)
390+
? currentDoc.replace(/\\|"""/g, match =>
391+
match === "\\" ? "\\\\" : '""\\"'
392+
)
398393
: currentDoc
399394
);
400395
}
401396

402397
function unescapeTextBlockContents(text: string) {
403-
return text.replace(
404-
/\\(?:([bstnfr"'\\])|\n|\r\n?|([0-3][0-7]{0,2}|[0-7]{1,2}))/g,
405-
(_, single, octal) => {
406-
if (single) {
407-
switch (single) {
408-
case "b":
409-
return "\b";
410-
case "s":
411-
return " ";
412-
case "t":
413-
return "\t";
414-
case "n":
415-
return "\n";
416-
case "f":
417-
return "\f";
418-
case "r":
419-
return "\r";
420-
default:
421-
return single;
422-
}
423-
} else if (octal) {
424-
return String.fromCharCode(parseInt(octal, 8));
425-
} else {
426-
return "";
427-
}
398+
return text.replace(/\\(?:([stnr"'\\])|\n|\r\n?)/g, (_, escaped) => {
399+
switch (escaped) {
400+
case "s":
401+
return " ";
402+
case "t":
403+
return "\t";
404+
case "n":
405+
return "\n";
406+
case "r":
407+
return "\r";
408+
default:
409+
return escaped ?? "";
428410
}
429-
);
411+
});
430412
}
431413

432414
export type NamedNodePrinters = {

test/unit-test/text-blocks/_input.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ public void print(%s object) {
7272
void json() {
7373
// language = json
7474
String someJson = """
75-
{"glossary":{"title": "example glossary"}}
75+
{"glossary":{"title": "example \'glossary\'"}}
7676
""";
7777

7878
// language=json
7979
String config = """
80-
{ "name":"example",
80+
{ \t "name":"example",
8181
"enabled" :true,
8282
"timeout":30}
8383
""";
@@ -108,6 +108,17 @@ void html() {
108108
""";
109109
}
110110

111+
void typescript() {
112+
// language=typescript
113+
String typescript = """
114+
const s = `\"""`;
115+
""";
116+
117+
// language=typescript
118+
String typescript = """
119+
const s = ""; // \"""";
120+
}
121+
111122
void unsupported() {
112123
// language=unsupported
113124
String unsupported = """

test/unit-test/text-blocks/_output.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,26 @@ public void print(%s object) {
6565
String escapes = """
6666
\n\t\r\f\b\s\\
6767
\077
68-
A""";
68+
\u0041""";
6969

7070
void json() {
7171
// language = json
7272
String someJson = """
73-
{ "glossary": { "title": "example glossary" } }""";
73+
{ "glossary": { "title": "example 'glossary'" } }
74+
""";
7475

7576
// language=json
7677
String config = """
77-
{ "name": "example", "enabled": true, "timeout": 30 }""";
78+
{ "name": "example", "enabled": true, "timeout": 30 }
79+
""";
7880

7981
/* language = JSON */
8082
String query = """
8183
{
8284
"sql": "SELECT * FROM users WHERE active=1 AND deleted=0",
8385
"limit": 10
84-
}""";
86+
}
87+
""";
8588
}
8689

8790
void java() {
@@ -92,7 +95,8 @@ class Class {
9295
void method() {
9396
// comment
9497
}
95-
}""";
98+
}
99+
""";
96100
}
97101

98102
void html() {
@@ -107,7 +111,20 @@ void html() {
107111
<h1>My First Heading</h1>
108112
<p>My first paragraph.</p>
109113
</body>
110-
</html>""";
114+
</html>
115+
""";
116+
}
117+
118+
void typescript() {
119+
// language=typescript
120+
String typescript = """
121+
const s = `""\"`;
122+
""";
123+
124+
// language=typescript
125+
String typescript = """
126+
const s = ""; // "
127+
""";
111128
}
112129

113130
void unsupported() {

0 commit comments

Comments
 (0)