"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MdWorkspaceInfoCache = exports.MdDocumentInfoCache = void 0;
const vscode_languageserver_1 = require("vscode-languageserver");
const textDocument_1 = require("./types/textDocument");
const dispose_1 = require("./util/dispose");
const lazy_1 = require("./util/lazy");
const resourceMap_1 = require("./util/resourceMap");
/**
 * Cache of information per-document in the workspace.
 *
 * The values are computed lazily and invalidated when the document changes.
 */
class MdDocumentInfoCache extends dispose_1.Disposable {
    #cache = new resourceMap_1.ResourceMap();
    #loadingDocuments = new resourceMap_1.ResourceMap();
    #workspace;
    #getValue;
    constructor(workspace, getValue) {
        super();
        this.#workspace = workspace;
        this.#getValue = getValue;
        this._register(this.#workspace.onDidChangeMarkdownDocument(doc => this.#invalidate(doc)));
        this._register(this.#workspace.onDidDeleteMarkdownDocument(this.#onDidDeleteDocument, this));
    }
    async get(resource) {
        let existing = this.#cache.get(resource);
        if (existing) {
            return existing.value.value;
        }
        const doc = await this.#loadDocument(resource);
        if (!doc) {
            return undefined;
        }
        // Check if we have invalidated
        existing = this.#cache.get(resource);
        if (existing) {
            return existing.value.value;
        }
        return this.#resetEntry(doc)?.value;
    }
    async getForDocument(document) {
        const existing = this.#cache.get((0, textDocument_1.getDocUri)(document));
        if (existing) {
            return existing.value.value;
        }
        return this.#resetEntry(document).value;
    }
    #loadDocument(resource) {
        const existing = this.#loadingDocuments.get(resource);
        if (existing) {
            return existing;
        }
        const p = this.#workspace.openMarkdownDocument(resource);
        this.#loadingDocuments.set(resource, p);
        p.finally(() => {
            this.#loadingDocuments.delete(resource);
        });
        return p;
    }
    #resetEntry(document) {
        // TODO: cancel old request?
        const cts = new vscode_languageserver_1.CancellationTokenSource();
        const value = (0, lazy_1.lazy)(() => this.#getValue(document, cts.token));
        this.#cache.set((0, textDocument_1.getDocUri)(document), { value, cts });
        return value;
    }
    #invalidate(document) {
        if (this.#cache.has((0, textDocument_1.getDocUri)(document))) {
            this.#resetEntry(document);
        }
    }
    #onDidDeleteDocument(resource) {
        const entry = this.#cache.get(resource);
        if (entry) {
            entry.cts.cancel();
            entry.cts.dispose();
            this.#cache.delete(resource);
        }
    }
}
exports.MdDocumentInfoCache = MdDocumentInfoCache;
/**
 * Cache of information across all markdown files in the workspace.
 *
 * Unlike {@link MdDocumentInfoCache}, the entries here are computed eagerly for every file in the workspace.
 * However the computation of the values is still lazy.
 */
class MdWorkspaceInfoCache extends dispose_1.Disposable {
    #cache = new resourceMap_1.ResourceMap();
    #init;
    #workspace;
    #getValue;
    constructor(workspace, getValue) {
        super();
        this.#workspace = workspace;
        this.#getValue = getValue;
        this._register(this.#workspace.onDidChangeMarkdownDocument(this.#onDidChangeDocument, this));
        this._register(this.#workspace.onDidCreateMarkdownDocument(this.#onDidChangeDocument, this));
        this._register(this.#workspace.onDidDeleteMarkdownDocument(this.#onDidDeleteDocument, this));
    }
    async entries() {
        await this.#ensureInit();
        return Promise.all(Array.from(this.#cache.entries(), async ([k, v]) => {
            return [k, await v.value.value];
        }));
    }
    async values() {
        await this.#ensureInit();
        return Promise.all(Array.from(this.#cache.entries(), x => x[1].value.value));
    }
    async getForDocs(docs) {
        for (const doc of docs) {
            if (!this.#cache.has((0, textDocument_1.getDocUri)(doc))) {
                this.#update(doc);
            }
        }
        return Promise.all(docs.map(doc => this.#cache.get((0, textDocument_1.getDocUri)(doc)).value.value));
    }
    async #ensureInit() {
        if (!this.#init) {
            this.#init = this.#populateCache();
        }
        await this.#init;
    }
    async #populateCache() {
        const markdownDocuments = await this.#workspace.getAllMarkdownDocuments();
        for (const document of markdownDocuments) {
            if (!this.#cache.has((0, textDocument_1.getDocUri)(document))) {
                this.#update(document);
            }
        }
    }
    #update(document) {
        // TODO: cancel old request?
        const cts = new vscode_languageserver_1.CancellationTokenSource();
        this.#cache.set((0, textDocument_1.getDocUri)(document), {
            value: (0, lazy_1.lazy)(() => this.#getValue(document, cts.token)),
            cts
        });
    }
    #onDidChangeDocument(document) {
        this.#update(document);
    }
    #onDidDeleteDocument(resource) {
        const entry = this.#cache.get(resource);
        if (entry) {
            entry.cts.cancel();
            entry.cts.dispose();
            this.#cache.delete(resource);
        }
    }
}
exports.MdWorkspaceInfoCache = MdWorkspaceInfoCache;
//# sourceMappingURL=workspaceCache.js.map