From 7d39b457f88d667c6eab8530d447cede4507846c Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 28 Mar 2018 13:57:43 -0700 Subject: [PATCH] Simplify createChildren (#22270) * Simplify createChildren * Move 'getSourceFile()' call after early returns --- src/services/services.ts | 165 +++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 93 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 590c65c4eae..64e64ea1bad 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -54,7 +54,7 @@ namespace ts { public jsDoc: JSDoc[]; public original: Node; public transformFlags: TransformFlags; - private _children: Node[]; + private _children: Node[] | undefined; constructor(kind: SyntaxKind, pos: number, end: number) { this.pos = pos; @@ -117,106 +117,17 @@ namespace ts { return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); } - private addSyntheticNodes(nodes: Push, pos: number, end: number): number { - scanner.setTextPos(pos); - while (pos < end) { - const token = scanner.scan(); - const textPos = scanner.getTextPos(); - if (textPos <= end) { - if (token === SyntaxKind.Identifier) { - Debug.fail(`Did not expect ${Debug.showSyntaxKind(this)} to have an Identifier in its trivia`); - } - nodes.push(createNode(token, pos, textPos, this)); - } - pos = textPos; - if (token === SyntaxKind.EndOfFileToken) { - break; - } - } - return pos; - } - - private createSyntaxList(nodes: NodeArray): Node { - const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, this); - list._children = []; - let pos = nodes.pos; - - for (const node of nodes) { - if (pos < node.pos) { - pos = this.addSyntheticNodes(list._children, pos, node.pos); - } - list._children.push(node); - pos = node.end; - } - if (pos < nodes.end) { - this.addSyntheticNodes(list._children, pos, nodes.end); - } - return list; - } - - private createChildren(sourceFile?: SourceFileLike) { - if (!isNodeKind(this.kind)) { - this._children = emptyArray; - return; - } - - if (isJSDocCommentContainingNode(this)) { - /** Don't add trivia for "tokens" since this is in a comment. */ - const children: Node[] = []; - this.forEachChild(child => { children.push(child); }); - this._children = children; - return; - } - - const children: Node[] = []; - scanner.setText((sourceFile || this.getSourceFile()).text); - let pos = this.pos; - const processNode = (node: Node) => { - pos = this.addSyntheticNodes(children, pos, node.pos); - children.push(node); - pos = node.end; - }; - const processNodes = (nodes: NodeArray) => { - if (pos < nodes.pos) { - pos = this.addSyntheticNodes(children, pos, nodes.pos); - } - children.push(this.createSyntaxList(nodes)); - pos = nodes.end; - }; - // jsDocComments need to be the first children - if (this.jsDoc) { - for (const jsDocComment of this.jsDoc) { - processNode(jsDocComment); - } - } - // For syntactic classifications, all trivia are classcified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. - pos = this.pos; - forEachChild(this, processNode, processNodes); - if (pos < this.end) { - this.addSyntheticNodes(children, pos, this.end); - } - scanner.setText(undefined); - this._children = children; - } - public getChildCount(sourceFile?: SourceFile): number { - this.assertHasRealPosition(); - if (!this._children) this.createChildren(sourceFile); - return this._children.length; + return this.getChildren(sourceFile).length; } public getChildAt(index: number, sourceFile?: SourceFile): Node { - this.assertHasRealPosition(); - if (!this._children) this.createChildren(sourceFile); - return this._children[index]; + return this.getChildren(sourceFile)[index]; } public getChildren(sourceFile?: SourceFileLike): Node[] { this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine"); - if (!this._children) this.createChildren(sourceFile); - return this._children; + return this._children || (this._children = createChildren(this, sourceFile)); } public getFirstToken(sourceFile?: SourceFile): Node { @@ -249,6 +160,74 @@ namespace ts { } } + function createChildren(node: Node, sourceFile: SourceFileLike | undefined): Node[] { + if (!isNodeKind(node.kind)) { + return emptyArray; + } + + const children: Node[] = []; + + if (isJSDocCommentContainingNode(node)) { + /** Don't add trivia for "tokens" since this is in a comment. */ + node.forEachChild(child => { children.push(child); }); + return children; + } + + scanner.setText((sourceFile || node.getSourceFile()).text); + let pos = node.pos; + const processNode = (child: Node) => { + addSyntheticNodes(children, pos, child.pos, node); + children.push(child); + pos = child.end; + }; + const processNodes = (nodes: NodeArray) => { + addSyntheticNodes(children, pos, nodes.pos, node); + children.push(createSyntaxList(nodes, node)); + pos = nodes.end; + }; + // jsDocComments need to be the first children + forEach((node as JSDocContainer).jsDoc, processNode); + // For syntactic classifications, all trivia are classified together, including jsdoc comments. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. + pos = node.pos; + node.forEachChild(processNode, processNodes); + addSyntheticNodes(children, pos, node.end, node); + scanner.setText(undefined); + return children; + } + + function addSyntheticNodes(nodes: Push, pos: number, end: number, parent: Node): void { + scanner.setTextPos(pos); + while (pos < end) { + const token = scanner.scan(); + const textPos = scanner.getTextPos(); + if (textPos <= end) { + if (token === SyntaxKind.Identifier) { + Debug.fail(`Did not expect ${Debug.showSyntaxKind(parent)} to have an Identifier in its trivia`); + } + nodes.push(createNode(token, pos, textPos, parent)); + } + pos = textPos; + if (token === SyntaxKind.EndOfFileToken) { + break; + } + } + } + + function createSyntaxList(nodes: NodeArray, parent: Node): Node { + const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList; + list._children = []; + let pos = nodes.pos; + for (const node of nodes) { + addSyntheticNodes(list._children, pos, node.pos, parent); + list._children.push(node); + pos = node.end; + } + addSyntheticNodes(list._children, pos, nodes.end, parent); + return list; + } + class TokenOrIdentifierObject implements Node { public kind: SyntaxKind; public pos: number;