From c1e0db7953e0a2f558ae156fb7f4db2f77e356ed Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 5 Aug 2019 09:31:59 -0700 Subject: [PATCH] Escape apparent substitution in synthesized NoSubstitutionTemplateLiterals (#32580) * Add failing test * Escape apparent substitution in synthesized NoSubstitutionTemplateLiterals --- src/compiler/utilities.ts | 13 ++++++++++++- ...ParamsToDestructuredObject_templateLiteral.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 91e6515f664..4ca13e663ab 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -566,6 +566,8 @@ namespace ts { return emitNode && emitNode.flags || 0; } + const escapeNoSubstitutionTemplateLiteralText = compose(escapeString, escapeTemplateSubstitution); + const escapeNonAsciiNoSubstitutionTemplateLiteralText = compose(escapeNonAsciiString, escapeTemplateSubstitution); export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, neverAsciiEscape: boolean | undefined) { // If we don't need to downlevel and we can reach the original source text using // the node's parent reference, then simply get the text as it was originally written. @@ -576,7 +578,11 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } - const escapeText = neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString : escapeNonAsciiString; + // If a NoSubstitutionTemplateLiteral appears to have a substitution in it, the original text + // had to include a backslash: `not \${a} substitution`. + const escapeText = neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? + node.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? escapeNoSubstitutionTemplateLiteralText : escapeString : + node.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? escapeNonAsciiNoSubstitutionTemplateLiteralText : escapeNonAsciiString; // If we can't reach the original source text, use the canonical form if it's a number, // or a (possibly escaped) quoted form of the original text if it's string-like. @@ -3113,6 +3119,11 @@ namespace ts { } } + const templateSubstitutionRegExp = /\$\{/g; + function escapeTemplateSubstitution(str: string): string { + return str.replace(templateSubstitutionRegExp, "\\${"); + } + // This consists of the first 19 unprintable ASCII characters, canonical escapes, lineSeparator, // paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in // the language service. These characters should be escaped when printing, and if any characters are added, diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts new file mode 100644 index 00000000000..1075fb7dddb --- /dev/null +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts @@ -0,0 +1,16 @@ +/// + +////function /*a*/insert/*b*/(template: string, overwriteBefore = 0) {} +////insert(`this is \${not} a substitution`); + + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert parameters to destructured object", + actionName: "Convert parameters to destructured object", + actionDescription: "Convert parameters to destructured object", + newContent: [ + 'function insert({ template, overwriteBefore = 0 }: { template: string; overwriteBefore?: number; }) {}', + 'insert({ template: `this is \\${not} a substitution` });' + ].join('\n') +});