diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 6d9f7e46b0b..a35f921c867 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -8,8 +8,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const toTemplateLiteralDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_literal); const toStringConcatenationDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_string_concatenation); - // TODO let a = 45 - 45 + " ee" - 33; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ReadonlyArray { @@ -19,7 +17,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const maybeTemplateExpression = findAncestor(node, n => isTemplateExpression(n)); const actions: RefactorActionInfo[] = []; - if ((isBinaryExpression(maybeBinary) || isStringLiteral(maybeBinary)) && containsString(maybeBinary)) { + if ((isBinaryExpression(maybeBinary) || isStringLiteral(maybeBinary)) && isStringConcatenationValid(maybeBinary)) { actions.push({ name: toTemplateLiteralActionName, description: toTemplateLiteralDescription }); } @@ -39,7 +37,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const maybeBinary = getParentBinaryExpression(node); const arrayOfNodes = treeToArray(maybeBinary)[0]; const templateLiteral = nodesToTemplate(arrayOfNodes); - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, maybeBinary, templateLiteral)); return { edits }; @@ -97,7 +94,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const left = nodes[0]; const right = nodes[1]; - const binary = createBinary (left, SyntaxKind.PlusToken, right); + const binary = createBinary(left, SyntaxKind.PlusToken, right); return arrayToTree(nodes.slice(2), binary); } @@ -106,16 +103,25 @@ namespace ts.refactor.convertStringOrTemplateLiteral { return arrayToTree(nodes.slice(1), binary); } - function treeToArray(node: Node): [Node[], boolean] { - if (isBinaryExpression(node)) { - const [leftNodes, leftHasString] = treeToArray(node.left); - const [rightNodes, rightHasString] = treeToArray(node.right); + function isStringConcatenationValid(node: Node): boolean { + const [, containsString, areOperatorsValid] = treeToArray(node); + return containsString && areOperatorsValid; + } - if (!leftHasString && !rightHasString) return [[node], false]; - return [leftNodes.concat(rightNodes), true]; + function treeToArray(node: Node): [Node[], /* containsString */ boolean, /* areOperatorsValid */ boolean] { + if (isBinaryExpression(node)) { + const [leftNodes, leftHasString, leftOp] = treeToArray(node.left); + const [rightNodes, rightHasString, rightOp] = treeToArray(node.right); + + if (!leftHasString && !rightHasString) return [[node], false, true]; + + const currentOp = node.operatorToken.kind === SyntaxKind.PlusToken; + const isPlus = leftOp && currentOp && rightOp; + + return [leftNodes.concat(rightNodes), true, isPlus]; } - return [[node], isStringLiteral(node)]; + return [[node], isStringLiteral(node), true]; } function nodesToTemplate(nodes: Node[]) { diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToStringTaggedAvailability.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToStringAvailabilityTagged.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToStringTaggedAvailability.ts rename to tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToStringAvailabilityTagged.ts diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityMinus.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityMinus.ts new file mode 100644 index 00000000000..50c7921836c --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityMinus.ts @@ -0,0 +1,25 @@ +/// + +//// const age = 22 +//// const name = "Eddy" +//// const /*z*/f/*y*/oo = /*x*/"/*w*/M/*v*/r/*u*/ " /*t*/+/*s*/ name + " is " - /*r*/a/*q*/ge - " years old" + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("r", "q"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityNoStrings.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityNoStrings.ts new file mode 100644 index 00000000000..f015af3b3c7 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityNoStrings.ts @@ -0,0 +1,19 @@ +/// + +//// const /*z*/f/*y*/oo = /*x*/4/*w*/2 /*v*/-/*u*/ 56 + /*t*/2/*s*/2 * 4 / 33 + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityPrecedingMinus.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityPrecedingMinus.ts new file mode 100644 index 00000000000..04f24dffc8f --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_ToTemplateAvailabilityPrecedingMinus.ts @@ -0,0 +1,24 @@ +/// + +//// const age = 22 +//// const /*z*/f/*y*/oo = /*x*/a/*w*/ge * 4 /*v*/-/*u*/ 2 / 4 /*t*/+/*s*/ " /*r*/y/*q*/ears old" + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal"); + +goTo.select("r", "q"); +verify.not.refactorAvailable("Convert string concatenation or template literal", "Convert to string concatenation"); +verify.refactorAvailable("Convert string concatenation or template literal", "Convert to template literal");