"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.register = register;
const language_service_1 = require("@volar/language-service");
const vscode = require("vscode-languageserver");
const vscode_uri_1 = require("vscode-uri");
const protocol_1 = require("../../protocol");
const reportedCapabilities = new Set();
function register(server, documents, configurations) {
    configurations.onDidChange(() => requestRefresh(false));
    // Diagnostics support
    let refreshReq = 0;
    let updateDiagnosticsBatchReq = 0;
    const refreshHandlers = [];
    server.onInitialize(serverCapabilities => {
        let lastCompleteUri;
        let lastCompleteLs;
        let lastCodeLensLs;
        let lastCodeActionLs;
        let lastCallHierarchyLs;
        let lastTypeHierarchyLs;
        let lastDocumentLinkLs;
        let lastInlayHintLs;
        let languageServiceToId = new WeakMap();
        let currentLanguageServiceId = 0;
        const languageServiceById = new Map();
        const { languageServicePlugins, project, initializeParams } = server;
        if (languageServicePlugins.some(({ capabilities }) => capabilities.selectionRangeProvider)) {
            serverCapabilities.selectionRangeProvider = true;
            server.connection.onSelectionRanges(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getSelectionRanges(uri, params.positions, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.foldingRangeProvider)) {
            serverCapabilities.foldingRangeProvider = true;
            server.connection.onFoldingRanges(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getFoldingRanges(uri, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.linkedEditingRangeProvider)) {
            serverCapabilities.linkedEditingRangeProvider = true;
            server.connection.languages.onLinkedEditingRange(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getLinkedEditingRanges(uri, params.position, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.colorProvider)) {
            serverCapabilities.colorProvider = true;
            server.connection.onDocumentColor(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentColors(uri, token);
                });
            });
            server.connection.onColorPresentation(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getColorPresentations(uri, params.color, params.range, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentSymbolProvider)) {
            serverCapabilities.documentSymbolProvider = true;
            server.connection.onDocumentSymbol(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentSymbols(uri, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentFormattingProvider)) {
            serverCapabilities.documentFormattingProvider = true;
            serverCapabilities.documentRangeFormattingProvider = true;
            server.connection.onDocumentFormatting(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentFormattingEdits(uri, params.options, undefined, undefined, token);
                });
            });
            server.connection.onDocumentRangeFormatting(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentFormattingEdits(uri, params.options, params.range, undefined, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.referencesProvider)) {
            serverCapabilities.referencesProvider = true;
            server.connection.onReferences(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getReferences(uri, params.position, { includeDeclaration: true }, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.implementationProvider)) {
            serverCapabilities.implementationProvider = true;
            server.connection.onImplementation(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    const definitions = await languageService.getImplementations(uri, params.position, token);
                    return handleDefinitions(initializeParams, 'implementation', definitions ?? []);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.declarationProvider)) {
            serverCapabilities.declarationProvider = true;
            server.connection.onDeclaration(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    const definitions = await languageService.getDeclaration(uri, params.position, token);
                    return handleDefinitions(initializeParams, 'declaration', definitions ?? []);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.definitionProvider)) {
            serverCapabilities.definitionProvider = true;
            server.connection.onDefinition(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    const definitions = await languageService.getDefinition(uri, params.position, token);
                    return handleDefinitions(initializeParams, 'definition', definitions ?? []);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.typeDefinitionProvider)) {
            serverCapabilities.typeDefinitionProvider = true;
            server.connection.onTypeDefinition(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    const definitions = await languageService.getTypeDefinition(uri, params.position, token);
                    return handleDefinitions(initializeParams, 'typeDefinition', definitions ?? []);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.callHierarchyProvider)) {
            serverCapabilities.callHierarchyProvider = true;
            server.connection.languages.callHierarchy.onPrepare(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    lastCallHierarchyLs = languageService;
                    return languageService.getCallHierarchyItems(uri, params.position, token);
                }) ?? [];
            });
            server.connection.languages.callHierarchy.onIncomingCalls(async (params, token) => {
                return await lastCallHierarchyLs?.getCallHierarchyIncomingCalls(params.item, token) ?? [];
            });
            server.connection.languages.callHierarchy.onOutgoingCalls(async (params, token) => {
                return await lastCallHierarchyLs?.getCallHierarchyOutgoingCalls(params.item, token) ?? [];
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.typeHierarchyProvider)) {
            serverCapabilities.typeHierarchyProvider = true;
            server.connection.languages.typeHierarchy.onPrepare(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    lastTypeHierarchyLs = languageService;
                    return languageService.getTypeHierarchyItems(uri, params.position, token);
                }) ?? [];
            });
            server.connection.languages.typeHierarchy.onSupertypes(async (params, token) => {
                return await lastTypeHierarchyLs?.getTypeHierarchySupertypes(params.item, token) ?? [];
            });
            server.connection.languages.typeHierarchy.onSubtypes(async (params, token) => {
                return await lastTypeHierarchyLs?.getTypeHierarchySubtypes(params.item, token) ?? [];
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.hoverProvider)) {
            serverCapabilities.hoverProvider = true;
            server.connection.onHover(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getHover(uri, params.position, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentHighlightProvider)) {
            serverCapabilities.documentHighlightProvider = true;
            server.connection.onDocumentHighlight(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentHighlights(uri, params.position, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.workspaceSymbolProvider)) {
            serverCapabilities.workspaceSymbolProvider = {};
            server.connection.onWorkspaceSymbol(async (params, token) => {
                let languageServices = await project.getExistingLanguageServices();
                if (!languageServices.length) {
                    for (const document of documents.all()) {
                        await project.getLanguageService(vscode_uri_1.URI.parse(document.uri));
                    }
                    languageServices = await project.getExistingLanguageServices();
                }
                const symbols = [];
                for (const languageService of languageServices) {
                    if (token.isCancellationRequested) {
                        return;
                    }
                    let languageServiceId = languageServiceToId.get(languageService);
                    if (languageServiceId === undefined) {
                        languageServiceId = currentLanguageServiceId;
                        languageServiceToId.set(languageService, languageServiceId);
                        languageServiceById.set(languageServiceId, new WeakRef(languageService));
                    }
                    const languageServiceResult = await languageService.getWorkspaceSymbols(params.query, token);
                    for (const symbol of languageServiceResult) {
                        symbol.data = {
                            languageServiceId,
                            originalData: symbol.data,
                        };
                    }
                    symbols.push(...await languageService.getWorkspaceSymbols(params.query, token));
                }
                return symbols;
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.workspaceSymbolProvider?.resolveProvider)) {
                serverCapabilities.workspaceSymbolProvider.resolveProvider = true;
                server.connection.onWorkspaceSymbolResolve(async (symbol, token) => {
                    const languageServiceId = symbol.data?.languageServiceId;
                    const languageService = languageServiceById.get(languageServiceId)?.deref();
                    if (!languageService) {
                        return symbol;
                    }
                    symbol.data = symbol.data?.originalData;
                    return await languageService.resolveWorkspaceSymbol?.(symbol, token);
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.renameProvider)) {
            serverCapabilities.renameProvider = {};
            server.connection.onRenameRequest(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getRenameEdits(uri, params.position, params.newName, token);
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.renameProvider?.prepareProvider)) {
                serverCapabilities.renameProvider.prepareProvider = true;
                server.connection.onPrepareRename(async (params, token) => {
                    const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                    return await worker(uri, token, async (languageService) => {
                        const result = await languageService.getRenameRange(uri, params.position, token);
                        if (result && 'message' in result) {
                            return new vscode.ResponseError(0, result.message);
                        }
                        return result;
                    });
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentLinkProvider)) {
            serverCapabilities.documentLinkProvider = {};
            server.connection.onDocumentLinks(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    lastDocumentLinkLs = languageService;
                    return languageService.getDocumentLinks(uri, token);
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.documentLinkProvider?.resolveProvider)) {
                serverCapabilities.documentLinkProvider.resolveProvider = true;
                server.connection.onDocumentLinkResolve(async (link, token) => {
                    return await lastDocumentLinkLs?.resolveDocumentLink(link, token);
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.codeLensProvider)) {
            serverCapabilities.codeLensProvider = {};
            server.connection.onCodeLens(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    lastCodeLensLs = languageService;
                    return languageService.getCodeLenses(uri, token);
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.codeLensProvider?.resolveProvider)) {
                serverCapabilities.codeLensProvider.resolveProvider = true;
                server.connection.onCodeLensResolve(async (codeLens, token) => {
                    return await lastCodeLensLs?.resolveCodeLens(codeLens, token) ?? codeLens;
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.inlayHintProvider)) {
            serverCapabilities.inlayHintProvider = {};
            server.connection.languages.inlayHint.on(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    lastInlayHintLs = languageService;
                    return languageService.getInlayHints(uri, params.range, token);
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.inlayHintProvider?.resolveProvider)) {
                serverCapabilities.inlayHintProvider.resolveProvider = true;
                server.connection.languages.inlayHint.resolve(async (hint, token) => {
                    return await lastInlayHintLs?.resolveInlayHint(hint, token) ?? hint;
                });
            }
            refreshHandlers.push(() => {
                if (initializeParams.capabilities.workspace?.inlayHint?.refreshSupport) {
                    server.connection.languages.inlayHint.refresh();
                }
                else {
                    wranCapabilitiesNotSupported('workspace.inlayHint.refreshSupport');
                }
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.signatureHelpProvider)) {
            serverCapabilities.signatureHelpProvider = {
                triggerCharacters: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.signatureHelpProvider?.triggerCharacters ?? []).flat()),
                ],
                retriggerCharacters: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.signatureHelpProvider?.retriggerCharacters ?? []).flat()),
                ],
            };
            server.connection.onSignatureHelp(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getSignatureHelp(uri, params.position, params.context, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.completionProvider)) {
            serverCapabilities.completionProvider = {
                triggerCharacters: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.completionProvider?.triggerCharacters ?? [])
                        .flat()),
                ],
            };
            server.connection.onCompletion(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    lastCompleteUri = params.textDocument.uri;
                    lastCompleteLs = languageService;
                    const list = await languageService.getCompletionItems(uri, params.position, params.context, token);
                    list.items = list.items.map(item => handleCompletionItem(initializeParams, item));
                    return list;
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.completionProvider?.resolveProvider)) {
                serverCapabilities.completionProvider.resolveProvider = true;
                server.connection.onCompletionResolve(async (item, token) => {
                    if (lastCompleteUri && lastCompleteLs) {
                        item = await lastCompleteLs.resolveCompletionItem(item, token);
                        item = handleCompletionItem(initializeParams, item);
                    }
                    return item;
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.semanticTokensProvider)) {
            serverCapabilities.semanticTokensProvider = {
                full: true,
                range: true,
                legend: {
                    tokenTypes: [
                        ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.semanticTokensProvider?.legend?.tokenTypes ?? []).flat()),
                    ],
                    tokenModifiers: [
                        ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.semanticTokensProvider?.legend?.tokenModifiers ?? []).flat()),
                    ],
                },
            };
            server.connection.languages.semanticTokens.on(async (params, token, _, resultProgress) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    return await languageService?.getSemanticTokens(uri, undefined, serverCapabilities.semanticTokensProvider.legend, tokens => resultProgress?.report(tokens), token);
                }) ?? { data: [] };
            });
            server.connection.languages.semanticTokens.onRange(async (params, token, _, resultProgress) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    return await languageService?.getSemanticTokens(uri, params.range, serverCapabilities.semanticTokensProvider.legend, tokens => resultProgress?.report(tokens), token);
                }) ?? { data: [] };
            });
            refreshHandlers.push(() => {
                if (initializeParams.capabilities.workspace?.semanticTokens?.refreshSupport) {
                    server.connection.languages.semanticTokens.refresh();
                }
                else {
                    wranCapabilitiesNotSupported('workspace.semanticTokens.refreshSupport');
                }
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.codeActionProvider)) {
            serverCapabilities.codeActionProvider = {
                codeActionKinds: languageServicePlugins.some(({ capabilities }) => capabilities.codeActionProvider?.codeActionKinds)
                    ? [
                        ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.codeActionProvider?.codeActionKinds ?? [])
                            .flat()),
                    ]
                    : undefined,
            };
            server.connection.onCodeAction(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, async (languageService) => {
                    lastCodeActionLs = languageService;
                    let codeActions = await languageService.getCodeActions(uri, params.range, params.context, token) ?? [];
                    for (const codeAction of codeActions) {
                        if (codeAction.data && typeof codeAction.data === 'object') {
                            codeAction.data.uri = params.textDocument.uri;
                        }
                        else {
                            codeAction.data = { uri: params.textDocument.uri };
                        }
                    }
                    if (!initializeParams.capabilities.textDocument?.codeAction?.disabledSupport
                        && codeActions.some(codeAction => !codeAction.disabled)) {
                        codeActions = codeActions.filter(codeAction => !codeAction.disabled);
                        wranCapabilitiesNotSupported('textDocument.codeAction.disabledSupport');
                    }
                    return codeActions;
                });
            });
            if (languageServicePlugins.some(({ capabilities }) => capabilities.codeActionProvider?.resolveProvider)) {
                serverCapabilities.codeActionProvider.resolveProvider = true;
                server.connection.onCodeActionResolve(async (codeAction, token) => {
                    return await lastCodeActionLs?.resolveCodeAction(codeAction, token) ?? codeAction;
                });
            }
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentOnTypeFormattingProvider)) {
            serverCapabilities.documentOnTypeFormattingProvider = {
                firstTriggerCharacter: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.documentOnTypeFormattingProvider?.triggerCharacters ?? []).flat()),
                ][0],
                moreTriggerCharacter: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.documentOnTypeFormattingProvider?.triggerCharacters ?? []).flat()),
                ].slice(1),
            };
            server.connection.onDocumentOnTypeFormatting(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getDocumentFormattingEdits(uri, params.options, undefined, params, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.executeCommandProvider)) {
            serverCapabilities.executeCommandProvider = {
                commands: [
                    ...new Set(languageServicePlugins.map(({ capabilities }) => capabilities.executeCommandProvider?.commands ?? [])
                        .flat()),
                ],
            };
            server.connection.onExecuteCommand(async (params, token) => {
                let languageServices = await project.getExistingLanguageServices();
                if (!languageServices.length) {
                    for (const document of documents.all()) {
                        await project.getLanguageService(vscode_uri_1.URI.parse(document.uri));
                    }
                    languageServices = await project.getExistingLanguageServices();
                }
                for (const languageService of languageServices) {
                    if (languageService.executeCommand && languageService.commands.includes(params.command)) {
                        try {
                            return await languageService.executeCommand(params.command, params.arguments ?? [], token);
                        }
                        catch { }
                    }
                }
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.monikerProvider)) {
            serverCapabilities.monikerProvider = true;
            server.connection.languages.moniker.on(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getMoniker(uri, params.position, token);
                }) ?? null;
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.inlineValueProvider)) {
            serverCapabilities.inlineValueProvider = true;
            server.connection.languages.inlineValue.on(async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getInlineValue(uri, params.range, params.context, token);
                });
            });
            refreshHandlers.push(() => {
                if (initializeParams.capabilities.workspace?.inlineValue?.refreshSupport) {
                    server.connection.languages.inlineValue.refresh();
                }
                else {
                    wranCapabilitiesNotSupported('workspace.inlineValue.refreshSupport');
                }
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.autoInsertionProvider)) {
            const triggerCharacterToConfigurationSections = new Map();
            const tryAdd = (char, section) => {
                let sectionSet = triggerCharacterToConfigurationSections.get(char);
                if (!sectionSet) {
                    triggerCharacterToConfigurationSections.set(char, sectionSet = new Set());
                }
                if (section) {
                    sectionSet.add(section);
                }
            };
            for (const { capabilities } of languageServicePlugins) {
                if (capabilities.autoInsertionProvider) {
                    const { triggerCharacters, configurationSections } = capabilities.autoInsertionProvider;
                    if (configurationSections) {
                        if (configurationSections.length !== triggerCharacters.length) {
                            throw new Error('configurationSections.length !== triggerCharacters.length');
                        }
                        for (let i = 0; i < configurationSections.length; i++) {
                            tryAdd(triggerCharacters[i], configurationSections[i]);
                        }
                    }
                    else {
                        for (const char of triggerCharacters) {
                            tryAdd(char);
                        }
                    }
                }
            }
            serverCapabilities.experimental ??= {};
            serverCapabilities.experimental.autoInsertionProvider = {
                triggerCharacters: [],
                configurationSections: [],
            };
            for (const [char, sections] of triggerCharacterToConfigurationSections) {
                if (sections.size) {
                    serverCapabilities.experimental.autoInsertionProvider.triggerCharacters.push(char);
                    serverCapabilities.experimental.autoInsertionProvider.configurationSections.push([...sections]);
                }
                else {
                    serverCapabilities.experimental.autoInsertionProvider.triggerCharacters.push(char);
                    serverCapabilities.experimental.autoInsertionProvider.configurationSections.push(null);
                }
            }
            server.connection.onRequest(protocol_1.AutoInsertRequest.type, async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getAutoInsertSnippet(uri, params.selection, params.change, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.fileRenameEditsProvider)) {
            serverCapabilities.experimental ??= {};
            serverCapabilities.experimental.fileRenameEditsProvider = true;
            server.connection.workspace.onWillRenameFiles(async (params, token) => {
                const _edits = await Promise.all(params.files.map(async (file) => {
                    const oldUri = vscode_uri_1.URI.parse(file.oldUri);
                    const newUri = vscode_uri_1.URI.parse(file.newUri);
                    return await worker(oldUri, token, languageService => {
                        return languageService.getFileRenameEdits(oldUri, newUri, token) ?? null;
                    }) ?? null;
                }));
                const edits = _edits.filter((edit) => !!edit);
                if (edits.length) {
                    (0, language_service_1.mergeWorkspaceEdits)(edits[0], ...edits.slice(1));
                    return edits[0];
                }
                return null;
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.fileReferencesProvider)) {
            serverCapabilities.experimental ??= {};
            serverCapabilities.experimental.fileReferencesProvider = true;
            server.connection.onRequest(protocol_1.FindFileReferenceRequest.type, async (params, token) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                return await worker(uri, token, languageService => {
                    return languageService.getFileReferences(uri, token);
                });
            });
        }
        if (languageServicePlugins.some(({ capabilities }) => capabilities.documentDropEditsProvider)) {
            serverCapabilities.experimental ??= {};
            serverCapabilities.experimental.documentDropEditsProvider = true;
            server.connection.onRequest(protocol_1.DocumentDropRequest.type, async ({ textDocument, position, dataTransfer }, token) => {
                const dataTransferMap = new Map();
                for (const item of dataTransfer) {
                    dataTransferMap.set(item.mimeType, {
                        value: item.value,
                        asString() {
                            return server.connection.sendRequest(protocol_1.DocumentDrop_DataTransferItemAsStringRequest.type, {
                                mimeType: item.mimeType,
                            });
                        },
                        asFile() {
                            if (item.file) {
                                return {
                                    name: item.file.name,
                                    uri: item.file.uri,
                                    data() {
                                        return server.connection.sendRequest(protocol_1.DocumentDrop_DataTransferItemFileDataRequest.type, {
                                            mimeType: item.mimeType,
                                        });
                                    },
                                };
                            }
                        },
                    });
                }
                const uri = vscode_uri_1.URI.parse(textDocument.uri);
                const languageService = await project.getLanguageService(uri);
                return languageService.getDocumentDropEdits(uri, position, dataTransferMap, token);
            });
        }
        // Diagnostic support
        const supportsDiagnosticPull = !!initializeParams.capabilities.workspace?.diagnostics;
        const diagnosticProvider = languageServicePlugins.some(({ capabilities }) => !!capabilities.diagnosticProvider);
        const interFileDependencies = languageServicePlugins.some(({ capabilities }) => capabilities.diagnosticProvider?.interFileDependencies);
        const workspaceDiagnostics = languageServicePlugins.some(({ capabilities }) => capabilities.diagnosticProvider?.workspaceDiagnostics);
        if (diagnosticProvider) {
            if (supportsDiagnosticPull && !interFileDependencies) {
                serverCapabilities.diagnosticProvider = {
                    // Unreliable, see https://github.com/microsoft/vscode-languageserver-node/issues/848#issuecomment-2189521060
                    interFileDependencies: false,
                    workspaceDiagnostics,
                };
                refreshHandlers.push(() => {
                    if (initializeParams.capabilities.workspace?.diagnostics?.refreshSupport) {
                        server.connection.languages.diagnostics.refresh();
                    }
                    else {
                        wranCapabilitiesNotSupported('workspace.diagnostics.refreshSupport');
                    }
                });
            }
            else {
                documents.onDidChangeContent(({ document }) => {
                    const changedDocument = documents.get(vscode_uri_1.URI.parse(document.uri));
                    if (!changedDocument) {
                        return;
                    }
                    if (interFileDependencies) {
                        const remainingDocuments = [...documents.all()].filter(doc => doc !== changedDocument);
                        updateDiagnosticsBatch(project, [changedDocument, ...remainingDocuments]);
                    }
                    else {
                        updateDiagnosticsBatch(project, [changedDocument]);
                    }
                });
                documents.onDidClose(({ document }) => {
                    server.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
                });
                refreshHandlers.push(async (clearDiagnostics) => {
                    if (clearDiagnostics) {
                        for (const document of documents.all()) {
                            server.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
                        }
                    }
                    await updateDiagnosticsBatch(project, [...documents.all()]);
                });
            }
            server.connection.languages.diagnostics.on(async (params, token, _workDoneProgressReporter, resultProgressReporter) => {
                const uri = vscode_uri_1.URI.parse(params.textDocument.uri);
                const result = await worker(uri, token, languageService => {
                    return languageService.getDiagnostics(uri, errors => {
                        // resultProgressReporter is undefined in vscode
                        resultProgressReporter?.report({
                            relatedDocuments: {
                                [params.textDocument.uri]: {
                                    kind: vscode.DocumentDiagnosticReportKind.Full,
                                    items: errors,
                                },
                            },
                        });
                    }, token);
                });
                return {
                    kind: vscode.DocumentDiagnosticReportKind.Full,
                    items: result ?? [],
                };
            });
        }
        if (workspaceDiagnostics) {
            server.connection.languages.diagnostics.onWorkspace(async (_params, token) => {
                let languageServices = await project.getExistingLanguageServices();
                if (!languageServices.length) {
                    for (const document of documents.all()) {
                        await project.getLanguageService(vscode_uri_1.URI.parse(document.uri));
                    }
                    languageServices = await project.getExistingLanguageServices();
                }
                const items = [];
                for (const languageService of languageServices) {
                    if (token.isCancellationRequested) {
                        break;
                    }
                    const result = await languageService.getWorkspaceDiagnostics(token);
                    items.push(...result);
                }
                return { items };
            });
        }
    });
    return { requestRefresh };
    async function requestRefresh(clearDiagnostics) {
        const req = ++refreshReq;
        const delay = 250;
        await sleep(delay);
        if (req !== refreshReq) {
            return;
        }
        for (const handler of refreshHandlers) {
            handler(clearDiagnostics);
        }
    }
    async function updateDiagnosticsBatch(project, documents) {
        const req = ++updateDiagnosticsBatchReq;
        const delay = 250;
        const token = {
            get isCancellationRequested() {
                return req !== updateDiagnosticsBatchReq;
            },
            onCancellationRequested: vscode.Event.None,
        };
        for (const doc of documents) {
            await sleep(delay);
            if (token.isCancellationRequested) {
                break;
            }
            await updateDiagnostics(project, vscode_uri_1.URI.parse(doc.uri), doc.version, token);
        }
    }
    async function updateDiagnostics(project, uri, version, token) {
        const languageService = await project.getLanguageService(uri);
        const diagnostics = await languageService.getDiagnostics(uri, diagnostics => server.connection.sendDiagnostics({ uri: uri.toString(), diagnostics, version }), token);
        if (!token.isCancellationRequested) {
            server.connection.sendDiagnostics({ uri: uri.toString(), diagnostics, version });
        }
    }
    function worker(uri, token, cb) {
        return new Promise(resolve => {
            server.env.timer.setImmediate(async () => {
                if (token.isCancellationRequested) {
                    resolve(undefined);
                    return;
                }
                const languageService = await server.project.getLanguageService((0, language_service_1.decodeEmbeddedDocumentUri)(uri)?.[0] ?? uri);
                const result = await cb(languageService);
                if (token.isCancellationRequested) {
                    resolve(undefined);
                    return;
                }
                resolve(result);
            });
        });
    }
    function handleCompletionItem(initializeParams, item) {
        const snippetSupport = initializeParams.capabilities.textDocument?.completion?.completionItem?.snippetSupport
            ?? false;
        const insertReplaceSupport = initializeParams.capabilities.textDocument?.completion?.completionItem?.insertReplaceSupport ?? false;
        if (!snippetSupport && item.insertTextFormat === vscode.InsertTextFormat.Snippet) {
            item.insertTextFormat = vscode.InsertTextFormat.PlainText;
            if (item.insertText) {
                item.insertText = item.insertText.replace(/\$\d+/g, '');
                item.insertText = item.insertText.replace(/\${\d+:([^}]*)}/g, '');
            }
            wranCapabilitiesNotSupported('textDocument.completion.completionItem.snippetSupport');
        }
        if (!insertReplaceSupport && item.textEdit && vscode.InsertReplaceEdit.is(item.textEdit)) {
            item.textEdit = vscode.TextEdit.replace(item.textEdit.insert, item.textEdit.newText);
            wranCapabilitiesNotSupported('textDocument.completion.completionItem.insertReplaceSupport');
        }
        return item;
    }
    function handleDefinitions(initializeParams, type, items) {
        const linkSupport = initializeParams.capabilities.textDocument?.[type]?.linkSupport ?? false;
        if (!linkSupport) {
            wranCapabilitiesNotSupported(`textDocument.${type}.linkSupport`);
            return items.map(item => ({
                uri: item.targetUri,
                range: item.targetRange,
            }));
        }
        return items;
    }
}
function wranCapabilitiesNotSupported(path) {
    if (reportedCapabilities.has(path)) {
        return;
    }
    reportedCapabilities.add(path);
    console.warn(`${path} is not supported by the client but could be used by the server.`);
}
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
//# sourceMappingURL=languageFeatures.js.map