"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SemanticTokensProviderImpl = void 0;
const typescript_1 = __importDefault(require("typescript"));
const vscode_languageserver_1 = require("vscode-languageserver");
const documents_1 = require("../../../lib/documents");
const utils_1 = require("../utils");
const utils_2 = require("./utils");
const svelte2tsx_1 = require("svelte2tsx");
const CONTENT_LENGTH_LIMIT = 50000;
class SemanticTokensProviderImpl {
    constructor(lsAndTsDocResolver) {
        this.lsAndTsDocResolver = lsAndTsDocResolver;
    }
    async getSemanticTokens(textDocument, range, cancellationToken) {
        const { lang, tsDoc } = await this.lsAndTsDocResolver.getLSAndTSDoc(textDocument);
        // for better performance, don't do full-file semantic tokens when the file is too big
        if ((!range && tsDoc.getLength() > CONTENT_LENGTH_LIMIT) ||
            cancellationToken?.isCancellationRequested) {
            return null;
        }
        // No script tags -> nothing to analyse semantic tokens for
        if (!textDocument.scriptInfo && !textDocument.moduleScriptInfo) {
            return null;
        }
        const textSpan = range
            ? (0, utils_1.convertToTextSpan)(range, tsDoc)
            : {
                start: 0,
                length: tsDoc.parserError
                    ? tsDoc.getLength()
                    : // This is appended by svelte2tsx, there's nothing mappable afterwards
                        tsDoc.getFullText().lastIndexOf('return { props:') || tsDoc.getLength()
            };
        const { spans } = lang.getEncodedSemanticClassifications(tsDoc.filePath, textSpan, typescript_1.default.SemanticClassificationFormat.TwentyTwenty);
        const data = [];
        let index = 0;
        while (index < spans.length) {
            // [start, length, encodedClassification, start2, length2, encodedClassification2]
            const generatedOffset = spans[index++];
            const generatedLength = spans[index++];
            const encodedClassification = spans[index++];
            const classificationType = this.getTokenTypeFromClassification(encodedClassification);
            if (classificationType < 0) {
                continue;
            }
            const original = this.map(textDocument, tsDoc, generatedOffset, generatedLength, encodedClassification, classificationType);
            // remove identifiers whose start and end mapped to the same location,
            // like the svelte2tsx inserted render function,
            // or reversed like Component.$on
            if (!original || original[2] <= 0) {
                continue;
            }
            data.push(original);
        }
        const sorted = data.sort((a, b) => {
            const [lineA, charA] = a;
            const [lineB, charB] = b;
            return lineA - lineB || charA - charB;
        });
        const builder = new vscode_languageserver_1.SemanticTokensBuilder();
        sorted.forEach((tokenData) => builder.push(...tokenData));
        return builder.build();
    }
    map(document, snapshot, generatedOffset, generatedLength, encodedClassification, classificationType) {
        const text = snapshot.getFullText();
        if ((0, utils_2.isInGeneratedCode)(text, generatedOffset, generatedOffset + generatedLength) ||
            (encodedClassification === 2817 /* top level function */ &&
                text.substring(generatedOffset, generatedOffset + generatedLength) ===
                    svelte2tsx_1.internalHelpers.renderName)) {
            return;
        }
        const range = {
            start: snapshot.positionAt(generatedOffset),
            end: snapshot.positionAt(generatedOffset + generatedLength)
        };
        const { start: startPosition, end: endPosition } = (0, documents_1.mapRangeToOriginal)(snapshot, range);
        if (startPosition.line < 0 || endPosition.line < 0) {
            return;
        }
        const startOffset = document.offsetAt(startPosition);
        const endOffset = document.offsetAt(endPosition);
        const originalText = document.getText();
        // Ensure components in the template get no semantic highlighting
        if ((classificationType === 0 /* TokenType.class */ ||
            classificationType === 5 /* TokenType.type */ ||
            classificationType === 6 /* TokenType.parameter */ ||
            classificationType === 7 /* TokenType.variable */ ||
            classificationType === 10 /* TokenType.function */) &&
            (originalText.charCodeAt(startOffset - 1) === /* < */ 60 ||
                originalText.charCodeAt(startOffset - 1) === /* / */ 47) &&
            snapshot.svelteNodeAt(startOffset)?.type === 'InlineComponent') {
            return;
        }
        return [
            startPosition.line,
            startPosition.character,
            endOffset - startOffset,
            classificationType,
            this.getTokenModifierFromClassification(encodedClassification)
        ];
    }
    /**
     *  TSClassification = (TokenType + 1) << TokenEncodingConsts.typeOffset + TokenModifier
     */
    getTokenTypeFromClassification(tsClassification) {
        return (tsClassification >> 8 /* TokenEncodingConsts.typeOffset */) - 1;
    }
    getTokenModifierFromClassification(tsClassification) {
        return tsClassification & 255 /* TokenEncodingConsts.modifierMask */;
    }
}
exports.SemanticTokensProviderImpl = SemanticTokensProviderImpl;
//# sourceMappingURL=SemanticTokensProvider.js.map