Skip to content

Commit 4b95c1f

Browse files
committed
optimize treeToArray
1 parent dd89a49 commit 4b95c1f

File tree

1 file changed

+31
-22
lines changed

1 file changed

+31
-22
lines changed

src/services/refactors/convertStringOrTemplateLiteral.ts

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
7373
const lastComment = trailingCommentRanges[trailingCommentRanges.length - 1];
7474
const trailingRange = { pos: trailingCommentRanges[0].pos, end: lastComment.end };
7575

76+
// since suppressTrailingTrivia(maybeBinary) does not work, the trailing comment is removed manually
77+
// otherwise it would have the trailing comment twice
7678
return textChanges.ChangeTracker.with(context, t => {
7779
t.deleteRange(file, trailingRange);
7880
t.replaceNode(file, maybeBinary, templateLiteral);
@@ -81,7 +83,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
8183
else {
8284
return textChanges.ChangeTracker.with(context, t => t.replaceNode(file, maybeBinary, templateLiteral));
8385
}
84-
8586
}
8687

8788
const templateSpanToExpressions = (file: SourceFile) => (templateSpan: TemplateSpan): Expression[] => {
@@ -147,31 +148,36 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
147148
return containsString && areOperatorsValid;
148149
}
149150

150-
function treeToArray(node: Node): { nodes: ReadonlyArray<Expression>, operators: Token<BinaryOperator>[], containsString: boolean, areOperatorsValid: boolean} {
151-
if (isBinaryExpression(node)) {
152-
const { nodes: leftNodes, operators: leftOperator, containsString: leftHasString, areOperatorsValid: leftOperatorValid } = treeToArray(node.left);
153-
const { nodes: rightNodes, containsString: rightHasString, areOperatorsValid: rightOperatorValid } = treeToArray(node.right);
151+
function treeToArray(current: Node): { nodes: Expression[], operators: Token<BinaryOperator>[], containsString: boolean, areOperatorsValid: boolean} {
152+
if (isBinaryExpression(current)) {
153+
const { nodes, operators, containsString: leftHasString, areOperatorsValid: leftOperatorValid } = treeToArray(current.left);
154154

155-
if (!leftHasString && !rightHasString) {
156-
return { nodes: [node], operators: [], containsString: false, areOperatorsValid: true };
155+
if (!leftHasString && !isStringLiteral(current.right)) {
156+
return { nodes: [current], operators: [], containsString: false, areOperatorsValid: true };
157157
}
158158

159-
const currentOperatorValid = node.operatorToken.kind === SyntaxKind.PlusToken;
160-
const areOperatorsValid = leftOperatorValid && currentOperatorValid && rightOperatorValid;
161-
leftOperator.push(node.operatorToken);
159+
const currentOperatorValid = current.operatorToken.kind === SyntaxKind.PlusToken;
160+
const areOperatorsValid = leftOperatorValid && currentOperatorValid;
161+
162+
nodes.push(current.right);
163+
operators.push(current.operatorToken);
162164

163-
return { nodes: leftNodes.concat(rightNodes), operators: leftOperator, containsString: true, areOperatorsValid };
165+
return { nodes, operators, containsString: true, areOperatorsValid };
164166
}
165167

166-
return { nodes: [node as Expression], operators: [], containsString: isStringLiteral(node), areOperatorsValid: true };
168+
return { nodes: [current as Expression], operators: [], containsString: isStringLiteral(current), areOperatorsValid: true };
167169
}
168170

171+
// to copy comments following the operator
172+
// "foo" + /* comment */ "bar"
169173
const copyTrailingOperatorComments = (operators: Token<BinaryOperator>[], file: SourceFile) => (index: number, targetNode: Node) => {
170174
if (index < operators.length) {
171175
copyTrailingComments(operators[index], targetNode, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
172176
}
173177
};
174178

179+
// to copy comments following the string
180+
// "foo" /* comment */ + "bar" /* comment */ + "bar2"
175181
const copyCommentFromMultiNode = (nodes: ReadonlyArray<Expression>, file: SourceFile, copyOperatorComments: (index: number, targetNode: Node) => void) =>
176182
(indexes: number[], targetNode: Node) => {
177183
while (indexes.length > 0) {
@@ -198,8 +204,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
198204
function nodesToTemplate({nodes, operators}: {nodes: ReadonlyArray<Expression>, operators: Token<BinaryOperator>[]}, file: SourceFile) {
199205
const copyOperatorComments = copyTrailingOperatorComments(operators, file);
200206
const copyCommentFromStringLiterals = copyCommentFromMultiNode(nodes, file, copyOperatorComments);
201-
202-
const templateSpans: TemplateSpan[] = [];
203207
const [begin, headText, headIndexes] = concatConsecutiveString(0, nodes);
204208

205209
if (begin === nodes.length) {
@@ -208,36 +212,41 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
208212
return noSubstitutionTemplateLiteral;
209213
}
210214

215+
const templateSpans: TemplateSpan[] = [];
211216
const templateHead = createTemplateHead(headText);
212217
copyCommentFromStringLiterals(headIndexes, templateHead);
213218

214219
for (let i = begin; i < nodes.length; i++) {
215-
let currentNode = nodes[i];
216-
217-
if (isParenthesizedExpression(currentNode)) {
218-
copyCommentsWhenParenthesized(currentNode);
219-
currentNode = currentNode.expression;
220-
}
221-
220+
const currentNode = getExpressionFromParenthesesOrExpression(nodes[i]);
222221
copyOperatorComments(i, currentNode);
222+
223223
const [newIndex, subsequentText, stringIndexes] = concatConsecutiveString(i + 1, nodes);
224224
i = newIndex - 1;
225225

226226
const templatePart = i === nodes.length - 1 ? createTemplateTail(subsequentText) : createTemplateMiddle(subsequentText);
227227
copyCommentFromStringLiterals(stringIndexes, templatePart);
228-
229228
templateSpans.push(createTemplateSpan(currentNode, templatePart));
230229
}
231230

232231
return createTemplateExpression(templateHead, templateSpans);
233232
}
234233

234+
// to copy comments following the opening & closing parentheses
235+
// "foo" + ( /* comment */ 5 + 5 ) /* comment */ + "bar"
235236
function copyCommentsWhenParenthesized(node: ParenthesizedExpression) {
236237
const file = node.getSourceFile();
237238
copyTrailingComments(node, node.expression, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
238239
copyTrailingAsLeadingComments(node.expression, node.expression, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
239240
}
240241

242+
function getExpressionFromParenthesesOrExpression(node: Expression) {
243+
if (isParenthesizedExpression(node)) {
244+
copyCommentsWhenParenthesized(node);
245+
node = node.expression;
246+
}
247+
return node;
248+
}
249+
241250
const hexToUnicode = (_match: string, grp: string) => String.fromCharCode(parseInt(grp, 16));
242251
const octalToUnicode = (_match: string, grp: string) => String.fromCharCode(parseInt(grp, 8));
243252

0 commit comments

Comments
 (0)