Skip to content

Commit 45a05f3

Browse files
authored
Support converting string literals to templates outside of concatenation (#54647)
1 parent f3dad2a commit 45a05f3

5 files changed

+69
-4
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7564,7 +7564,7 @@
75647564
"category": "Message",
75657565
"code": 95153
75667566
},
7567-
"Can only convert string concatenation": {
7567+
"Can only convert string concatenations and string literals": {
75687568
"category": "Message",
75697569
"code": 95154
75707570
},

src/services/refactors/convertStringOrTemplateLiteral.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
getTokenAtPosition,
1616
getTrailingCommentRanges,
1717
isBinaryExpression,
18+
isExpressionNode,
1819
isNoSubstitutionTemplateLiteral,
1920
isParenthesizedExpression,
2021
isStringLiteral,
@@ -58,14 +59,19 @@ function getRefactorActionsToConvertToTemplateString(context: RefactorContext):
5859
const { file, startPosition } = context;
5960
const node = getNodeOrParentOfParentheses(file, startPosition);
6061
const maybeBinary = getParentBinaryExpression(node);
62+
const nodeIsStringLiteral = isStringLiteral(maybeBinary);
6163
const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] };
6264

63-
if (isBinaryExpression(maybeBinary) && treeToArray(maybeBinary).isValidConcatenation) {
65+
if (nodeIsStringLiteral && context.triggerReason !== "invoked") {
66+
return emptyArray;
67+
}
68+
69+
if (isExpressionNode(maybeBinary) && (nodeIsStringLiteral || isBinaryExpression(maybeBinary) && treeToArray(maybeBinary).isValidConcatenation)) {
6470
refactorInfo.actions.push(convertStringAction);
6571
return [refactorInfo];
6672
}
6773
else if (context.preferences.provideRefactorNotApplicableReason) {
68-
refactorInfo.actions.push({ ...convertStringAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) });
74+
refactorInfo.actions.push({ ...convertStringAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenations_and_string_literals) });
6975
return [refactorInfo];
7076
}
7177
return emptyArray;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// import * as a from "/*1a*/f/*1b*/oobar";
4+
//// export * as b from "/*2a*/f/*2b*/oobar";
5+
//// import * as c from "foobar" assert { "/*3a*/f/*3b*/oobar": "something" };
6+
//// import * as d from "foobar" assert { "something": "/*4a*/f/*4b*/oobar" };
7+
////
8+
//// let x = {
9+
//// "/*5a*/f/*5b*/oobar": 1234,
10+
//// };
11+
12+
verify.getSyntacticDiagnostics([]);
13+
14+
goTo.select("1a", "1b");
15+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
16+
verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
17+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
18+
19+
goTo.select("2a", "2b");
20+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
21+
verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
22+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
23+
24+
goTo.select("3a", "3b");
25+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
26+
verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
27+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
28+
29+
goTo.select("4a", "4b");
30+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
31+
verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
32+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
33+
34+
goTo.select("5a", "5b");
35+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
36+
verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
37+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);

tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSimple.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,15 @@
33
//// const foo = "/*x*/f/*y*/oobar rocks"
44

55
goTo.select("x", "y");
6-
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
6+
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
7+
verify.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
8+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
9+
10+
edit.applyRefactor({
11+
refactorName: "Convert to template string",
12+
actionName: "Convert to template string",
13+
actionDescription: ts.Diagnostics.Convert_to_template_string.message,
14+
triggerReason: "invoked",
15+
newContent:
16+
`const foo = \`foobar rocks\``,
17+
});

tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSingleQuote.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@
44

55
goTo.select("x", "y");
66
verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message);
7+
verify.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message);
8+
verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message);
9+
10+
edit.applyRefactor({
11+
refactorName: "Convert to template string",
12+
actionName: "Convert to template string",
13+
actionDescription: ts.Diagnostics.Convert_to_template_string.message,
14+
triggerReason: "invoked",
15+
newContent:
16+
`const foo = \`foobar rocks\``,
17+
});

0 commit comments

Comments
 (0)