"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.SemanticTokensFeature = void 0;
const vscode = require("vscode");
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const features_1 = require("./features");
const Is = require("./utils/is");
class SemanticTokensFeature extends features_1.TextDocumentLanguageFeature {
    constructor(client) {
        super(client, vscode_languageserver_protocol_1.SemanticTokensRegistrationType.type);
    }
    fillClientCapabilities(capabilities) {
        const capability = (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'semanticTokens');
        capability.dynamicRegistration = true;
        capability.tokenTypes = [
            vscode_languageserver_protocol_1.SemanticTokenTypes.namespace,
            vscode_languageserver_protocol_1.SemanticTokenTypes.type,
            vscode_languageserver_protocol_1.SemanticTokenTypes.class,
            vscode_languageserver_protocol_1.SemanticTokenTypes.enum,
            vscode_languageserver_protocol_1.SemanticTokenTypes.interface,
            vscode_languageserver_protocol_1.SemanticTokenTypes.struct,
            vscode_languageserver_protocol_1.SemanticTokenTypes.typeParameter,
            vscode_languageserver_protocol_1.SemanticTokenTypes.parameter,
            vscode_languageserver_protocol_1.SemanticTokenTypes.variable,
            vscode_languageserver_protocol_1.SemanticTokenTypes.property,
            vscode_languageserver_protocol_1.SemanticTokenTypes.enumMember,
            vscode_languageserver_protocol_1.SemanticTokenTypes.event,
            vscode_languageserver_protocol_1.SemanticTokenTypes.function,
            vscode_languageserver_protocol_1.SemanticTokenTypes.method,
            vscode_languageserver_protocol_1.SemanticTokenTypes.macro,
            vscode_languageserver_protocol_1.SemanticTokenTypes.keyword,
            vscode_languageserver_protocol_1.SemanticTokenTypes.modifier,
            vscode_languageserver_protocol_1.SemanticTokenTypes.comment,
            vscode_languageserver_protocol_1.SemanticTokenTypes.string,
            vscode_languageserver_protocol_1.SemanticTokenTypes.number,
            vscode_languageserver_protocol_1.SemanticTokenTypes.regexp,
            vscode_languageserver_protocol_1.SemanticTokenTypes.operator,
            vscode_languageserver_protocol_1.SemanticTokenTypes.decorator
        ];
        capability.tokenModifiers = [
            vscode_languageserver_protocol_1.SemanticTokenModifiers.declaration,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.definition,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.readonly,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.static,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.deprecated,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.abstract,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.async,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.modification,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.documentation,
            vscode_languageserver_protocol_1.SemanticTokenModifiers.defaultLibrary
        ];
        capability.formats = [vscode_languageserver_protocol_1.TokenFormat.Relative];
        capability.requests = {
            range: true,
            full: {
                delta: true
            }
        };
        capability.multilineTokenSupport = false;
        capability.overlappingTokenSupport = false;
        capability.serverCancelSupport = true;
        capability.augmentsSyntaxTokens = true;
        (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'workspace'), 'semanticTokens').refreshSupport = true;
    }
    initialize(capabilities, documentSelector) {
        const client = this._client;
        client.onRequest(vscode_languageserver_protocol_1.SemanticTokensRefreshRequest.type, async () => {
            for (const provider of this.getAllProviders()) {
                provider.onDidChangeSemanticTokensEmitter.fire();
            }
        });
        const [id, options] = this.getRegistration(documentSelector, capabilities.semanticTokensProvider);
        if (!id || !options) {
            return;
        }
        this.register({ id: id, registerOptions: options });
    }
    registerLanguageProvider(options) {
        const selector = options.documentSelector;
        const fullProvider = Is.boolean(options.full) ? options.full : options.full !== undefined;
        const hasEditProvider = options.full !== undefined && typeof options.full !== 'boolean' && options.full.delta === true;
        const eventEmitter = new vscode.EventEmitter();
        const documentProvider = fullProvider
            ? {
                onDidChangeSemanticTokens: eventEmitter.event,
                provideDocumentSemanticTokens: (document, token) => {
                    const client = this._client;
                    const middleware = client.middleware;
                    const provideDocumentSemanticTokens = (document, token) => {
                        const params = {
                            textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document)
                        };
                        return client.sendRequest(vscode_languageserver_protocol_1.SemanticTokensRequest.type, params, token).then((result) => {
                            if (token.isCancellationRequested) {
                                return null;
                            }
                            return client.protocol2CodeConverter.asSemanticTokens(result, token);
                        }, (error) => {
                            return client.handleFailedRequest(vscode_languageserver_protocol_1.SemanticTokensRequest.type, token, error, null);
                        });
                    };
                    return middleware.provideDocumentSemanticTokens
                        ? middleware.provideDocumentSemanticTokens(document, token, provideDocumentSemanticTokens)
                        : provideDocumentSemanticTokens(document, token);
                },
                provideDocumentSemanticTokensEdits: hasEditProvider
                    ? (document, previousResultId, token) => {
                        const client = this._client;
                        const middleware = client.middleware;
                        const provideDocumentSemanticTokensEdits = (document, previousResultId, token) => {
                            const params = {
                                textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
                                previousResultId
                            };
                            return client.sendRequest(vscode_languageserver_protocol_1.SemanticTokensDeltaRequest.type, params, token).then(async (result) => {
                                if (token.isCancellationRequested) {
                                    return null;
                                }
                                if (vscode_languageserver_protocol_1.SemanticTokens.is(result)) {
                                    return await client.protocol2CodeConverter.asSemanticTokens(result, token);
                                }
                                else {
                                    return await client.protocol2CodeConverter.asSemanticTokensEdits(result, token);
                                }
                            }, (error) => {
                                return client.handleFailedRequest(vscode_languageserver_protocol_1.SemanticTokensDeltaRequest.type, token, error, null);
                            });
                        };
                        return middleware.provideDocumentSemanticTokensEdits
                            ? middleware.provideDocumentSemanticTokensEdits(document, previousResultId, token, provideDocumentSemanticTokensEdits)
                            : provideDocumentSemanticTokensEdits(document, previousResultId, token);
                    }
                    : undefined
            }
            : undefined;
        const hasRangeProvider = options.range === true;
        const rangeProvider = hasRangeProvider
            ? {
                provideDocumentRangeSemanticTokens: (document, range, token) => {
                    const client = this._client;
                    const middleware = client.middleware;
                    const provideDocumentRangeSemanticTokens = (document, range, token) => {
                        const params = {
                            textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
                            range: client.code2ProtocolConverter.asRange(range)
                        };
                        return client.sendRequest(vscode_languageserver_protocol_1.SemanticTokensRangeRequest.type, params, token).then((result) => {
                            if (token.isCancellationRequested) {
                                return null;
                            }
                            return client.protocol2CodeConverter.asSemanticTokens(result, token);
                        }, (error) => {
                            return client.handleFailedRequest(vscode_languageserver_protocol_1.SemanticTokensRangeRequest.type, token, error, null);
                        });
                    };
                    return middleware.provideDocumentRangeSemanticTokens
                        ? middleware.provideDocumentRangeSemanticTokens(document, range, token, provideDocumentRangeSemanticTokens)
                        : provideDocumentRangeSemanticTokens(document, range, token);
                }
            }
            : undefined;
        const disposables = [];
        const client = this._client;
        const legend = client.protocol2CodeConverter.asSemanticTokensLegend(options.legend);
        const documentSelector = client.protocol2CodeConverter.asDocumentSelector(selector);
        if (documentProvider !== undefined) {
            disposables.push(vscode.languages.registerDocumentSemanticTokensProvider(documentSelector, documentProvider, legend));
        }
        if (rangeProvider !== undefined) {
            disposables.push(vscode.languages.registerDocumentRangeSemanticTokensProvider(documentSelector, rangeProvider, legend));
        }
        return [new vscode.Disposable(() => disposables.forEach(item => item.dispose())), { range: rangeProvider, full: documentProvider, onDidChangeSemanticTokensEmitter: eventEmitter }];
    }
}
exports.SemanticTokensFeature = SemanticTokensFeature;
