/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export const strings = (() => { function format(value: string, ...rest: unknown[]): string { return value.replace(/(\{\d+\})/g, function (match) { const index = Number(match.substring(1, match.length - 1)); return String(rest[index]) || match; }); } return { format }; })(); export const graph = (() => { class Node { readonly incoming = new Map>(); readonly outgoing = new Map>(); readonly data: T; constructor(data: T) { this.data = data; } } class Graph { private _nodes = new Map>(); inertEdge(from: T, to: T): void { const fromNode = this.lookupOrInsertNode(from); const toNode = this.lookupOrInsertNode(to); fromNode.outgoing.set(toNode.data, toNode); toNode.incoming.set(fromNode.data, fromNode); } 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(); } lookupOrInsertNode(data: T): Node { let node = this._nodes.get(data); if (!node) { node = new Node(data); this._nodes.set(data, node); } return node; } lookup(data: T): Node | null { return this._nodes.get(data) ?? null; } findCycles(allData: T[]): Map { const result = new Map(); const checked = new Set(); for (const data of allData) { const node = this.lookup(data); if (!node) { continue; } const r = this._findCycle(node, checked, new Set()); result.set(node.data, r); } return result; } private _findCycle(node: Node, checked: Set, seen: Set): T[] | undefined { if (checked.has(node.data)) { return undefined; } let result: T[] | undefined; for (const child of node.outgoing.values()) { if (seen.has(child.data)) { const seenArr = Array.from(seen); const idx = seenArr.indexOf(child.data); seenArr.push(child.data); return idx > 0 ? seenArr.slice(idx) : seenArr; } seen.add(child.data); result = this._findCycle(child, checked, seen); seen.delete(child.data); if (result) { break; } } checked.add(node.data); return result; } } return { Node, Graph }; })();