diff --git a/src/services/refactors/inlineVariable.ts b/src/services/refactors/inlineVariable.ts
index 0bcf841ce39e2..d18b94f04472b 100644
--- a/src/services/refactors/inlineVariable.ts
+++ b/src/services/refactors/inlineVariable.ts
@@ -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, "\\`"),
+ );
+}
diff --git a/tests/cases/fourslash/inlineVariableTemplateString1.ts b/tests/cases/fourslash/inlineVariableTemplateString1.ts
new file mode 100644
index 0000000000000..48df225455a55
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString1.ts
@@ -0,0 +1,13 @@
+///
+
+////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 🍕?`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString10.ts b/tests/cases/fourslash/inlineVariableTemplateString10.ts
new file mode 100644
index 0000000000000..b6409d3133bac
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString10.ts
@@ -0,0 +1,13 @@
+///
+
+////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!"))}`;',
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString11.ts b/tests/cases/fourslash/inlineVariableTemplateString11.ts
new file mode 100644
index 0000000000000..d626b3e4cf5f4
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString11.ts
@@ -0,0 +1,13 @@
+///
+
+////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 🍕?`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString2.ts b/tests/cases/fourslash/inlineVariableTemplateString2.ts
new file mode 100644
index 0000000000000..0984278c1357c
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString2.ts
@@ -0,0 +1,13 @@
+///
+
+////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 (\\\\\\\`).?`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString3.ts b/tests/cases/fourslash/inlineVariableTemplateString3.ts
new file mode 100644
index 0000000000000..9851f87961774
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString3.ts
@@ -0,0 +1,15 @@
+///
+
+////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 🍕?`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString4.ts b/tests/cases/fourslash/inlineVariableTemplateString4.ts
new file mode 100644
index 0000000000000..9a0dc1805f5a1
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString4.ts
@@ -0,0 +1,13 @@
+///
+
+////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 🍕?`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString5.ts b/tests/cases/fourslash/inlineVariableTemplateString5.ts
new file mode 100644
index 0000000000000..fd1f1c510b605
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString5.ts
@@ -0,0 +1,13 @@
+///
+
+////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 🍕`;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString6.ts b/tests/cases/fourslash/inlineVariableTemplateString6.ts
new file mode 100644
index 0000000000000..9d04b8f4b292f
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString6.ts
@@ -0,0 +1,13 @@
+///
+
+////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 = `\\\\\\``;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString7.ts b/tests/cases/fourslash/inlineVariableTemplateString7.ts
new file mode 100644
index 0000000000000..c0315aa54b9e7
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString7.ts
@@ -0,0 +1,13 @@
+///
+
+////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 = `\\``;"
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString8.ts b/tests/cases/fourslash/inlineVariableTemplateString8.ts
new file mode 100644
index 0000000000000..82e0abb8562e4
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString8.ts
@@ -0,0 +1,13 @@
+///
+
+////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!"}`;',
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlineVariableTemplateString9.ts b/tests/cases/fourslash/inlineVariableTemplateString9.ts
new file mode 100644
index 0000000000000..fc6ea1847964c
--- /dev/null
+++ b/tests/cases/fourslash/inlineVariableTemplateString9.ts
@@ -0,0 +1,13 @@
+///
+
+////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!")}`;',
+});
\ No newline at end of file