import { stream as e } from "../utils/stream.js";
import { cancellation_exports as t } from "../utils/cancellation.js";
import { OperationCancelled as n, interruptAndCheck as r, isOperationCancelled as i } from "../utils/promise-utils.js";
import { UriUtils as a } from "../utils/uri-utils.js";
import { DocumentState as o } from "./documents.js";
import { MultiMap as s } from "../utils/collections.js";
import { require_main as c } from "../../../vscode-languageserver-protocol/lib/browser/main.js";
import { Disposable as l } from "../utils/disposable.js";
//#region ../../node_modules/langium/lib/workspace/document-builder.js
var u = c(), d = class {
	constructor(e) {
		this.updateBuildOptions = { validation: { categories: ["built-in", "fast"] } }, this.updateListeners = [], this.buildPhaseListeners = new s(), this.documentPhaseListeners = new s(), this.buildState = /* @__PURE__ */ new Map(), this.documentBuildWaiters = /* @__PURE__ */ new Map(), this.currentState = o.Changed, this.langiumDocuments = e.workspace.LangiumDocuments, this.langiumDocumentFactory = e.workspace.LangiumDocumentFactory, this.textDocuments = e.workspace.TextDocuments, this.indexManager = e.workspace.IndexManager, this.fileSystemProvider = e.workspace.FileSystemProvider, this.workspaceManager = () => e.workspace.WorkspaceManager, this.serviceRegistry = e.ServiceRegistry;
	}
	async build(e, n = {}, r = t.CancellationToken.None) {
		for (let t of e) {
			let e = t.uri.toString();
			if (t.state === o.Validated) {
				if (typeof n.validation == "boolean" && n.validation) this.resetToState(t, o.IndexedReferences);
				else if (typeof n.validation == "object") {
					let r = this.findMissingValidationCategories(t, n);
					r.length > 0 && (this.buildState.set(e, {
						completed: !1,
						options: { validation: { categories: r } },
						result: this.buildState.get(e)?.result
					}), t.state = o.IndexedReferences);
				}
			} else this.buildState.delete(e);
		}
		this.currentState = o.Changed, await this.emitUpdate(e.map((e) => e.uri), []), await this.buildDocuments(e, n, r);
	}
	async update(n, i, a = t.CancellationToken.None) {
		this.currentState = o.Changed;
		let s = [];
		for (let e of i) {
			let t = this.langiumDocuments.deleteDocuments(e);
			for (let e of t) s.push(e.uri), this.cleanUpDeleted(e);
		}
		let c = (await Promise.all(n.map((e) => this.findChangedUris(e)))).flat();
		for (let e of c) {
			let t = this.langiumDocuments.getDocument(e);
			t === void 0 && (t = this.langiumDocumentFactory.fromModel({ $type: "INVALID" }, e), t.state = o.Changed, this.langiumDocuments.addDocument(t)), this.resetToState(t, o.Changed);
		}
		let l = e(c).concat(s).map((e) => e.toString()).toSet();
		this.langiumDocuments.all.filter((e) => !l.has(e.uri.toString()) && this.shouldRelink(e, l)).forEach((e) => this.resetToState(e, o.ComputedScopes)), await this.emitUpdate(c, s), await r(a);
		let u = this.sortDocuments(this.langiumDocuments.all.filter((e) => e.state < o.Validated || !this.buildState.get(e.uri.toString())?.completed || this.resultsAreIncomplete(e, this.updateBuildOptions)).toArray());
		await this.buildDocuments(u, this.updateBuildOptions, a);
	}
	resultsAreIncomplete(e, t) {
		return this.findMissingValidationCategories(e, t).length >= 1;
	}
	findMissingValidationCategories(t, n) {
		let r = this.buildState.get(t.uri.toString()), i = this.serviceRegistry.getServices(t.uri).validation.ValidationRegistry.getAllValidationCategories(t), a = r?.result?.validationChecks ? new Set(r?.result?.validationChecks) : r?.completed ? i : /* @__PURE__ */ new Set();
		return e(n === void 0 || n.validation === !0 ? i : typeof n.validation == "object" ? n.validation.categories ?? i : []).filter((e) => !a.has(e)).toArray();
	}
	async findChangedUris(e) {
		if (this.langiumDocuments.getDocument(e) ?? this.textDocuments?.get(e)) return [e];
		try {
			let t = await this.fileSystemProvider.stat(e);
			if (t.isDirectory) return await this.workspaceManager().searchFolder(e);
			if (this.workspaceManager().shouldIncludeEntry(t)) return [e];
		} catch {}
		return [];
	}
	async emitUpdate(e, t) {
		await Promise.all(this.updateListeners.map((n) => n(e, t)));
	}
	sortDocuments(e) {
		let t = 0, n = e.length - 1;
		for (; t < n;) {
			for (; t < e.length && this.hasTextDocument(e[t]);) t++;
			for (; n >= 0 && !this.hasTextDocument(e[n]);) n--;
			t < n && ([e[t], e[n]] = [e[n], e[t]]);
		}
		return e;
	}
	hasTextDocument(e) {
		return !!this.textDocuments?.get(e.uri);
	}
	shouldRelink(e, t) {
		return e.references.some((e) => e.error !== void 0) ? !0 : this.indexManager.isAffected(e, t);
	}
	onUpdate(e) {
		return this.updateListeners.push(e), l.create(() => {
			let t = this.updateListeners.indexOf(e);
			t >= 0 && this.updateListeners.splice(t, 1);
		});
	}
	resetToState(e, t) {
		switch (t) {
			case o.Changed:
			case o.Parsed: this.indexManager.removeContent(e.uri);
			case o.IndexedContent: e.localSymbols = void 0;
			case o.ComputedScopes: this.serviceRegistry.getServices(e.uri).references.Linker.unlink(e);
			case o.Linked: this.indexManager.removeReferences(e.uri);
			case o.IndexedReferences: e.diagnostics = void 0, this.buildState.delete(e.uri.toString());
			case o.Validated:
		}
		e.state > t && (e.state = t);
	}
	cleanUpDeleted(e) {
		this.buildState.delete(e.uri.toString()), this.indexManager.remove(e.uri), e.state = o.Changed;
	}
	async buildDocuments(e, t, n) {
		this.prepareBuild(e, t), await this.runCancelable(e, o.Parsed, n, (e) => this.langiumDocumentFactory.update(e, n)), await this.runCancelable(e, o.IndexedContent, n, (e) => this.indexManager.updateContent(e, n)), await this.runCancelable(e, o.ComputedScopes, n, async (e) => {
			e.localSymbols = await this.serviceRegistry.getServices(e.uri).references.ScopeComputation.collectLocalSymbols(e, n);
		});
		let r = e.filter((e) => this.shouldLink(e));
		await this.runCancelable(r, o.Linked, n, (e) => this.serviceRegistry.getServices(e.uri).references.Linker.link(e, n)), await this.runCancelable(r, o.IndexedReferences, n, (e) => this.indexManager.updateReferences(e, n));
		let i = e.filter((e) => this.shouldValidate(e) ? !0 : (this.markAsCompleted(e), !1));
		await this.runCancelable(i, o.Validated, n, async (e) => {
			await this.validate(e, n), this.markAsCompleted(e);
		});
	}
	markAsCompleted(e) {
		let t = this.buildState.get(e.uri.toString());
		t && (t.completed = !0);
	}
	prepareBuild(e, t) {
		for (let n of e) {
			let e = n.uri.toString(), r = this.buildState.get(e);
			(!r || r.completed) && this.buildState.set(e, {
				completed: !1,
				options: t,
				result: r?.result
			});
		}
	}
	async runCancelable(e, t, n, i) {
		for (let a of e) a.state < t && (await r(n), await i(a), a.state = t, await this.notifyDocumentPhase(a, t, n));
		let a = e.filter((e) => e.state === t);
		await this.notifyBuildPhase(a, t, n), this.currentState = t;
	}
	onBuildPhase(e, t) {
		return this.buildPhaseListeners.add(e, t), l.create(() => {
			this.buildPhaseListeners.delete(e, t);
		});
	}
	onDocumentPhase(e, t) {
		return this.documentPhaseListeners.add(e, t), l.create(() => {
			this.documentPhaseListeners.delete(e, t);
		});
	}
	waitUntil(e, n, r) {
		let i;
		return n && "path" in n ? i = n : r = n, r ??= t.CancellationToken.None, i ? this.awaitDocumentState(e, i, r) : this.awaitBuilderState(e, r);
	}
	awaitDocumentState(e, t, r) {
		let i = this.langiumDocuments.getDocument(t);
		return i ? i.state >= e ? Promise.resolve(t) : r.isCancellationRequested ? Promise.reject(n) : this.currentState >= e && e > i.state ? Promise.reject(new u.ResponseError(u.LSPErrorCodes.RequestFailed, `Document state of ${t.toString()} is ${o[i.state]}, requiring ${o[e]}, but workspace state is already ${o[this.currentState]}. Returning undefined.`)) : new Promise((i, o) => {
			let s = this.onDocumentPhase(e, (e) => {
				a.equals(e.uri, t) && (s.dispose(), c.dispose(), i(e.uri));
			}), c = r.onCancellationRequested(() => {
				s.dispose(), c.dispose(), o(n);
			});
		}) : Promise.reject(new u.ResponseError(u.LSPErrorCodes.ServerCancelled, `No document found for URI: ${t.toString()}`));
	}
	awaitBuilderState(e, t) {
		return this.currentState >= e ? Promise.resolve() : t.isCancellationRequested ? Promise.reject(n) : new Promise((r, i) => {
			let a = this.onBuildPhase(e, () => {
				a.dispose(), o.dispose(), r();
			}), o = t.onCancellationRequested(() => {
				a.dispose(), o.dispose(), i(n);
			});
		});
	}
	async notifyDocumentPhase(e, t, n) {
		let a = this.documentPhaseListeners.get(t).slice();
		for (let t of a) try {
			await r(n), await t(e, n);
		} catch (e) {
			if (!i(e)) throw e;
		}
	}
	async notifyBuildPhase(e, t, n) {
		if (e.length === 0) return;
		let i = this.buildPhaseListeners.get(t).slice();
		for (let t of i) await r(n), await t(e, n);
	}
	shouldLink(e) {
		return this.getBuildOptions(e).eagerLinking ?? !0;
	}
	shouldValidate(e) {
		return !!this.getBuildOptions(e).validation;
	}
	async validate(t, n) {
		let r = this.serviceRegistry.getServices(t.uri).validation.DocumentValidator, i = this.getBuildOptions(t), a = typeof i.validation == "object" ? { ...i.validation } : {};
		a.categories = this.findMissingValidationCategories(t, i);
		let o = await r.validateDocument(t, a, n);
		t.diagnostics ? t.diagnostics.push(...o) : t.diagnostics = o;
		let s = this.buildState.get(t.uri.toString());
		s && (s.result ??= {}, s.result.validationChecks ? s.result.validationChecks = e(s.result.validationChecks).concat(a.categories).distinct().toArray() : s.result.validationChecks = [...a.categories]);
	}
	getBuildOptions(e) {
		return this.buildState.get(e.uri.toString())?.options ?? {};
	}
};
//#endregion
export { d as DefaultDocumentBuilder };
