"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveConfig = void 0;
const css_1 = require("@volar-plugins/css");
const emmet_1 = require("@volar-plugins/emmet");
const html_1 = require("@volar-plugins/html");
const json_1 = require("@volar-plugins/json");
const pug_1 = require("@volar-plugins/pug");
const typescript_1 = require("@volar-plugins/typescript");
const typescript_twoslash_queries_1 = require("@volar-plugins/typescript-twoslash-queries");
const vue = require("@volar/vue-language-core");
const vscode = require("vscode-languageserver-protocol");
const vue_1 = require("./plugins/vue");
const vue_autoinsert_dotvalue_1 = require("./plugins/vue-autoinsert-dotvalue");
const vue_codelens_references_1 = require("./plugins/vue-codelens-references");
const vue_twoslash_queries_1 = require("./plugins/vue-twoslash-queries");
const vue_template_1 = require("./plugins/vue-template");
const vue_visualize_hidden_callback_param_1 = require("./plugins/vue-visualize-hidden-callback-param");
const shared_1 = require("@vue/shared");
const pug_beautify_1 = require("@volar-plugins/pug-beautify");
const vue_autoinsert_parentheses_1 = require("./plugins/vue-autoinsert-parentheses");
const vue_autoinsert_space_1 = require("./plugins/vue-autoinsert-space");
const types_1 = require("./types");
const nameCasing_1 = require("./ideFeatures/nameCasing");
function resolveConfig(config, // volar.config.js
ts, compilerOptions, vueCompilerOptions, settings) {
    const resolvedVueOptions = vue.resolveVueCompilerOptions(vueCompilerOptions);
    const vueLanguageModules = vue.createLanguageModules(ts, compilerOptions, resolvedVueOptions);
    config.languages = Object.assign({}, vueLanguageModules, config.languages);
    config.plugins = resolvePlugins(config.plugins, resolvedVueOptions, settings);
    return config;
}
exports.resolveConfig = resolveConfig;
function resolvePlugins(plugins, vueCompilerOptions, settings) {
    const originalTsPlugin = plugins?.typescript ?? (0, typescript_1.default)();
    plugins ??= {};
    plugins.typescript = (_context) => {
        const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(_context) : originalTsPlugin;
        if (!_context?.typescript)
            return base;
        const ts = _context.typescript.module;
        const autoImportPositions = new WeakSet();
        return {
            ...base,
            resolveEmbeddedRange(range) {
                if (autoImportPositions.has(range.start) && autoImportPositions.has(range.end))
                    return range;
            },
            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 && _context.typescript) {
                                    casing ??= await (0, nameCasing_1.getNameCasing)(_context, _context.typescript, _context.fileNameToUri(source.fileName));
                                    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
                        && _context.typescript
                        && item.textEdit?.newText.endsWith(suffix)
                        && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0
                        && (await _context.configurationHost?.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)(_context, _context.typescript, _context.fileNameToUri(source.fileName));
                            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/johnsoncodehk/volar/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) {
                    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 = vscode.Range.create(textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.start), textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.end));
                                        autoImportPositions.add(editRange.start);
                                        autoImportPositions.add(editRange.end);
                                        item.additionalTextEdits.push(vscode.TextEdit.replace(editRange, 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 = vscode.Range.create(textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.start), textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.end));
                                        autoImportPositions.add(editRange.start);
                                        autoImportPositions.add(editRange.end);
                                        item.additionalTextEdits.push(vscode.TextEdit.replace(editRange, unescape(printText.replace(/\\u/g, '%u'))));
                                    }
                                }
                            }
                        }
                    }
                }
                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 (vscode.TextDocumentEdit.is(documentChange)) {
                            patchAdditionalTextEdits(documentChange.textDocument.uri, documentChange.edits);
                        }
                    }
                }
                return result;
            },
        };
    };
    plugins.html ??= (0, vue_template_1.default)({
        templateLanguagePlugin: (0, html_1.default)(),
        getScanner: (document, htmlPlugin) => {
            return htmlPlugin.getHtmlLs().createScanner(document.getText());
        },
        isSupportedDocument: (document) => document.languageId === 'html',
        vueCompilerOptions,
    });
    plugins.pug ??= (0, vue_template_1.default)({
        templateLanguagePlugin: (0, pug_1.default)(),
        getScanner: (document, pugPlugin) => {
            const pugDocument = pugPlugin.getPugDocument(document);
            if (pugDocument) {
                return pugPlugin.getPugLs().createScanner(pugDocument);
            }
        },
        isSupportedDocument: (document) => document.languageId === 'jade',
        vueCompilerOptions,
    });
    plugins.vue ??= (0, vue_1.default)();
    plugins.css ??= (0, css_1.default)();
    plugins['pug-beautify'] ??= (0, pug_beautify_1.default)();
    plugins.json ??= (0, json_1.default)(settings?.json);
    plugins['typescript/twoslash-queries'] ??= (0, typescript_twoslash_queries_1.default)();
    plugins['vue/referencesCodeLens'] ??= (0, vue_codelens_references_1.default)();
    plugins['vue/autoInsertDotValue'] ??= (0, vue_autoinsert_dotvalue_1.default)();
    plugins['vue/twoslash-queries'] ??= (0, vue_twoslash_queries_1.default)();
    plugins['vue/autoInsertParentheses'] ??= (0, vue_autoinsert_parentheses_1.default)();
    plugins['vue/autoInsertSpaces'] ??= (0, vue_autoinsert_space_1.default)();
    plugins['vue/visualizeHiddenCallbackParam'] ??= (0, vue_visualize_hidden_callback_param_1.default)();
    plugins.emmet ??= (0, emmet_1.default)();
    return plugins;
}
// fix https://github.com/johnsoncodehk/volar/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