void/build/lib/tsb/utils.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

118 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

2024-09-11 02:37:36 +00:00
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
2025-03-01 02:01:53 +00:00
export namespace strings {
2024-09-11 02:37:36 +00:00
export function format(value: string, ...rest: any[]): string {
return value.replace(/({\d+})/g, function (match) {
const index = Number(match.substring(1, match.length - 1));
return String(rest[index]) || match;
});
}
}
2025-03-01 02:01:53 +00:00
export namespace graph {
2024-09-11 02:37:36 +00:00
2025-03-01 02:01:53 +00:00
export class Node<T> {
2024-09-11 02:37:36 +00:00
2025-03-01 02:01:53 +00:00
readonly incoming = new Map<T, Node<T>>();
readonly outgoing = new Map<T, Node<T>>();
2024-09-11 02:37:36 +00:00
2025-03-01 02:01:53 +00:00
constructor(readonly data: T) {
2024-09-11 02:37:36 +00:00
}
2025-03-01 02:01:53 +00:00
}
2024-09-11 02:37:36 +00:00
2025-03-01 02:01:53 +00:00
export class Graph<T> {
2024-09-11 02:37:36 +00:00
2025-03-01 02:01:53 +00:00
private _nodes = new Map<T, Node<T>>();
2024-09-11 02:37:36 +00:00
inertEdge(from: T, to: T): void {
const fromNode = this.lookupOrInsertNode(from);
const toNode = this.lookupOrInsertNode(to);
2025-03-01 02:01:53 +00:00
fromNode.outgoing.set(toNode.data, toNode);
toNode.incoming.set(fromNode.data, fromNode);
2024-09-11 02:37:36 +00:00
}
2025-03-01 02:01:53 +00:00
resetNode(data: T): void {
const node = this._nodes.get(data);
if (!node) {
return;
}
for (const outDep of node.outgoing.values()) {
outDep.incoming.delete(node.data);
}
node.outgoing.clear();
2024-09-11 02:37:36 +00:00
}
lookupOrInsertNode(data: T): Node<T> {
2025-03-01 02:01:53 +00:00
let node = this._nodes.get(data);
2024-09-11 02:37:36 +00:00
if (!node) {
2025-03-01 02:01:53 +00:00
node = new Node(data);
this._nodes.set(data, node);
2024-09-11 02:37:36 +00:00
}
return node;
}
lookup(data: T): Node<T> | null {
2025-03-01 02:01:53 +00:00
return this._nodes.get(data) ?? null;
}
findCycle(): T[] | undefined {
let result: T[] | undefined;
let foundStartNodes = false;
const checked = new Set<Node<T>>();
for (const [_start, value] of this._nodes) {
if (Object.values(value.incoming).length > 0) {
continue;
}
foundStartNodes = true;
const dfs = (node: Node<T>, visited: Set<Node<T>>) => {
if (checked.has(node)) {
return;
}
if (visited.has(node)) {
result = [...visited, node].map(n => n.data);
const idx = result.indexOf(node.data);
result = result.slice(idx);
return;
}
visited.add(node);
for (const child of Object.values(node.outgoing)) {
dfs(child, visited);
if (result) {
break;
}
}
visited.delete(node);
checked.add(node);
};
dfs(value, new Set());
if (result) {
break;
}
}
if (!foundStartNodes) {
// everything is a cycle
return Array.from(this._nodes.keys());
}
return result;
2024-09-11 02:37:36 +00:00
}
}
}