diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c3c8a58d8f5..f0f102bd129 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7564,7 +7564,7 @@ "category": "Message", "code": 95153 }, - "Can only convert string concatenation": { + "Can only convert string concatenations and string literals": { "category": "Message", "code": 95154 }, diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index a13e4f6f6eb..a43121260aa 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -15,6 +15,7 @@ import { getTokenAtPosition, getTrailingCommentRanges, isBinaryExpression, + isExpressionNode, isNoSubstitutionTemplateLiteral, isParenthesizedExpression, isStringLiteral, @@ -58,14 +59,19 @@ function getRefactorActionsToConvertToTemplateString(context: RefactorContext): const { file, startPosition } = context; const node = getNodeOrParentOfParentheses(file, startPosition); const maybeBinary = getParentBinaryExpression(node); + const nodeIsStringLiteral = isStringLiteral(maybeBinary); const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] }; - if (isBinaryExpression(maybeBinary) && treeToArray(maybeBinary).isValidConcatenation) { + if (nodeIsStringLiteral && context.triggerReason !== "invoked") { + return emptyArray; + } + + if (isExpressionNode(maybeBinary) && (nodeIsStringLiteral || isBinaryExpression(maybeBinary) && treeToArray(maybeBinary).isValidConcatenation)) { refactorInfo.actions.push(convertStringAction); return [refactorInfo]; } else if (context.preferences.provideRefactorNotApplicableReason) { - refactorInfo.actions.push({ ...convertStringAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) }); + refactorInfo.actions.push({ ...convertStringAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenations_and_string_literals) }); return [refactorInfo]; } return emptyArray; diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateInvalidLocation.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateInvalidLocation.ts new file mode 100644 index 00000000000..9abc6840992 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateInvalidLocation.ts @@ -0,0 +1,37 @@ +/// + +//// import * as a from "/*1a*/f/*1b*/oobar"; +//// export * as b from "/*2a*/f/*2b*/oobar"; +//// import * as c from "foobar" assert { "/*3a*/f/*3b*/oobar": "something" }; +//// import * as d from "foobar" assert { "something": "/*4a*/f/*4b*/oobar" }; +//// +//// let x = { +//// "/*5a*/f/*5b*/oobar": 1234, +//// }; + +verify.getSyntacticDiagnostics([]); + +goTo.select("1a", "1b"); +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +goTo.select("2a", "2b"); +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +goTo.select("3a", "3b"); +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +goTo.select("4a", "4b"); +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +goTo.select("5a", "5b"); +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSimple.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSimple.ts index 86b1300ad01..f9d327b3ac9 100644 --- a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSimple.ts +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSimple.ts @@ -3,4 +3,15 @@ //// const foo = "/*x*/f/*y*/oobar rocks" goTo.select("x", "y"); -verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); \ No newline at end of file +verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +edit.applyRefactor({ + refactorName: "Convert to template string", + actionName: "Convert to template string", + actionDescription: ts.Diagnostics.Convert_to_template_string.message, + triggerReason: "invoked", + newContent: + `const foo = \`foobar rocks\``, +}); diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSingleQuote.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSingleQuote.ts index 8883cbb3ae3..a3606a0ca64 100644 --- a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSingleQuote.ts +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateSingleQuote.ts @@ -4,3 +4,14 @@ goTo.select("x", "y"); verify.not.refactorAvailable(ts.Diagnostics.Convert_to_template_string.message); +verify.refactorAvailableForTriggerReason("invoked", ts.Diagnostics.Convert_to_template_string.message); +verify.not.refactorAvailableForTriggerReason("implicit", ts.Diagnostics.Convert_to_template_string.message); + +edit.applyRefactor({ + refactorName: "Convert to template string", + actionName: "Convert to template string", + actionDescription: ts.Diagnostics.Convert_to_template_string.message, + triggerReason: "invoked", + newContent: + `const foo = \`foobar rocks\``, +});