"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
const transform_js_1 = require("@volar/typescript/lib/node/transform.js");
const createLanguageServicePlugin_1 = require("@volar/typescript/lib/quickstart/createLanguageServicePlugin");
const core = __importStar(require("@vue/language-core"));
const common_1 = require("./lib/common");
const collectExtractProps_1 = require("./lib/requests/collectExtractProps");
const getComponentDirectives_1 = require("./lib/requests/getComponentDirectives");
const getComponentMeta_1 = require("./lib/requests/getComponentMeta");
const getComponentNames_1 = require("./lib/requests/getComponentNames");
const getComponentSlots_1 = require("./lib/requests/getComponentSlots");
const getElementAttrs_1 = require("./lib/requests/getElementAttrs");
const getElementNames_1 = require("./lib/requests/getElementNames");
const getImportPathForFile_1 = require("./lib/requests/getImportPathForFile");
const isRefAtPosition_1 = require("./lib/requests/isRefAtPosition");
const resolveModuleName_1 = require("./lib/requests/resolveModuleName");
const projectToOriginalLanguageService = new WeakMap();
module.exports = (0, createLanguageServicePlugin_1.createLanguageServicePlugin)((ts, info) => {
    projectToOriginalLanguageService.set(info.project, info.languageService);
    const vueOptions = getVueCompilerOptions();
    const languagePlugin = core.createVueLanguagePlugin(ts, info.languageServiceHost.getCompilationSettings(), vueOptions, id => id);
    addVueCommands();
    let _language;
    (0, common_1.preprocessLanguageService)(info.languageService, () => _language);
    return {
        languagePlugins: [languagePlugin],
        setup: language => {
            _language = language;
            info.languageService = (0, common_1.postprocessLanguageService)(ts, language, info.languageService, vueOptions, fileName => fileName);
            info.project.__vue__ = { language };
        },
    };
    function getVueCompilerOptions() {
        if (info.project.projectKind === ts.server.ProjectKind.Configured) {
            const tsconfig = info.project.getProjectName();
            return core.createParsedCommandLine(ts, ts.sys, tsconfig.replace(/\\/g, '/')).vueOptions;
        }
        else {
            return core.createParsedCommandLineByJson(ts, ts.sys, info.languageServiceHost.getCurrentDirectory(), {})
                .vueOptions;
        }
    }
    // https://github.com/JetBrains/intellij-plugins/blob/6435723ad88fa296b41144162ebe3b8513f4949b/Angular/src-js/angular-service/src/index.ts#L69
    function addVueCommands() {
        const projectService = info.project.projectService;
        projectService.logger.info('Vue: called handler processing ' + info.project.projectKind);
        if (!info.session) {
            projectService.logger.info('Vue: there is no session in info.');
            return;
        }
        const session = info.session;
        if (!session.addProtocolHandler) {
            // addProtocolHandler was introduced in TS 4.4 or 4.5 in 2021, see https://github.com/microsoft/TypeScript/issues/43893
            projectService.logger.info('Vue: there is no addProtocolHandler method.');
            return;
        }
        // @ts-expect-error
        const handlers = session.handlers;
        if (handlers.has('_vue:projectInfo')) {
            return;
        }
        session.addProtocolHandler('_vue:projectInfo', request => {
            return handlers.get('projectInfo')(request);
        });
        session.addProtocolHandler('_vue:documentHighlights-full', request => {
            return handlers.get('documentHighlights-full')(request);
        });
        session.addProtocolHandler('_vue:encodedSemanticClassifications-full', request => {
            return handlers.get('encodedSemanticClassifications-full')(request);
        });
        session.addProtocolHandler('_vue:quickinfo', request => {
            return handlers.get('quickinfo')(request);
        });
        session.addProtocolHandler('_vue:collectExtractProps', request => {
            const [fileName, templateCodeRange] = request.arguments;
            const { program, language, sourceScript, virtualCode } = getProjectAndVirtualCode(fileName);
            return createResponse((0, collectExtractProps_1.collectExtractProps)(ts, language, program, sourceScript, virtualCode, templateCodeRange, sourceScript.generated ? sourceScript.snapshot.getLength() : 0));
        });
        session.addProtocolHandler('_vue:getImportPathForFile', request => {
            const [fileName, incomingFileName, preferences] = request.arguments;
            const { project, program } = getProject(fileName);
            return createResponse((0, getImportPathForFile_1.getImportPathForFile)(ts, project, program, fileName, incomingFileName, preferences));
        });
        session.addProtocolHandler('_vue:getAutoImportSuggestions', request => {
            const [fileName, position] = request.arguments;
            const { project, language, sourceScript, virtualCode } = getProjectAndVirtualCode(fileName);
            const tsLanguageService = projectToOriginalLanguageService.get(project);
            if (!tsLanguageService) {
                return createResponse(undefined);
            }
            for (const code of core.forEachEmbeddedCode(virtualCode)) {
                if (!code.id.startsWith('script_')) {
                    continue;
                }
                const map = language.maps.get(code, sourceScript);
                for (const [tsPosition, mapping] of map.toGeneratedLocation(position)) {
                    if (!mapping.data.__importCompletion) {
                        continue;
                    }
                    const tsPosition2 = tsPosition + sourceScript.snapshot.getLength();
                    const result = tsLanguageService.getCompletionsAtPosition(fileName, tsPosition2, session['getPreferences'](fileName), session['getFormatOptions'](fileName));
                    if (result) {
                        (0, common_1.resolveCompletionResult)(ts, language, fileName => fileName, vueOptions, fileName, position, result);
                        result.entries = result.entries
                            .filter(entry => {
                            const data = entry.data;
                            return data?.__vue__componentAutoImport || data?.__vue__autoImport;
                        });
                        for (const entry of result.entries) {
                            const data = entry.data;
                            data.__vue__autoImportSuggestions = {
                                fileName,
                                position: tsPosition + sourceScript.snapshot.getLength(),
                                entryName: data.__vue__componentAutoImport?.oldName ?? entry.name,
                                source: entry.source,
                            };
                        }
                    }
                    return createResponse(result);
                }
                const result = tsLanguageService.getCompletionsAtPosition(fileName, 0, session['getPreferences'](fileName), session['getFormatOptions'](fileName));
                if (result) {
                    (0, common_1.resolveCompletionResult)(ts, language, fileName => fileName, vueOptions, fileName, position, result);
                    result.entries = result.entries
                        .filter(entry => {
                        const data = entry.data;
                        return data?.__vue__componentAutoImport || data?.__vue__autoImport;
                    });
                    for (const entry of result.entries) {
                        const data = entry.data;
                        data.__vue__autoImportSuggestions = {
                            fileName,
                            position: 0,
                            entryName: data.__vue__componentAutoImport?.oldName ?? entry.name,
                            source: entry.source,
                        };
                    }
                    return createResponse(result);
                }
            }
            return createResponse(undefined);
        });
        session.addProtocolHandler('_vue:resolveAutoImportCompletionEntry', request => {
            const [data] = request.arguments;
            if (!data?.__vue__autoImportSuggestions) {
                return createResponse(undefined);
            }
            const { fileName, position, entryName, source } = data.__vue__autoImportSuggestions;
            const { project, language } = getProject(fileName);
            const tsLanguageService = projectToOriginalLanguageService.get(project);
            if (!tsLanguageService) {
                return createResponse(undefined);
            }
            const details = tsLanguageService.getCompletionEntryDetails(fileName, position, entryName, session['getFormatOptions'](fileName), source, session['getPreferences'](fileName), data);
            if (details) {
                for (const codeAction of details.codeActions ?? []) {
                    codeAction.changes = (0, transform_js_1.transformFileTextChanges)(language, codeAction.changes, false, core.isCompletionEnabled);
                }
                (0, common_1.resolveCompletionEntryDetails)(language, details, data);
            }
            return createResponse(details);
        });
        session.addProtocolHandler('_vue:isRefAtPosition', request => {
            const [fileName, position] = request.arguments;
            const { program, language, sourceScript, virtualCode } = getProjectAndVirtualCode(fileName);
            return createResponse((0, isRefAtPosition_1.isRefAtPosition)(ts, language, program, sourceScript, virtualCode, position, sourceScript.generated ? sourceScript.snapshot.getLength() : 0));
        });
        session.addProtocolHandler('_vue:getComponentDirectives', request => {
            const [fileName] = request.arguments;
            const { program } = getProject(fileName);
            return createResponse((0, getComponentDirectives_1.getComponentDirectives)(ts, program, fileName));
        });
        session.addProtocolHandler('_vue:getComponentNames', request => {
            const [fileName] = request.arguments;
            const { program, virtualCode } = getProjectAndVirtualCode(fileName);
            return createResponse((0, getComponentNames_1.getComponentNames)(ts, program, virtualCode));
        });
        session.addProtocolHandler('_vue:getComponentMeta', request => {
            const [fileName, tag] = request.arguments;
            const { program, virtualCode, language } = getProjectAndVirtualCode(fileName);
            const sourceFile = program.getSourceFile(virtualCode.fileName);
            return createResponse((0, getComponentMeta_1.getComponentMeta)(ts, program, language, fileName => language.scripts.get(fileName), sourceFile, virtualCode, tag));
        });
        session.addProtocolHandler('_vue:getComponentSlots', request => {
            const [fileName] = request.arguments;
            const { program, virtualCode } = getProjectAndVirtualCode(fileName);
            return createResponse((0, getComponentSlots_1.getComponentSlots)(ts, program, virtualCode));
        });
        session.addProtocolHandler('_vue:getElementAttrs', request => {
            const [fileName, tag] = request.arguments;
            const { program } = getProject(fileName);
            return createResponse((0, getElementAttrs_1.getElementAttrs)(ts, program, fileName, tag));
        });
        session.addProtocolHandler('_vue:getElementNames', request => {
            const [fileName] = request.arguments;
            const { program } = getProject(fileName);
            return createResponse((0, getElementNames_1.getElementNames)(ts, program, fileName));
        });
        session.addProtocolHandler('_vue:resolveModuleName', request => {
            const [fileName, moduleName] = request.arguments;
            return createResponse((0, resolveModuleName_1.resolveModuleName)(ts, info.languageServiceHost, fileName, moduleName));
        });
        projectService.logger.info('Vue specific commands are successfully added.');
        function createResponse(res) {
            return {
                response: res,
                responseRequired: true,
            };
        }
        function getProjectAndVirtualCode(fileName) {
            const project = getProject(fileName);
            const sourceScript = project.language.scripts.get(fileName);
            if (!sourceScript) {
                throw new Error('No source script found for file: ' + fileName);
            }
            const virtualCode = sourceScript.generated?.root;
            if (!(virtualCode instanceof core.VueVirtualCode)) {
                throw new Error('No virtual code found for file: ' + fileName);
            }
            return {
                ...project,
                sourceScript,
                virtualCode,
            };
        }
        function getProject(fileName) {
            const { project } = session['getFileAndProject']({
                file: fileName,
                projectFileName: undefined,
            });
            if (!('__vue__' in project)) {
                throw new Error('No vue project info for project: ' + project.getProjectName());
            }
            return {
                project,
                program: project.getLanguageService().getProgram(),
                language: project.__vue__.language,
            };
        }
    }
});
//# sourceMappingURL=index.js.map