"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveConfig = void 0;
const vue = require("@vue/language-core");
const shared_1 = require("@vue/shared");
const volar_service_css_1 = require("volar-service-css");
const volar_service_emmet_1 = require("volar-service-emmet");
const volar_service_html_1 = require("volar-service-html");
const volar_service_json_1 = require("volar-service-json");
const volar_service_pug_1 = require("volar-service-pug");
const volar_service_pug_beautify_1 = require("volar-service-pug-beautify");
const volar_service_typescript_1 = require("volar-service-typescript");
const volar_service_typescript_twoslash_queries_1 = require("volar-service-typescript-twoslash-queries");
const nameCasing_1 = require("./ideFeatures/nameCasing");
const vue_1 = require("./plugins/vue");
const vue_autoinsert_dotvalue_1 = require("./plugins/vue-autoinsert-dotvalue");
const vue_autoinsert_parentheses_1 = require("./plugins/vue-autoinsert-parentheses");
const vue_autoinsert_space_1 = require("./plugins/vue-autoinsert-space");
const vue_codelens_references_1 = require("./plugins/vue-codelens-references");
const vue_template_1 = require("./plugins/vue-template");
const vue_twoslash_queries_1 = require("./plugins/vue-twoslash-queries");
const vue_visualize_hidden_callback_param_1 = require("./plugins/vue-visualize-hidden-callback-param");
const types_1 = require("./types");
function resolveConfig(config, compilerOptions = {}, vueCompilerOptions = {}, ts = require('typescript'), settings, codegenStack = false) {
    const resolvedVueCompilerOptions = vue.resolveVueCompilerOptions(vueCompilerOptions);
    const vueLanguageModules = vue.createLanguages(compilerOptions, resolvedVueCompilerOptions, ts, codegenStack);
    config.languages = Object.assign({}, vueLanguageModules, config.languages);
    config.services = resolvePlugins(config.services, resolvedVueCompilerOptions, settings);
    return config;
}
exports.resolveConfig = resolveConfig;
function resolvePlugins(services, vueCompilerOptions, settings) {
    const originalTsPlugin = services?.typescript ?? (0, volar_service_typescript_1.default)();
    services ??= {};
    services.typescript = (_context, modules) => {
        const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(_context, modules) : originalTsPlugin;
        if (!_context || !modules?.typescript)
            return base;
        const ts = modules.typescript;
        const transformedItem = new WeakSet();
        return {
            ...base,
            transformCompletionItem(item) {
                if (transformedItem.has(item)) {
                    return item;
                }
            },
            async provideCompletionItems(document, position, context, item) {
                const result = await base.provideCompletionItems?.(document, position, context, item);
                if (result) {
                    // filter __VLS_
                    result.items = result.items.filter(item => item.label.indexOf('__VLS_') === -1
                        && (!item.labelDetails?.description || item.labelDetails.description.indexOf('__VLS_') === -1));
                    // handle component auto-import patch
                    let casing;
                    for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
                        const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
                        if (virtualFile instanceof vue.VueFile) {
                            const isAutoImport = !!map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly);
                            if (isAutoImport) {
                                const source = _context.documents.getVirtualFileByUri(document.uri)[1];
                                for (const item of result.items) {
                                    item.data.__isComponentAutoImport = true;
                                }
                                // fix #2458
                                if (source) {
                                    casing ??= await (0, nameCasing_1.getNameCasing)(ts, _context, _context.env.fileNameToUri(source.fileName), vueCompilerOptions);
                                    if (casing.tag === types_1.TagNameCasing.Kebab) {
                                        for (const item of result.items) {
                                            item.filterText = (0, shared_1.hyphenate)(item.filterText ?? item.label);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                return result;
            },
            async resolveCompletionItem(item, token) {
                item = await base.resolveCompletionItem?.(item, token) ?? item;
                const itemData = item.data;
                let newName;
                if (itemData?.uri && item.additionalTextEdits) {
                    patchAdditionalTextEdits(itemData.uri, item.additionalTextEdits);
                }
                for (const ext of vueCompilerOptions.extensions) {
                    const suffix = (0, shared_1.capitalize)(ext.substring('.'.length)); // .vue -> Vue
                    if (itemData?.uri
                        && item.textEdit?.newText.endsWith(suffix)
                        && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0
                        && (await _context.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true)) {
                        newName = item.textEdit.newText.slice(0, -suffix.length);
                        newName = newName[0].toUpperCase() + newName.substring(1);
                        if (newName === 'Index') {
                            const tsItem = item.data.originalItem;
                            if (tsItem.source) {
                                const dirs = tsItem.source.split('/');
                                if (dirs.length >= 3) {
                                    newName = dirs[dirs.length - 2];
                                    newName = newName[0].toUpperCase() + newName.substring(1);
                                }
                            }
                        }
                        item.additionalTextEdits[0].newText = item.additionalTextEdits[0].newText.replace('import ' + item.textEdit.newText + ' from ', 'import ' + newName + ' from ');
                        item.textEdit.newText = newName;
                        const source = _context.documents.getVirtualFileByUri(itemData.uri)[1];
                        if (source) {
                            const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, _context.env.fileNameToUri(source.fileName), vueCompilerOptions);
                            if (casing.tag === types_1.TagNameCasing.Kebab) {
                                item.textEdit.newText = (0, shared_1.hyphenate)(item.textEdit.newText);
                            }
                        }
                    }
                    else if (item.textEdit?.newText && new RegExp(`import \\w*${suffix}\\$1 from [\\S\\s]*`).test(item.textEdit.newText)) {
                        // https://github.com/vuejs/language-tools/issues/2286
                        item.textEdit.newText = item.textEdit.newText.replace(`${suffix}$1`, '$1');
                    }
                }
                const data = item.data;
                if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit) {
                    let transformed = false;
                    for (const [_, map] of _context.documents.getMapsByVirtualFileUri(data.uri)) {
                        const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
                        if (virtualFile instanceof vue.VueFile) {
                            const sfc = virtualFile.sfc;
                            const componentName = newName ?? item.textEdit.newText;
                            const textDoc = _context.documents.getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName);
                            if (sfc.scriptAst && sfc.script) {
                                const _scriptRanges = vue.scriptRanges.parseScriptRanges(ts, sfc.scriptAst, !!sfc.scriptSetup, true);
                                const exportDefault = _scriptRanges.exportDefault;
                                if (exportDefault) {
                                    // https://github.com/microsoft/TypeScript/issues/36174
                                    const printer = ts.createPrinter();
                                    if (exportDefault.componentsOption && exportDefault.componentsOptionNode) {
                                        const newNode = {
                                            ...exportDefault.componentsOptionNode,
                                            properties: [
                                                ...exportDefault.componentsOptionNode.properties,
                                                ts.factory.createShorthandPropertyAssignment(componentName),
                                            ],
                                        };
                                        const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
                                        const editRange = {
                                            start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.start),
                                            end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.end),
                                        };
                                        transformed = true;
                                        item.additionalTextEdits.push({
                                            range: editRange,
                                            newText: unescape(printText.replace(/\\u/g, '%u')),
                                        });
                                    }
                                    else if (exportDefault.args && exportDefault.argsNode) {
                                        const newNode = {
                                            ...exportDefault.argsNode,
                                            properties: [
                                                ...exportDefault.argsNode.properties,
                                                ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
                                            ],
                                        };
                                        const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
                                        const editRange = {
                                            start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.start),
                                            end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.end),
                                        };
                                        transformed = true;
                                        item.additionalTextEdits.push({
                                            range: editRange,
                                            newText: unescape(printText.replace(/\\u/g, '%u')),
                                        });
                                    }
                                }
                            }
                        }
                    }
                    if (transformed) {
                        transformedItem.add(item);
                    }
                }
                return item;
            },
            async provideCodeActions(document, range, context, token) {
                const result = await base.provideCodeActions?.(document, range, context, token);
                return result?.filter(codeAction => codeAction.title.indexOf('__VLS_') === -1);
            },
            async resolveCodeAction(item, token) {
                const result = await base.resolveCodeAction?.(item, token) ?? item;
                if (result?.edit?.changes) {
                    for (const uri in result.edit.changes) {
                        const edits = result.edit.changes[uri];
                        if (edits) {
                            patchAdditionalTextEdits(uri, edits);
                        }
                    }
                }
                if (result?.edit?.documentChanges) {
                    for (const documentChange of result.edit.documentChanges) {
                        if ('textDocument' in documentChange) {
                            patchAdditionalTextEdits(documentChange.textDocument.uri, documentChange.edits);
                        }
                    }
                }
                return result;
            },
        };
    };
    services.html ??= (0, vue_template_1.default)({
        baseService: (0, volar_service_html_1.default)(),
        getScanner: (htmlService, document) => {
            return htmlService.provide['html/languageService']().createScanner(document.getText());
        },
        updateCustomData(htmlService, extraData) {
            htmlService.provide['html/updateCustomData'](extraData);
        },
        isSupportedDocument: (document) => document.languageId === 'html',
        vueCompilerOptions,
    });
    services.pug ??= (0, vue_template_1.default)({
        baseService: (0, volar_service_pug_1.default)(),
        getScanner: (pugService, document) => {
            const pugDocument = pugService.provide['pug/pugDocument'](document);
            if (pugDocument) {
                return pugService.provide['pug/languageService']().createScanner(pugDocument);
            }
        },
        updateCustomData(pugService, extraData) {
            pugService.provide['pug/updateCustomData'](extraData);
        },
        isSupportedDocument: (document) => document.languageId === 'jade',
        vueCompilerOptions,
    });
    services.vue ??= (0, vue_1.default)();
    services.css ??= (0, volar_service_css_1.default)();
    services['pug-beautify'] ??= (0, volar_service_pug_beautify_1.default)();
    services.json ??= (0, volar_service_json_1.default)(settings?.json);
    services['typescript/twoslash-queries'] ??= (0, volar_service_typescript_twoslash_queries_1.default)();
    services['vue/referencesCodeLens'] ??= (0, vue_codelens_references_1.default)();
    services['vue/autoInsertDotValue'] ??= (0, vue_autoinsert_dotvalue_1.default)();
    services['vue/twoslash-queries'] ??= (0, vue_twoslash_queries_1.default)();
    services['vue/autoInsertParentheses'] ??= (0, vue_autoinsert_parentheses_1.default)();
    services['vue/autoInsertSpaces'] ??= (0, vue_autoinsert_space_1.default)();
    services['vue/visualizeHiddenCallbackParam'] ??= (0, vue_visualize_hidden_callback_param_1.default)();
    services.emmet ??= (0, volar_service_emmet_1.default)();
    return services;
}
// fix https://github.com/vuejs/language-tools/issues/916
function patchAdditionalTextEdits(uri, edits) {
    if (uri.endsWith('.vue.js')
        || uri.endsWith('.vue.ts')
        || uri.endsWith('.vue.jsx')
        || uri.endsWith('.vue.tsx')) {
        for (const edit of edits) {
            if (edit.range.start.line === 0
                && edit.range.start.character === 0
                && edit.range.end.line === 0
                && edit.range.end.character === 0) {
                edit.newText = '\n' + edit.newText;
            }
        }
    }
}
//# sourceMappingURL=languageService.js.map