mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
fix forEachChildRecursively (#42300)
This commit is contained in:
+38
-45
@@ -558,58 +558,51 @@ namespace ts {
|
||||
* and while doing so, handles traversing the structure without relying on the callstack to encode the tree structure.
|
||||
*/
|
||||
export function forEachChildRecursively<T>(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray<Node>, parent: Node) => T | "skip" | undefined): T | undefined {
|
||||
|
||||
const stack: Node[] = [rootNode];
|
||||
while (stack.length) {
|
||||
const parent = stack.pop()!;
|
||||
const res = visitAllPossibleChildren(parent, gatherPossibleChildren(parent));
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
const queue: (Node | NodeArray<Node>)[] = gatherPossibleChildren(rootNode);
|
||||
const parents: Node[] = []; // tracks parent references for elements in queue
|
||||
while (parents.length < queue.length) {
|
||||
parents.push(rootNode);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function gatherPossibleChildren(node: Node) {
|
||||
const children: (Node | NodeArray<Node>)[] = [];
|
||||
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
|
||||
return children;
|
||||
|
||||
function addWorkItem(n: Node | NodeArray<Node>) {
|
||||
children.unshift(n);
|
||||
}
|
||||
}
|
||||
|
||||
function visitAllPossibleChildren(parent: Node, children: readonly (Node | NodeArray<Node>)[]) {
|
||||
for (const child of children) {
|
||||
if (isArray(child)) {
|
||||
if (cbNodes) {
|
||||
const res = cbNodes(child, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = child.length - 1; i >= 0; i--) {
|
||||
const realChild = child[i];
|
||||
const res = cbNode(realChild, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
stack.push(realChild);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stack.push(child);
|
||||
const res = cbNode(child, parent);
|
||||
while (queue.length !== 0) {
|
||||
const current = queue.pop()!;
|
||||
const parent = parents.pop()!;
|
||||
if (isArray(current)) {
|
||||
if (cbNodes) {
|
||||
const res = cbNodes(current, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
for (let i = current.length - 1; i >= 0; --i) {
|
||||
queue.push(current[i]);
|
||||
parents.push(parent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const res = cbNode(current, parent);
|
||||
if (res) {
|
||||
if (res === "skip") continue;
|
||||
return res;
|
||||
}
|
||||
if (current.kind >= SyntaxKind.FirstNode) {
|
||||
// add children in reverse order to the queue, so popping gives the first child
|
||||
for (const child of gatherPossibleChildren(current)) {
|
||||
queue.push(child);
|
||||
parents.push(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gatherPossibleChildren(node: Node) {
|
||||
const children: (Node | NodeArray<Node>)[] = [];
|
||||
forEachChild(node, addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal
|
||||
return children;
|
||||
|
||||
function addWorkItem(n: Node | NodeArray<Node>) {
|
||||
children.unshift(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user