diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 4238f96e02c27..2c09eb7d1d530 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -20,7 +20,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const maybeBinary = getParentBinaryExpression(node); const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] }; - if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) { + if (isBinaryExpression(maybeBinary) && treeToArray(maybeBinary).isValidConcatenation) { refactorInfo.actions.push(convertStringAction); return [refactorInfo]; } @@ -36,7 +36,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { function getNodeOrParentOfParentheses(file: SourceFile, startPosition: number) { const node = getTokenAtPosition(file, startPosition); const nestedBinary = getParentBinaryExpression(node); - const isNonStringBinary = !isStringConcatenationValid(nestedBinary); + const isNonStringBinary = !treeToArray(nestedBinary).isValidConcatenation; if ( isNonStringBinary && @@ -101,32 +101,31 @@ namespace ts.refactor.convertStringOrTemplateLiteral { } }); - return container || expr; + return (container || expr) as Expression; } - function isStringConcatenationValid(node: Node): boolean { - const { containsString, areOperatorsValid } = treeToArray(node); - return containsString && areOperatorsValid; - } - - function treeToArray(current: Node): { nodes: Expression[], operators: Token[], containsString: boolean, areOperatorsValid: boolean} { - if (isBinaryExpression(current)) { - const { nodes, operators, containsString: leftHasString, areOperatorsValid: leftOperatorValid } = treeToArray(current.left); + function treeToArray(current: Expression) { + const loop = (current: Node): { nodes: Expression[], operators: Token[], hasString: boolean, validOperators: boolean} => { + if (!isBinaryExpression(current)) { + return { nodes: [current as Expression], operators: [], validOperators: true, + hasString: isStringLiteral(current) || isNoSubstitutionTemplateLiteral(current) }; + } + const { nodes, operators, hasString: leftHasString, validOperators: leftOperatorValid } = loop(current.left); - if (!leftHasString && !isStringLiteral(current.right) && !isTemplateExpression(current.right)) { - return { nodes: [current], operators: [], containsString: false, areOperatorsValid: true }; + if (!(leftHasString || isStringLiteral(current.right) || isTemplateExpression(current.right))) { + return { nodes: [current], operators: [], hasString: false, validOperators: true }; } const currentOperatorValid = current.operatorToken.kind === SyntaxKind.PlusToken; - const areOperatorsValid = leftOperatorValid && currentOperatorValid; + const validOperators = leftOperatorValid && currentOperatorValid; nodes.push(current.right); operators.push(current.operatorToken); - return { nodes, operators, containsString: true, areOperatorsValid }; - } - - return { nodes: [current as Expression], operators: [], containsString: isStringLiteral(current), areOperatorsValid: true }; + return { nodes, operators, hasString: true, validOperators }; + }; + const { nodes, operators, validOperators, hasString } = loop(current); + return { nodes, operators, isValidConcatenation: validOperators && hasString }; } // to copy comments following the operator @@ -153,7 +152,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { let text = ""; while (index < nodes.length) { const node = nodes[index]; - if (isStringLiteralLike(node)) { + if (isStringLiteralLike(node)) { // includes isNoSubstitutionTemplateLiteral(node) text = text + node.text; indexes.push(index); index++; diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString11.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString11.ts new file mode 100644 index 0000000000000..2928a1ceaeaec --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString11.ts @@ -0,0 +1,11 @@ +/// + +////const a = /*x*/`x` + `y` + text + "z"/*y*/; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert to template string", + actionName: "Convert to template string", + actionDescription: ts.Diagnostics.Convert_to_template_string.message, + newContent: "const a = `xy${text}z`;" +}); diff --git a/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString12.ts b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString12.ts new file mode 100644 index 0000000000000..6d563655d4878 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertStringOrTemplateLiteral_TemplateString12.ts @@ -0,0 +1,11 @@ +/// + +////const a = /*x*/`x` + `y` + text + `z`/*y*/; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert to template string", + actionName: "Convert to template string", + actionDescription: ts.Diagnostics.Convert_to_template_string.message, + newContent: "const a = `xy${text}z`;" +});