Skip to content

Commit a053072

Browse files
authored
Improve inlining of string variables in template literals (#54849)
1 parent 20f26a4 commit a053072

12 files changed

+172
-1
lines changed

src/services/refactors/inlineVariable.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import {
2424
isObjectLiteralExpression,
2525
isPropertyAccessExpression,
2626
isShorthandPropertyAssignment,
27+
isStringLiteral,
28+
isTaggedTemplateExpression,
29+
isTemplateSpan,
2730
isTypeQueryNode,
2831
isVariableDeclarationInVariableStatement,
2932
isVariableStatement,
@@ -33,11 +36,14 @@ import {
3336
refactor,
3437
some,
3538
SourceFile,
39+
StringLiteral,
3640
SymbolFlags,
41+
TemplateSpan,
3742
textChanges,
3843
textRangeContainsPositionInclusive,
3944
TypeChecker,
4045
VariableDeclaration,
46+
walkUpParenthesizedExpressions,
4147
} from "../_namespaces/ts.js";
4248
import {
4349
RefactorErrorInfo,
@@ -115,7 +121,13 @@ registerRefactor(refactorName, {
115121
const { references, declaration, replacement } = info;
116122
const edits = textChanges.ChangeTracker.with(context, tracker => {
117123
for (const node of references) {
118-
tracker.replaceNode(file, node, getReplacementExpression(node, replacement));
124+
const closestStringIdentifierParent = isStringLiteral(replacement) && isIdentifier(node) && walkUpParenthesizedExpressions(node.parent);
125+
if (closestStringIdentifierParent && isTemplateSpan(closestStringIdentifierParent) && !isTaggedTemplateExpression(closestStringIdentifierParent.parent.parent)) {
126+
replaceTemplateStringVariableWithLiteral(tracker, file, closestStringIdentifierParent, replacement);
127+
}
128+
else {
129+
tracker.replaceNode(file, node, getReplacementExpression(node, replacement));
130+
}
119131
}
120132
tracker.delete(file, declaration);
121133
});
@@ -255,3 +267,17 @@ function getReplacementExpression(reference: Node, replacement: Expression) {
255267

256268
return replacement;
257269
}
270+
271+
function replaceTemplateStringVariableWithLiteral(tracker: textChanges.ChangeTracker, sourceFile: SourceFile, reference: TemplateSpan, replacement: StringLiteral) {
272+
const templateExpression = reference.parent;
273+
const index = templateExpression.templateSpans.indexOf(reference);
274+
const prevNode = index === 0 ? templateExpression.head : templateExpression.templateSpans[index - 1];
275+
tracker.replaceRangeWithText(
276+
sourceFile,
277+
{
278+
pos: prevNode.getEnd() - 2,
279+
end: reference.literal.getStart() + 1,
280+
},
281+
replacement.text.replace(/\\/g, "\\\\").replace(/`/g, "\\`"),
282+
);
283+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/pizza/*b*/ = "🍕";
4+
////export const prompt = `Hello, would you like some ${pizza}?`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/message/*b*/ = "Hello, World!";
4+
////await $`echo ${((message))}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: 'await $`echo ${(("Hello, World!"))}`;',
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/pizza/*b*/ = "🍕";
4+
////export const prompt = `Hello, would you like some ${((pizza))}?`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/codeText/*b*/ = "Code-formatted text looks `like this` and requires surrounding by backticks (\\`).";
4+
////export const mdTutorial = `Let's talk about markdown.\n${codeText}?`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const mdTutorial = `Let's talk about markdown.\\nCode-formatted text looks \\`like this\\` and requires surrounding by backticks (\\\\\\\`).?`;"
13+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/pizza/*b*/ = "🍕";
4+
////export const prompt = `Hello, would you like some ${
5+
//// pizza
6+
//// }?`;
7+
8+
goTo.select("a", "b");
9+
verify.refactorAvailable("Inline variable");
10+
edit.applyRefactor({
11+
refactorName: "Inline variable",
12+
actionName: "Inline variable",
13+
actionDescription: "Inline variable",
14+
newContent: "export const prompt = `Hello, would you like some 🍕?`;"
15+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/pizza/*b*/ = "🍕";
4+
////export const prompt = `Hello, would you like some ${pizza} or ${pizza}?`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const prompt = `Hello, would you like some 🍕 or 🍕?`;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/pizza/*b*/ = "🍕";
4+
////export const prompt = `Hello, would you like some ${pizza}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const prompt = `Hello, would you like some 🍕`;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/x/*b*/ = "\\`";
4+
////export const y = `${x}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const y = `\\\\\\``;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/x/*b*/ = "`";
4+
////export const y = `${x}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: "export const y = `\\``;"
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/message/*b*/ = "Hello, World!";
4+
////await $`echo ${message}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: 'await $`echo ${"Hello, World!"}`;',
13+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const /*a*/message/*b*/ = "Hello, World!";
4+
////await $`echo ${(message)}`;
5+
6+
goTo.select("a", "b");
7+
verify.refactorAvailable("Inline variable");
8+
edit.applyRefactor({
9+
refactorName: "Inline variable",
10+
actionName: "Inline variable",
11+
actionDescription: "Inline variable",
12+
newContent: 'await $`echo ${("Hello, World!")}`;',
13+
});

0 commit comments

Comments
 (0)