/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import * as lsp from 'vscode-languageserver-protocol';
import { LogLevel } from '../logging.js';
import { MdLinkKind } from '../types/documentLink.js';
import { isBefore } from '../types/position.js';
export 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(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: lsp.Range.create(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 === 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(root, entries, additionalSymbols) {
        let parent = root;
        for (const entry of entries) {
            if (entry.kind !== 'header') {
                continue;
            }
            while (additionalSymbols.length && isBefore(additionalSymbols[0].range.end, entry.sectionLocation.range.start)) {
                parent.children.push(additionalSymbols.shift());
            }
            while (parent && entry.level <= parent.level) {
                parent = parent.parent;
            }
            if (!parent) {
                // Should not happen
                return;
            }
            const symbol = this.#tocToDocumentSymbol(entry);
            symbol.children = [];
            parent.children.push(symbol);
            parent = { level: entry.level, children: symbol.children, parent, range: entry.sectionLocation.range };
        }
    }
    #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;
    }
}
//# sourceMappingURL=documentSymbols.js.map