"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sleep = exports.register = void 0;
const embedded = require("@volar/language-service");
const vscode = require("vscode-languageserver");
const protocol_1 = require("../../protocol");
const types_1 = require("../../types");
function register(connection, projects, initParams, initOptions, cancelHost, semanticTokensLegend, runtime, documents) {
    let lastCompleteUri;
    let lastCompleteLs;
    let lastCodeLensLs;
    let lastCodeActionLs;
    let lastCallHierarchyLs;
    let lastDocumentLinkLs;
    let lastInlayHintLs;
    connection.onDocumentFormatting(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.format(params.textDocument.uri, params.options, undefined, undefined, token);
        });
    });
    connection.onDocumentRangeFormatting(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.format(params.textDocument.uri, params.options, params.range, undefined, token);
        });
    });
    connection.onDocumentOnTypeFormatting(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.format(params.textDocument.uri, params.options, undefined, params, token);
        });
    });
    connection.onSelectionRanges(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.getSelectionRanges(params.textDocument.uri, params.positions, token);
        });
    });
    connection.onFoldingRanges(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.getFoldingRanges(params.textDocument.uri, token);
        });
    });
    connection.languages.onLinkedEditingRange(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findLinkedEditingRanges(params.textDocument.uri, params.position, token);
        });
    });
    connection.onDocumentSymbol(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findDocumentSymbols(params.textDocument.uri, token);
        });
    });
    connection.onDocumentColor(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findDocumentColors(params.textDocument.uri, token);
        });
    });
    connection.onColorPresentation(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.getColorPresentations(params.textDocument.uri, params.color, params.range, token);
        });
    });
    connection.onCompletion(async (params, token) => {
        return worker(params.textDocument.uri, token, async (service) => {
            lastCompleteUri = params.textDocument.uri;
            lastCompleteLs = service;
            const document = documents.data.uriGet(params.textDocument.uri)?.getDocument();
            const list = await service.doComplete(params.textDocument.uri, params.position, params.context, token);
            for (const item of list.items) {
                fixTextEdit(item);
            }
            if (!initOptions.fullCompletionList && document) {
                list.items = list.items.filter(item => {
                    const range = item.textEdit ? (vscode.InsertReplaceEdit.is(item.textEdit)
                        ? item.textEdit.replace
                        : item.textEdit.range) : list.itemDefaults?.editRange ? (vscode.Range.is(list.itemDefaults.editRange)
                        ? list.itemDefaults.editRange
                        : list.itemDefaults.editRange.replace) : undefined;
                    if (range) {
                        const sourceText = document.getText(range).toLowerCase();
                        if (sourceText.trim()) {
                            let filterText = (item.filterText ?? item.label).toLowerCase();
                            for (const char of sourceText) {
                                const index = filterText.indexOf(char);
                                if (index === -1) {
                                    return false;
                                }
                                filterText = filterText.slice(index + 1);
                            }
                        }
                    }
                    return true;
                });
            }
            return list;
        });
    });
    connection.onCompletionResolve(async (item, token) => {
        if (lastCompleteUri && lastCompleteLs) {
            item = await lastCompleteLs.doCompletionResolve(item, token);
            fixTextEdit(item);
        }
        return item;
    });
    connection.onHover(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.doHover(params.textDocument.uri, params.position, token);
        });
    });
    connection.onSignatureHelp(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.getSignatureHelp(params.textDocument.uri, params.position, params.context, token);
        });
    });
    connection.onPrepareRename(async (params, token) => {
        return worker(params.textDocument.uri, token, async (service) => {
            const result = await service.prepareRename(params.textDocument.uri, params.position, token);
            if (result && 'message' in result) {
                return new vscode.ResponseError(0, result.message);
            }
            return result;
        });
    });
    connection.onRenameRequest(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.doRename(params.textDocument.uri, params.position, params.newName, token);
        });
    });
    connection.onCodeLens(async (params, token) => {
        return worker(params.textDocument.uri, token, async (service) => {
            lastCodeLensLs = service;
            return service.doCodeLens(params.textDocument.uri, token);
        });
    });
    connection.onCodeLensResolve(async (codeLens, token) => {
        return await lastCodeLensLs?.doCodeLensResolve(codeLens, token) ?? codeLens;
    });
    connection.onCodeAction(async (params, token) => {
        return worker(params.textDocument.uri, token, async (service) => {
            lastCodeActionLs = service;
            let codeActions = await service.doCodeActions(params.textDocument.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 (!initParams.capabilities.textDocument?.codeAction?.disabledSupport) {
                codeActions = codeActions.filter(codeAction => !codeAction.disabled);
            }
            return codeActions;
        });
    });
    connection.onCodeActionResolve(async (codeAction, token) => {
        return await lastCodeActionLs.doCodeActionResolve(codeAction, token) ?? codeAction;
    });
    connection.onReferences(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findReferences(params.textDocument.uri, params.position, token);
        });
    });
    connection.onRequest(protocol_1.FindFileReferenceRequest.type, async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findFileReferences(params.textDocument.uri, token);
        });
    });
    connection.onImplementation(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findImplementations(params.textDocument.uri, params.position, token);
        });
    });
    connection.onDefinition(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findDefinition(params.textDocument.uri, params.position, token);
        });
    });
    connection.onTypeDefinition(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findTypeDefinition(params.textDocument.uri, params.position, token);
        });
    });
    connection.onDocumentHighlight(async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.findDocumentHighlights(params.textDocument.uri, params.position, token);
        });
    });
    connection.onDocumentLinks(async (params, token) => {
        return await worker(params.textDocument.uri, token, service => {
            lastDocumentLinkLs = service;
            return service.findDocumentLinks(params.textDocument.uri, token);
        });
    });
    connection.onDocumentLinkResolve(async (link, token) => {
        return await lastDocumentLinkLs.doDocumentLinkResolve(link, token);
    });
    connection.onWorkspaceSymbol(async (params, token) => {
        let results = [];
        for (const _workspace of projects.workspaces.values()) {
            const workspace = await _workspace;
            let projects = [...workspace.projects.values()];
            projects = projects.length ? projects : [workspace.getInferredProject()];
            for (const project of projects) {
                if (token.isCancellationRequested)
                    return;
                const service = (await project).getLanguageService();
                results = results.concat(await service.findWorkspaceSymbols(params.query, token));
            }
        }
        return results;
    });
    connection.languages.callHierarchy.onPrepare(async (params, token) => {
        return await worker(params.textDocument.uri, token, async (service) => {
            lastCallHierarchyLs = service;
            return service.callHierarchy.doPrepare(params.textDocument.uri, params.position, token);
        }) ?? [];
    });
    connection.languages.callHierarchy.onIncomingCalls(async (params, token) => {
        return await lastCallHierarchyLs?.callHierarchy.getIncomingCalls(params.item, token) ?? [];
    });
    connection.languages.callHierarchy.onOutgoingCalls(async (params, token) => {
        return await lastCallHierarchyLs?.callHierarchy.getOutgoingCalls(params.item, token) ?? [];
    });
    connection.languages.semanticTokens.on(async (params, token, _, resultProgress) => {
        await sleep(200);
        return await worker(params.textDocument.uri, token, async (service) => {
            return await service?.getSemanticTokens(params.textDocument.uri, undefined, semanticTokensLegend, token, tokens => resultProgress?.report(tokens));
        }) ?? { data: [] };
    });
    connection.languages.semanticTokens.onRange(async (params, token, _, resultProgress) => {
        await sleep(200);
        return await worker(params.textDocument.uri, token, async (service) => {
            return await service?.getSemanticTokens(params.textDocument.uri, params.range, semanticTokensLegend, token, tokens => resultProgress?.report(tokens));
        }) ?? { data: [] };
    });
    connection.languages.diagnostics.on(async (params, token, _workDoneProgressReporter, resultProgressReporter) => {
        const result = await worker(params.textDocument.uri, token, service => {
            const tsToken = cancelHost.createCancellationToken(token);
            const mode = initOptions.serverMode === types_1.ServerMode.PartialSemantic ? 'semantic'
                : initOptions.serverMode === types_1.ServerMode.Syntactic ? 'syntactic'
                    : 'all';
            return service.doValidation(params.textDocument.uri, mode, tsToken, errors => {
                // resultProgressReporter is undefined in vscode
                resultProgressReporter?.report({
                    relatedDocuments: {
                        [params.textDocument.uri]: {
                            kind: vscode.DocumentDiagnosticReportKind.Full,
                            items: errors,
                        },
                    },
                });
            });
        });
        return {
            kind: vscode.DocumentDiagnosticReportKind.Full,
            items: result ?? [],
        };
    });
    connection.languages.inlayHint.on(async (params, token) => {
        return worker(params.textDocument.uri, token, async (service) => {
            lastInlayHintLs = service;
            return service.getInlayHints(params.textDocument.uri, params.range, token);
        });
    });
    connection.languages.inlayHint.resolve(async (hint, token) => {
        return await lastInlayHintLs.doInlayHintResolve(hint, token);
    });
    connection.workspace.onWillRenameFiles(async (params, token) => {
        const _edits = await Promise.all(params.files.map(async (file) => {
            return await worker(file.oldUri, token, service => {
                return service.getEditsForFileRename(file.oldUri, file.newUri, token) ?? null;
            }) ?? null;
        }));
        const edits = _edits.filter((edit) => !!edit);
        if (edits.length) {
            embedded.mergeWorkspaceEdits(edits[0], ...edits.slice(1));
            return edits[0];
        }
        return null;
    });
    connection.onRequest(protocol_1.AutoInsertRequest.type, async (params, token) => {
        return worker(params.textDocument.uri, token, service => {
            return service.doAutoInsert(params.textDocument.uri, params.position, params.options, token);
        });
    });
    function worker(uri, token, cb) {
        return new Promise(resolve => {
            runtime.timer.setImmediate(async () => {
                if (token.isCancellationRequested) {
                    resolve(undefined);
                    return;
                }
                const project = await getProject(uri);
                if (project) {
                    const service = project.getLanguageService();
                    try { // handle TS cancel throw
                        const result = await cb(service);
                        if (token.isCancellationRequested) {
                            resolve(undefined);
                            return;
                        }
                        resolve(result);
                    }
                    catch {
                        resolve(undefined);
                        return;
                    }
                }
                else {
                    resolve(undefined);
                }
            });
        });
    }
    async function getProject(uri) {
        return (await projects.getProject(uri))?.project;
    }
    function fixTextEdit(item) {
        const insertReplaceSupport = initParams.capabilities.textDocument?.completion?.completionItem?.insertReplaceSupport ?? false;
        if (!insertReplaceSupport) {
            if (item.textEdit && vscode.InsertReplaceEdit.is(item.textEdit)) {
                item.textEdit = vscode.TextEdit.replace(item.textEdit.insert, item.textEdit.newText);
            }
        }
    }
}
exports.register = register;
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
exports.sleep = sleep;
//# sourceMappingURL=languageFeatures.js.map