"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.MdDocumentSymbolProvider = void 0;
const lsp = require("vscode-languageserver-types");
const logging_1 = require("../logging");
const position_1 = require("../types/position");
const range_1 = require("../types/range");
const documentLinks_1 = require("./documentLinks");
class MdDocumentSymbolProvider {
    #tocProvider;
    #linkProvider;
    #logger;
    constructor(tocProvider, linkProvider, logger) {
        this.#tocProvider = tocProvider;
        this.#linkProvider = linkProvider;
        this.#logger = logger;
    }
    async provideDocumentSymbols(document, options, token) {
        this.#logger.log(logging_1.LogLevel.Debug, 'DocumentSymbolProvider.provideDocumentSymbols', { document: document.uri, version: document.version });
        const linkSymbols = await (options.includeLinkDefinitions ? this.#provideLinkDefinitionSymbols(document, token) : []);
        if (token.isCancellationRequested) {
            return [];
        }
        const toc = await this.#tocProvider.getForDocument(document);
        if (token.isCancellationRequested) {
            return [];
        }
        return this.#toSymbolTree(document, linkSymbols, toc);
    }
    #toSymbolTree(document, linkSymbols, toc) {
        const root = {
            level: -Infinity,
            children: [],
            parent: undefined,
            range: (0, range_1.makeRange)(0, 0, document.lineCount + 1, 0),
        };
        const additionalSymbols = [...linkSymbols];
        this.#buildTocSymbolTree(root, toc.entries, additionalSymbols);
        // Put remaining link definitions into top level document instead of last header
        root.children.push(...additionalSymbols);
        return root.children;
    }
    async #provideLinkDefinitionSymbols(document, token) {
        const { links } = await this.#linkProvider.getLinks(document);
        if (token.isCancellationRequested) {
            return [];
        }
        return links
            .filter(link => link.kind === documentLinks_1.MdLinkKind.Definition)
            .map((link) => this.#definitionToDocumentSymbol(link));
    }
    #definitionToDocumentSymbol(def) {
        return {
            kind: lsp.SymbolKind.Constant,
            name: `[${def.ref.text}]`,
            selectionRange: def.ref.range,
            range: def.source.range,
        };
    }
    #buildTocSymbolTree(parent, entries, additionalSymbols) {
        if (entries.length) {
            while (additionalSymbols.length && (0, position_1.isBefore)(additionalSymbols[0].range.end, entries[0].sectionLocation.range.start)) {
                parent.children.push(additionalSymbols.shift());
            }
        }
        if (!entries.length) {
            return;
        }
        const entry = entries[0];
        const symbol = this.#tocToDocumentSymbol(entry);
        symbol.children = [];
        while (entry.level <= parent.level) {
            parent = parent.parent;
        }
        parent.children.push(symbol);
        this.#buildTocSymbolTree({ level: entry.level, children: symbol.children, parent, range: entry.sectionLocation.range }, entries.slice(1), additionalSymbols);
    }
    #tocToDocumentSymbol(entry) {
        return {
            name: this.#getTocSymbolName(entry),
            kind: lsp.SymbolKind.String,
            range: entry.sectionLocation.range,
            selectionRange: entry.sectionLocation.range
        };
    }
    #getTocSymbolName(entry) {
        return '#'.repeat(entry.level) + ' ' + entry.text;
    }
}
exports.MdDocumentSymbolProvider = MdDocumentSymbolProvider;
//# sourceMappingURL=documentSymbols.js.map