Improve inlining of string variables in template literals (#54849)

This commit is contained in:
Zzzen 2024-08-16 01:46:22 +08:00 committed by GitHub
parent 20f26a481b
commit a0530722fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 172 additions and 1 deletions

View File

@ -24,6 +24,9 @@ import {
isObjectLiteralExpression,
isPropertyAccessExpression,
isShorthandPropertyAssignment,
isStringLiteral,
isTaggedTemplateExpression,
isTemplateSpan,
isTypeQueryNode,
isVariableDeclarationInVariableStatement,
isVariableStatement,
@ -33,11 +36,14 @@ import {
refactor,
some,
SourceFile,
StringLiteral,
SymbolFlags,
TemplateSpan,
textChanges,
textRangeContainsPositionInclusive,
TypeChecker,
VariableDeclaration,
walkUpParenthesizedExpressions,
} from "../_namespaces/ts.js";
import {
RefactorErrorInfo,
@ -115,7 +121,13 @@ registerRefactor(refactorName, {
const { references, declaration, replacement } = info;
const edits = textChanges.ChangeTracker.with(context, tracker => {
for (const node of references) {
tracker.replaceNode(file, node, getReplacementExpression(node, replacement));
const closestStringIdentifierParent = isStringLiteral(replacement) && isIdentifier(node) && walkUpParenthesizedExpressions(node.parent);
if (closestStringIdentifierParent && isTemplateSpan(closestStringIdentifierParent) && !isTaggedTemplateExpression(closestStringIdentifierParent.parent.parent)) {
replaceTemplateStringVariableWithLiteral(tracker, file, closestStringIdentifierParent, replacement);
}
else {
tracker.replaceNode(file, node, getReplacementExpression(node, replacement));
}
}
tracker.delete(file, declaration);
});
@ -255,3 +267,17 @@ function getReplacementExpression(reference: Node, replacement: Expression) {
return replacement;
}
function replaceTemplateStringVariableWithLiteral(tracker: textChanges.ChangeTracker, sourceFile: SourceFile, reference: TemplateSpan, replacement: StringLiteral) {
const templateExpression = reference.parent;
const index = templateExpression.templateSpans.indexOf(reference);
const prevNode = index === 0 ? templateExpression.head : templateExpression.templateSpans[index - 1];
tracker.replaceRangeWithText(
sourceFile,
{
pos: prevNode.getEnd() - 2,
end: reference.literal.getStart() + 1,
},
replacement.text.replace(/\\/g, "\\\\").replace(/`/g, "\\`"),
);
}

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/pizza/*b*/ = "🍕";
////export const prompt = `Hello, would you like some ${pizza}?`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/message/*b*/ = "Hello, World!";
////await $`echo ${((message))}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: 'await $`echo ${(("Hello, World!"))}`;',
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/pizza/*b*/ = "🍕";
////export const prompt = `Hello, would you like some ${((pizza))}?`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/codeText/*b*/ = "Code-formatted text looks `like this` and requires surrounding by backticks (\\`).";
////export const mdTutorial = `Let's talk about markdown.\n${codeText}?`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const mdTutorial = `Let's talk about markdown.\\nCode-formatted text looks \\`like this\\` and requires surrounding by backticks (\\\\\\\`).?`;"
});

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
////const /*a*/pizza/*b*/ = "🍕";
////export const prompt = `Hello, would you like some ${
//// pizza
//// }?`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/pizza/*b*/ = "🍕";
////export const prompt = `Hello, would you like some ${pizza} or ${pizza}?`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const prompt = `Hello, would you like some 🍕 or 🍕?`;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/pizza/*b*/ = "🍕";
////export const prompt = `Hello, would you like some ${pizza}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const prompt = `Hello, would you like some 🍕`;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/x/*b*/ = "\\`";
////export const y = `${x}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const y = `\\\\\\``;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/x/*b*/ = "`";
////export const y = `${x}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: "export const y = `\\``;"
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/message/*b*/ = "Hello, World!";
////await $`echo ${message}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: 'await $`echo ${"Hello, World!"}`;',
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
////const /*a*/message/*b*/ = "Hello, World!";
////await $`echo ${(message)}`;
goTo.select("a", "b");
verify.refactorAvailable("Inline variable");
edit.applyRefactor({
refactorName: "Inline variable",
actionName: "Inline variable",
actionDescription: "Inline variable",
newContent: 'await $`echo ${("Hello, World!")}`;',
});