"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startLanguageServer = startLanguageServer;
exports.printSnapshots = printSnapshots;
exports.printSnapshot = printSnapshot;
const language_core_1 = require("@volar/language-core");
const _ = require("@volar/language-server/node");
const assert = require("assert");
const cp = require("child_process");
const fs = require("fs");
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const vscode_uri_1 = require("vscode-uri");
function startLanguageServer(serverModule, cwd) {
    const childProcess = cp.fork(serverModule, ['--stdio', `--clientProcessId=${process.pid.toString()}`], {
        execArgv: ['--nolazy'],
        env: process.env,
        cwd,
        stdio: 'pipe',
    });
    const connection = _.createProtocolConnection(childProcess.stdout, childProcess.stdin);
    const documentVersions = new Map();
    const openedDocuments = new Map();
    const settings = {};
    let untitledCounter = 0;
    let running = false;
    connection.listen();
    connection.onClose(e => console.log(e));
    connection.onUnhandledNotification(e => console.log(e));
    connection.onError(e => console.log(e));
    connection.onNotification(_.LogMessageNotification.type, e => {
        if (e.type === _.MessageType.Error || e.type === _.MessageType.Warning) {
            console.error(e.message);
        }
        else {
            console.log(e.message);
        }
    });
    connection.onDispose(() => {
        connection.end();
    });
    connection.onRequest(_.ConfigurationRequest.type, ({ items }) => {
        return items.map(item => {
            if (item.section) {
                return getConfiguration(item.section);
            }
        });
    });
    return {
        process: childProcess,
        connection,
        async initialize(rootUri, initializationOptions, capabilities = {}, locale) {
            const result = await connection.sendRequest(_.InitializeRequest.type, {
                processId: childProcess.pid ?? null,
                rootUri: typeof rootUri === 'string' ? rootUri : null,
                workspaceFolders: Array.isArray(rootUri) ? rootUri : null,
                initializationOptions,
                capabilities,
                locale,
            });
            await connection.sendNotification(_.InitializedNotification.type, {});
            running = true;
            return result;
        },
        async shutdown() {
            running = false;
            await connection.sendRequest(_.ShutdownRequest.type);
            openedDocuments.clear();
        },
        async openTextDocument(fileName, languageId) {
            const uri = vscode_uri_1.URI.file(fileName).toString();
            if (!openedDocuments.has(uri)) {
                const document = vscode_languageserver_textdocument_1.TextDocument.create(uri, languageId, (documentVersions.get(uri) ?? 0) + 1, fs.readFileSync(fileName, 'utf-8'));
                documentVersions.set(uri, document.version);
                openedDocuments.set(uri, document);
                await connection.sendNotification(_.DidOpenTextDocumentNotification.type, {
                    textDocument: {
                        uri,
                        languageId,
                        version: document.version,
                        text: document.getText(),
                    },
                });
            }
            return openedDocuments.get(uri);
        },
        async openUntitledDocument(languageId, content) {
            const uri = vscode_uri_1.URI.from({ scheme: 'untitled', path: `Untitled-${untitledCounter++}` }).toString();
            const document = vscode_languageserver_textdocument_1.TextDocument.create(uri, languageId, (documentVersions.get(uri) ?? 0) + 1, content);
            documentVersions.set(uri, document.version);
            openedDocuments.set(uri, document);
            await connection.sendNotification(_.DidOpenTextDocumentNotification.type, {
                textDocument: {
                    uri,
                    languageId,
                    version: document.version,
                    text: document.getText(),
                },
            });
            return document;
        },
        async openInMemoryDocument(uri, languageId, content) {
            const oldDocument = openedDocuments.get(uri);
            if (oldDocument) {
                await this.closeTextDocument(uri);
            }
            const document = vscode_languageserver_textdocument_1.TextDocument.create(uri, languageId, (documentVersions.get(uri) ?? 0) + 1, content);
            documentVersions.set(uri, document.version);
            openedDocuments.set(uri, document);
            await connection.sendNotification(_.DidOpenTextDocumentNotification.type, {
                textDocument: {
                    uri,
                    languageId,
                    version: document.version,
                    text: document.getText(),
                },
            });
            return document;
        },
        closeTextDocument(uri) {
            assert(openedDocuments.has(uri));
            openedDocuments.delete(uri);
            return connection.sendNotification(_.DidCloseTextDocumentNotification.type, {
                textDocument: { uri },
            });
        },
        async updateTextDocument(uri, edits) {
            let document = openedDocuments.get(uri);
            assert(document);
            const newText = vscode_languageserver_textdocument_1.TextDocument.applyEdits(document, edits);
            document = vscode_languageserver_textdocument_1.TextDocument.create(uri, document.languageId, document.version + 1, newText);
            documentVersions.set(uri, document.version);
            openedDocuments.set(uri, document);
            await connection.sendNotification(_.DidChangeTextDocumentNotification.type, {
                textDocument: {
                    uri: document.uri,
                    version: document.version,
                },
                contentChanges: [{ text: document.getText() }],
            });
            return document;
        },
        async updateConfiguration(newSettings) {
            Object.assign(settings, newSettings);
            if (running) {
                await connection.sendNotification(_.DidChangeConfigurationNotification.type, { settings });
            }
        },
        didChangeWatchedFiles(changes) {
            return connection.sendNotification(_.DidChangeWatchedFilesNotification.type, { changes });
        },
        async sendCompletionRequest(uri, position) {
            const result = await connection.sendRequest(_.CompletionRequest.type, {
                textDocument: { uri },
                position,
            });
            // @volar/language-server only returns CompletionList
            assert(!Array.isArray(result));
            return result;
        },
        sendCompletionResolveRequest(item) {
            return connection.sendRequest(_.CompletionResolveRequest.type, item);
        },
        sendDocumentDiagnosticRequest(uri) {
            return connection.sendRequest(_.DocumentDiagnosticRequest.type, {
                textDocument: { uri },
            });
        },
        sendHoverRequest(uri, position) {
            return connection.sendRequest(_.HoverRequest.type, {
                textDocument: { uri },
                position,
            });
        },
        sendDocumentFormattingRequest(uri, options) {
            return connection.sendRequest(_.DocumentFormattingRequest.type, {
                textDocument: { uri },
                options,
            });
        },
        sendDocumentRangeFormattingRequestRequest(uri, range, options) {
            return connection.sendRequest(_.DocumentRangeFormattingRequest.type, {
                textDocument: { uri },
                range,
                options,
            });
        },
        sendRenameRequest(uri, position, newName) {
            return connection.sendRequest(_.RenameRequest.type, {
                textDocument: { uri },
                position,
                newName,
            });
        },
        sendPrepareRenameRequest(uri, position) {
            return connection.sendRequest(_.PrepareRenameRequest.type, {
                textDocument: { uri },
                position,
            });
        },
        sendFoldingRangesRequest(uri) {
            return connection.sendRequest(_.FoldingRangeRequest.type, {
                textDocument: { uri },
            });
        },
        sendDocumentSymbolRequest(uri) {
            return connection.sendRequest(_.DocumentSymbolRequest.type, {
                textDocument: { uri },
            });
        },
        sendDocumentColorRequest(uri) {
            return connection.sendRequest(_.DocumentColorRequest.type, {
                textDocument: { uri },
            });
        },
        sendDefinitionRequest(uri, position) {
            return connection.sendRequest(_.DefinitionRequest.type, {
                textDocument: { uri },
                position,
            });
        },
        sendTypeDefinitionRequest(uri, position) {
            return connection.sendRequest(_.TypeDefinitionRequest.type, {
                textDocument: { uri },
                position,
            });
        },
        sendReferencesRequest(uri, position, context) {
            return connection.sendRequest(_.ReferencesRequest.type, {
                textDocument: { uri },
                position,
                context,
            });
        },
        sendSignatureHelpRequest(uri, position) {
            return connection.sendRequest(_.SignatureHelpRequest.type, {
                textDocument: { uri },
                position,
            });
        },
        sendSelectionRangesRequest(uri, positions) {
            return connection.sendRequest(_.SelectionRangeRequest.type, {
                textDocument: { uri },
                positions,
            });
        },
        sendCodeActionsRequest(uri, range, context) {
            return connection.sendRequest(_.CodeActionRequest.type, {
                textDocument: { uri },
                range,
                context,
            });
        },
        sendCodeActionResolveRequest(codeAction) {
            return connection.sendRequest(_.CodeActionResolveRequest.type, codeAction);
        },
        sendExecuteCommandRequest(command, args) {
            return connection.sendRequest(_.ExecuteCommandRequest.type, {
                command,
                arguments: args,
            });
        },
        sendSemanticTokensRequest(uri) {
            return connection.sendRequest(_.SemanticTokensRequest.type, {
                textDocument: { uri },
            });
        },
        sendSemanticTokensRangeRequest(uri, range) {
            return connection.sendRequest(_.SemanticTokensRangeRequest.type, {
                textDocument: { uri },
                range,
            });
        },
        sendColorPresentationRequest(uri, color, range) {
            return connection.sendRequest(_.ColorPresentationRequest.type, {
                textDocument: { uri },
                color,
                range,
            });
        },
        sendDocumentLinkRequest(uri) {
            return connection.sendRequest(_.DocumentLinkRequest.type, {
                textDocument: { uri },
            });
        },
        sendDocumentLinkResolveRequest(link) {
            return connection.sendRequest(_.DocumentLinkResolveRequest.type, link);
        },
        sendInlayHintRequest(uri, range) {
            return connection.sendRequest(_.InlayHintRequest.type, {
                textDocument: { uri },
                range,
            });
        },
        sendInlayHintResolveRequest(hint) {
            return connection.sendRequest(_.InlayHintResolveRequest.type, hint);
        },
    };
    function getConfiguration(section) {
        if (section in settings) {
            return settings[section];
        }
        let result;
        for (const settingKey in settings) {
            if (settingKey.startsWith(`${section}.`)) {
                const value = settings[settingKey];
                const props = settingKey.substring(section.length + 1).split('.');
                result ??= {};
                let current = result;
                while (props.length > 1) {
                    const prop = props.shift();
                    if (typeof current[prop] !== 'object') {
                        current[prop] = {};
                    }
                    current = current[prop];
                }
                current[props.shift()] = value;
            }
        }
        return result;
    }
}
function* printSnapshots(sourceScript) {
    if (sourceScript.generated) {
        let lastId = 0;
        for (const file of (0, language_core_1.forEachEmbeddedCode)(sourceScript.generated.root)) {
            const id = lastId++;
            yield `#${id}`;
            for (const line of printSnapshot(sourceScript, file)) {
                yield '  ' + line;
            }
        }
    }
}
function* printSnapshot(sourceScript, file) {
    const sourceCode = sourceScript.snapshot.getText(0, sourceScript.snapshot.getLength());
    const sourceFileDocument = vscode_languageserver_textdocument_1.TextDocument.create('', '', 0, sourceCode);
    const virtualCode = file.snapshot.getText(0, file.snapshot.getLength());
    const virtualCodeLines = virtualCode.split('\n');
    for (let i = 0; i < virtualCodeLines.length - 2; i++) {
        virtualCodeLines[i] += '\n';
    }
    let lineOffset = 0;
    const map = (0, language_core_1.defaultMapperFactory)(file.mappings);
    for (let i = 0; i < virtualCodeLines.length; i++) {
        const line = virtualCodeLines[i];
        const lineHead = `[${i + 1}]`;
        yield [lineHead, normalizeLogText(line)].join(' ');
        const logs = [];
        for (let offset = 0; offset < line.length; offset++) {
            for (const [sourceOffset, mapping] of map.toSourceLocation(lineOffset + offset)) {
                let log = logs.find(log => log.mapping === mapping && log.lineOffset + log.length + 1 === offset);
                if (log) {
                    log.length++;
                }
                else {
                    log = {
                        mapping,
                        line,
                        lineOffset: offset,
                        sourceOffset: sourceOffset,
                        generatedOffset: offset,
                        length: 0,
                    };
                    logs.push(log);
                }
            }
        }
        for (const log of logs.reverse()) {
            const sourcePosition = sourceFileDocument.positionAt(log.sourceOffset);
            const spanText = log.length === 0 ? '^' : '~'.repeat(log.length);
            const prefix = ' '.repeat(lineHead.length);
            const sourceLineEnd = sourceFileDocument.offsetAt({ line: sourcePosition.line + 1, character: 0 }) - 1;
            const sourceLine = sourceFileDocument.getText().substring(sourceFileDocument.offsetAt({ line: sourcePosition.line, character: 0 }), sourceLineEnd + 1);
            const sourceLineHead = `[${sourcePosition.line + 1}]`;
            yield [
                prefix,
                ' '.repeat(log.lineOffset),
                spanText,
            ].join(' ');
            if (log.line === sourceLine) {
                yield [
                    prefix,
                    ' '.repeat(log.lineOffset),
                    sourceLineHead,
                    '(exact match)',
                    `(${':' + (sourcePosition.line + 1)
                        + ':' + (sourcePosition.character + 1)})`,
                ].join(' ');
            }
            else {
                yield [
                    prefix,
                    ' '.repeat(log.lineOffset),
                    sourceLineHead,
                    normalizeLogText(sourceLine),
                    `(${':' + (sourcePosition.line + 1)
                        + ':' + (sourcePosition.character + 1)})`,
                ].join(' ');
                yield [
                    prefix,
                    ' '.repeat(log.lineOffset),
                    ' '.repeat(sourceLineHead.length),
                    ' '.repeat(sourcePosition.character) + spanText,
                ].join(' ');
            }
        }
        lineOffset += line.length;
    }
}
function normalizeLogText(text) {
    return text
        .replace(/\t/g, '→')
        .replace(/\n/g, '↵')
        .replace(/ /g, '·');
}
//# sourceMappingURL=index.js.map