Skip to content

Commit 7d39b45

Browse files
author
Andy
authored
Simplify createChildren (microsoft#22270)
* Simplify createChildren * Move 'getSourceFile()' call after early returns
1 parent 85a7118 commit 7d39b45

File tree

1 file changed

+72
-93
lines changed

1 file changed

+72
-93
lines changed

src/services/services.ts

Lines changed: 72 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace ts {
5454
public jsDoc: JSDoc[];
5555
public original: Node;
5656
public transformFlags: TransformFlags;
57-
private _children: Node[];
57+
private _children: Node[] | undefined;
5858

5959
constructor(kind: SyntaxKind, pos: number, end: number) {
6060
this.pos = pos;
@@ -117,106 +117,17 @@ namespace ts {
117117
return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd());
118118
}
119119

120-
private addSyntheticNodes(nodes: Push<Node>, pos: number, end: number): number {
121-
scanner.setTextPos(pos);
122-
while (pos < end) {
123-
const token = scanner.scan();
124-
const textPos = scanner.getTextPos();
125-
if (textPos <= end) {
126-
if (token === SyntaxKind.Identifier) {
127-
Debug.fail(`Did not expect ${Debug.showSyntaxKind(this)} to have an Identifier in its trivia`);
128-
}
129-
nodes.push(createNode(token, pos, textPos, this));
130-
}
131-
pos = textPos;
132-
if (token === SyntaxKind.EndOfFileToken) {
133-
break;
134-
}
135-
}
136-
return pos;
137-
}
138-
139-
private createSyntaxList(nodes: NodeArray<Node>): Node {
140-
const list = <NodeObject>createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, this);
141-
list._children = [];
142-
let pos = nodes.pos;
143-
144-
for (const node of nodes) {
145-
if (pos < node.pos) {
146-
pos = this.addSyntheticNodes(list._children, pos, node.pos);
147-
}
148-
list._children.push(node);
149-
pos = node.end;
150-
}
151-
if (pos < nodes.end) {
152-
this.addSyntheticNodes(list._children, pos, nodes.end);
153-
}
154-
return list;
155-
}
156-
157-
private createChildren(sourceFile?: SourceFileLike) {
158-
if (!isNodeKind(this.kind)) {
159-
this._children = emptyArray;
160-
return;
161-
}
162-
163-
if (isJSDocCommentContainingNode(this)) {
164-
/** Don't add trivia for "tokens" since this is in a comment. */
165-
const children: Node[] = [];
166-
this.forEachChild(child => { children.push(child); });
167-
this._children = children;
168-
return;
169-
}
170-
171-
const children: Node[] = [];
172-
scanner.setText((sourceFile || this.getSourceFile()).text);
173-
let pos = this.pos;
174-
const processNode = (node: Node) => {
175-
pos = this.addSyntheticNodes(children, pos, node.pos);
176-
children.push(node);
177-
pos = node.end;
178-
};
179-
const processNodes = (nodes: NodeArray<Node>) => {
180-
if (pos < nodes.pos) {
181-
pos = this.addSyntheticNodes(children, pos, nodes.pos);
182-
}
183-
children.push(this.createSyntaxList(nodes));
184-
pos = nodes.end;
185-
};
186-
// jsDocComments need to be the first children
187-
if (this.jsDoc) {
188-
for (const jsDocComment of this.jsDoc) {
189-
processNode(jsDocComment);
190-
}
191-
}
192-
// For syntactic classifications, all trivia are classcified together, including jsdoc comments.
193-
// For that to work, the jsdoc comments should still be the leading trivia of the first child.
194-
// Restoring the scanner position ensures that.
195-
pos = this.pos;
196-
forEachChild(this, processNode, processNodes);
197-
if (pos < this.end) {
198-
this.addSyntheticNodes(children, pos, this.end);
199-
}
200-
scanner.setText(undefined);
201-
this._children = children;
202-
}
203-
204120
public getChildCount(sourceFile?: SourceFile): number {
205-
this.assertHasRealPosition();
206-
if (!this._children) this.createChildren(sourceFile);
207-
return this._children.length;
121+
return this.getChildren(sourceFile).length;
208122
}
209123

210124
public getChildAt(index: number, sourceFile?: SourceFile): Node {
211-
this.assertHasRealPosition();
212-
if (!this._children) this.createChildren(sourceFile);
213-
return this._children[index];
125+
return this.getChildren(sourceFile)[index];
214126
}
215127

216128
public getChildren(sourceFile?: SourceFileLike): Node[] {
217129
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");
218-
if (!this._children) this.createChildren(sourceFile);
219-
return this._children;
130+
return this._children || (this._children = createChildren(this, sourceFile));
220131
}
221132

222133
public getFirstToken(sourceFile?: SourceFile): Node {
@@ -249,6 +160,74 @@ namespace ts {
249160
}
250161
}
251162

163+
function createChildren(node: Node, sourceFile: SourceFileLike | undefined): Node[] {
164+
if (!isNodeKind(node.kind)) {
165+
return emptyArray;
166+
}
167+
168+
const children: Node[] = [];
169+
170+
if (isJSDocCommentContainingNode(node)) {
171+
/** Don't add trivia for "tokens" since this is in a comment. */
172+
node.forEachChild(child => { children.push(child); });
173+
return children;
174+
}
175+
176+
scanner.setText((sourceFile || node.getSourceFile()).text);
177+
let pos = node.pos;
178+
const processNode = (child: Node) => {
179+
addSyntheticNodes(children, pos, child.pos, node);
180+
children.push(child);
181+
pos = child.end;
182+
};
183+
const processNodes = (nodes: NodeArray<Node>) => {
184+
addSyntheticNodes(children, pos, nodes.pos, node);
185+
children.push(createSyntaxList(nodes, node));
186+
pos = nodes.end;
187+
};
188+
// jsDocComments need to be the first children
189+
forEach((node as JSDocContainer).jsDoc, processNode);
190+
// For syntactic classifications, all trivia are classified together, including jsdoc comments.
191+
// For that to work, the jsdoc comments should still be the leading trivia of the first child.
192+
// Restoring the scanner position ensures that.
193+
pos = node.pos;
194+
node.forEachChild(processNode, processNodes);
195+
addSyntheticNodes(children, pos, node.end, node);
196+
scanner.setText(undefined);
197+
return children;
198+
}
199+
200+
function addSyntheticNodes(nodes: Push<Node>, pos: number, end: number, parent: Node): void {
201+
scanner.setTextPos(pos);
202+
while (pos < end) {
203+
const token = scanner.scan();
204+
const textPos = scanner.getTextPos();
205+
if (textPos <= end) {
206+
if (token === SyntaxKind.Identifier) {
207+
Debug.fail(`Did not expect ${Debug.showSyntaxKind(parent)} to have an Identifier in its trivia`);
208+
}
209+
nodes.push(createNode(token, pos, textPos, parent));
210+
}
211+
pos = textPos;
212+
if (token === SyntaxKind.EndOfFileToken) {
213+
break;
214+
}
215+
}
216+
}
217+
218+
function createSyntaxList(nodes: NodeArray<Node>, parent: Node): Node {
219+
const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList;
220+
list._children = [];
221+
let pos = nodes.pos;
222+
for (const node of nodes) {
223+
addSyntheticNodes(list._children, pos, node.pos, parent);
224+
list._children.push(node);
225+
pos = node.end;
226+
}
227+
addSyntheticNodes(list._children, pos, nodes.end, parent);
228+
return list;
229+
}
230+
252231
class TokenOrIdentifierObject implements Node {
253232
public kind: SyntaxKind;
254233
public pos: number;

0 commit comments

Comments
 (0)