"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkspaceSymbolsProviderImpl = void 0;
const svelte2tsx_1 = require("svelte2tsx");
const typescript_1 = __importDefault(require("typescript"));
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const documents_1 = require("../../../lib/documents");
const utils_1 = require("../../../utils");
const DocumentSnapshot_1 = require("../DocumentSnapshot");
const service_1 = require("../service");
const utils_2 = require("../utils");
const utils_3 = require("./utils");
class WorkspaceSymbolsProviderImpl {
    constructor(lsAndTsDocResolver, configManager) {
        this.configManager = configManager;
        this.lsAndTsDocResolver = lsAndTsDocResolver;
    }
    async getWorkspaceSymbols(query, cancellationToken) {
        const allServices = [];
        await (0, service_1.forAllServices)((service) => {
            allServices.push(service);
        });
        const symbols = new Map();
        // The config only exists for typescript. No javascript counterpart.
        const preference = this.configManager.getTsUserPreferences('typescript', null);
        for (const ls of allServices) {
            if (cancellationToken?.isCancellationRequested) {
                return null;
            }
            const service = ls.getService();
            const projectItems = service.getNavigateToItems(query, 
            /* maxResultCount */ 256, 
            /* fileName */ undefined, 
            /* excludeDtsFiles */ ls.snapshotManager.allFilesAreJsOrDts(), preference.excludeLibrarySymbolsInNavTo);
            const snapshots = new utils_3.SnapshotMap(this.lsAndTsDocResolver, ls);
            for (const item of projectItems) {
                if (this.isGeneratedName(item) ||
                    (item.kind === typescript_1.default.ScriptElementKind.alias && !item.containerName)) {
                    continue;
                }
                const seen = symbols.get(item.name);
                if (!seen) {
                    symbols.set(item.name, [
                        [
                            item,
                            this.mapWorkspaceSymbol(item, await snapshots.retrieve(item.fileName))
                        ]
                    ]);
                    continue;
                }
                let skip = false;
                for (const [seenItem] of seen) {
                    if (this.navigateToItemIsEqualTo(seenItem, item)) {
                        skip = true;
                        break;
                    }
                }
                if (skip) {
                    continue;
                }
                const snapshot = await snapshots.retrieve(item.fileName);
                if (snapshot instanceof DocumentSnapshot_1.SvelteDocumentSnapshot &&
                    (0, utils_3.isInGeneratedCode)(snapshot.getFullText(), item.textSpan.start)) {
                    continue;
                }
                seen.push([
                    item,
                    this.mapWorkspaceSymbol(item, await snapshots.retrieve(item.fileName))
                ]);
            }
        }
        return Array.from(symbols.values())
            .flatMap((items) => items.map(([_, symbol]) => symbol))
            .filter(utils_1.isNotNullOrUndefined);
    }
    isGeneratedName(item) {
        if (!(0, utils_2.isSvelteFilePath)(item.fileName)) {
            return false;
        }
        return (item.name === svelte2tsx_1.internalHelpers.renderName ||
            item.name.startsWith('__sveltets_') ||
            item.name.startsWith('$$'));
    }
    mapWorkspaceSymbol(item, snapshot) {
        let location = (0, documents_1.mapLocationToOriginal)(snapshot, (0, utils_2.convertRange)(snapshot, item.textSpan));
        if (location.range.start.line < 0) {
            if ((0, utils_2.isGeneratedSvelteComponentName)(item.name)) {
                location = {
                    uri: snapshot.getURL(),
                    range: {
                        start: { line: 0, character: 0 },
                        end: { line: 0, character: 1 }
                    }
                };
            }
            else {
                return undefined;
            }
        }
        return {
            kind: this.convertSymbolKindForWorkspaceSymbol(item.kind),
            name: this.getLabel(item),
            containerName: snapshot instanceof DocumentSnapshot_1.SvelteDocumentSnapshot &&
                (item.containerName === svelte2tsx_1.internalHelpers.renderName || !item.containerName)
                ? (0, utils_2.isInScript)(location.range.start, snapshot)
                    ? 'script'
                    : undefined
                : item.containerName,
            location,
            tags: item.kindModifiers?.includes('deprecated') ? [vscode_languageserver_types_1.SymbolTag.Deprecated] : undefined
        };
    }
    /**
     *
     * https://github.com/microsoft/TypeScript/blob/81c951894e93bdc37c6916f18adcd80de76679bc/src/server/session.ts#L2878
     */
    navigateToItemIsEqualTo(a, b) {
        if (a === b) {
            return true;
        }
        if (!a || !b) {
            return false;
        }
        return (a.containerKind === b.containerKind &&
            a.containerName === b.containerName &&
            a.fileName === b.fileName &&
            a.isCaseSensitive === b.isCaseSensitive &&
            a.kind === b.kind &&
            a.kindModifiers === b.kindModifiers &&
            a.matchKind === b.matchKind &&
            a.name === b.name &&
            a.textSpan.start === b.textSpan.start &&
            a.textSpan.length === b.textSpan.length);
    }
    /**
     * Don't reuse our symbolKindFromString function, this should the same as the one in vscode
     * so that vscode deduplicate the symbols from svelte and the typescript server.
     * https://github.com/microsoft/vscode/blob/18ed64835ec8f8227dbd8562d2d9fd9fa339abbb/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts#L17
     */
    convertSymbolKindForWorkspaceSymbol(kind) {
        switch (kind) {
            case typescript_1.default.ScriptElementKind.memberFunctionElement:
                return vscode_languageserver_types_1.SymbolKind.Method;
            case typescript_1.default.ScriptElementKind.enumElement:
                return vscode_languageserver_types_1.SymbolKind.Enum;
            case typescript_1.default.ScriptElementKind.enumMemberElement:
                return vscode_languageserver_types_1.SymbolKind.EnumMember;
            case typescript_1.default.ScriptElementKind.functionElement:
                return vscode_languageserver_types_1.SymbolKind.Function;
            case typescript_1.default.ScriptElementKind.classElement:
                return vscode_languageserver_types_1.SymbolKind.Class;
            case typescript_1.default.ScriptElementKind.interfaceElement:
                return vscode_languageserver_types_1.SymbolKind.Interface;
            case typescript_1.default.ScriptElementKind.typeElement:
                return vscode_languageserver_types_1.SymbolKind.Class;
            case typescript_1.default.ScriptElementKind.memberVariableElement:
            case typescript_1.default.ScriptElementKind.memberGetAccessorElement:
            case typescript_1.default.ScriptElementKind.memberSetAccessorElement:
                return vscode_languageserver_types_1.SymbolKind.Field;
            default:
                return vscode_languageserver_types_1.SymbolKind.Variable;
        }
    }
    getLabel(item) {
        const label = item.name;
        if (item.kind === 'method' || item.kind === 'function') {
            return label + '()';
        }
        return label;
    }
}
exports.WorkspaceSymbolsProviderImpl = WorkspaceSymbolsProviderImpl;
//# sourceMappingURL=WorkspaceSymbolProvider.js.map