"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* --------------------------------------------------------------------------------------------
 * Licensed under the MIT License. See License.txt in the project root for license information.
 * ------------------------------------------------------------------------------------------ */
const node_1 = require("vscode-languageserver/node");
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const svindexer_1 = require("./svindexer");
const svdefprovider_1 = require("./svdefprovider");
const diagnostics_1 = require("./diagnostics");
const svcompleter_1 = require("./svcompleter");
const svsignhelpprovider_1 = require("./svsignhelpprovider");
const svformatter_1 = require("./svformatter");
const svhier_1 = require("./svhier");
const genutils_1 = require("./genutils");
const svutils_1 = require("./svutils");
const BuildIndexCommand = "systemverilog.build_index";
const ReportHierarchyCommand = "systemverilog.report_hierarchy";
// Create a connection for the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
let connection = node_1.createConnection(node_1.ProposedFeatures.all);
genutils_1.ConnectionLogger.setConnection(connection);
// client capabilities
let clientName;
let hasConfigurationCapability = false;
let hasShowWindowCapability = false;
// Create a simple text document manager.
let documents = new node_1.TextDocuments(vscode_languageserver_textdocument_1.TextDocument);
let svindexer = new svindexer_1.SystemVerilogIndexer();
let svdefprovider = new svdefprovider_1.SystemVerilogDefinitionProvider(svindexer);
let diagnostics = new diagnostics_1.VerilogDiagnostics(svindexer);
let svcompleter = new svcompleter_1.SystemVerilogCompleter(svindexer);
let svsignhelper = new svsignhelpprovider_1.SystemVerilogSignatureHelpProvider(svindexer);
let svformatter = new svformatter_1.SystemVerilogFormatter();
let svhiercalculator = new svhier_1.SystemVerilogHierarchyCalculator(svindexer);
let settings = svutils_1.default_settings;
connection.onInitialize((params) => {
    hasConfigurationCapability = !!(params.capabilities.workspace && !!params.capabilities.workspace.configuration);
    hasShowWindowCapability = !!(params.capabilities.window && params.capabilities.window.showDocument);
    clientName = !!params.clientInfo ? params.clientInfo.name : undefined;
    try {
        svindexer.setRoot(genutils_1.uriToPath(params.rootUri));
        if ((clientName.toLowerCase().startsWith("vscode")) ||
            (clientName.toLowerCase().startsWith("visual studio code"))) {
            svindexer.setClientDir(".vscode");
        }
        else if (clientName.toLowerCase().startsWith("coc.nvim")) {
            svindexer.setClientDir(".vim");
        }
        else if (clientName.toLowerCase().startsWith("sublime")) {
            svindexer.setClientDir(".sublime");
        }
        else if (clientName.toLowerCase().startsWith("emacs")) {
            svindexer.setClientDir(".emacs");
        }
        else if (clientName.toLowerCase().startsWith("neovim")) {
            svindexer.setClientDir(".nvim");
        }
        else {
            svindexer.setClientDir(".svlangserver");
        }
        diagnostics.setOptionsFile(svindexer.getLinterOptionsFile());
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
    const result = {
        capabilities: {
            textDocumentSync: node_1.TextDocumentSyncKind.Incremental,
            // Tell the client that this server supports code completion.
            completionProvider: {
                resolveProvider: true
            },
            documentSymbolProvider: true,
            workspaceSymbolProvider: true,
            definitionProvider: true,
            hoverProvider: true,
            signatureHelpProvider: {
                triggerCharacters: ['(', '[', ','],
                retriggerCharacters: [','] //TBD Not supported in CoC!
            },
            executeCommandProvider: {
                commands: [
                    BuildIndexCommand,
                    ReportHierarchyCommand,
                ]
            },
            documentFormattingProvider: true,
            documentRangeFormattingProvider: true,
        }
    };
    return result;
});
function getCurrentSettings() {
    let currentSettings = new Object();
    settings.forEach((val, prop) => { currentSettings[prop] = val; });
    return { settings: currentSettings };
}
function getSettings() {
    if (!hasConfigurationCapability) {
        return Promise.resolve(getCurrentSettings());
    }
    else if (clientName == "coc.nvim") {
        return connection.workspace.getConfiguration().then(svSettings => {
            let initSettings = new Object();
            settings.forEach((val, prop) => {
                initSettings[prop] = svSettings[prop] == undefined ? settings[prop] : svSettings[prop];
            });
            return { settings: initSettings };
        }).catch(error => {
            genutils_1.ConnectionLogger.error(error);
            return getCurrentSettings();
        });
    }
    else {
        return connection.workspace.getConfiguration({ section: 'systemverilog' }).then(svSettings => {
            let initSettings = new Object();
            settings.forEach((val, prop) => {
                let nprop = prop.substring("systemverilog.".length);
                initSettings[prop] = svSettings[nprop] == undefined ? settings[prop] : svSettings[nprop];
            });
            return { settings: initSettings };
        }).catch(error => {
            genutils_1.ConnectionLogger.error(error);
            return getCurrentSettings();
        });
    }
}
let settingsInitialized = false;
function updateSettings(change, updateIfNotInitialized = false) {
    let oldSettings = new Map();
    for (let [prop, val] of settings.entries()) {
        oldSettings.set(prop, val);
        if (change.settings[prop] == undefined) {
            let hierParts = prop.split(".");
            let newVal = change.settings;
            for (let i = 0; i < hierParts.length; i++) {
                newVal = newVal[hierParts[i]];
                if (newVal == undefined) {
                    break;
                }
            }
            settings.set(prop, newVal == undefined ? val : newVal);
        }
        else {
            settings.set(prop, change.settings[prop]);
        }
    }
    for (let [prop, val] of settings.entries()) {
        genutils_1.ConnectionLogger.log(`INFO: settings[${prop}] = ${settings.get(prop)}`);
    }
    let forceUpdate = !settingsInitialized && updateIfNotInitialized;
    let definesChanged = forceUpdate || !genutils_1.isStringListEqual(oldSettings.get("systemverilog.defines"), settings.get("systemverilog.defines"));
    if (forceUpdate || definesChanged ||
        !genutils_1.isStringListEqual(oldSettings.get("systemverilog.includeIndexing"), settings.get("systemverilog.includeIndexing")) ||
        !genutils_1.isStringListEqual(oldSettings.get("systemverilog.mustIncludeIndexing"), settings.get("systemverilog.mustIncludeIndexing")) ||
        !genutils_1.isStringListEqual(oldSettings.get("systemverilog.excludeIndexing"), settings.get("systemverilog.excludeIndexing"))) {
        if (definesChanged) {
            svindexer.setDefines(settings.get("systemverilog.defines"));
        }
        svindexer.index(settings.get("systemverilog.includeIndexing"), settings.get("systemverilog.mustIncludeIndexing"), settings.get("systemverilog.excludeIndexing"));
    }
    if (forceUpdate ||
        !genutils_1.isStringListEqual(oldSettings.get("systemverilog.libraryIndexing"), settings.get("systemverilog.libraryIndexing"))) {
        svindexer.setLibraries(settings.get("systemverilog.libraryIndexing"), settings.get("systemverilog.excludeIndexing"));
    }
    diagnostics.setLinter(settings.get("systemverilog.linter"));
    diagnostics.setCommand(settings.get("systemverilog.launchConfiguration"));
    diagnostics.setDefines(settings.get("systemverilog.defines"));
    diagnostics.setWhitelistedMessages(settings.get("systemverilog.linterWhitelist"));
    svformatter.setCommand(settings.get("systemverilog.formatCommand"));
    settingsInitialized = true;
}
connection.onInitialized(() => {
    try {
        getSettings()
            .then(initSettings => updateSettings(initSettings, true))
            .catch(error => {
            genutils_1.ConnectionLogger.error(error);
        });
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
connection.onDidChangeConfiguration(change => {
    try {
        updateSettings(change);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
documents.onDidChangeContent(change => {
    try {
        svindexer.processDocumentChanges(change.document);
        if (settings.get("systemverilog.lintOnUnsaved")) {
            lintDocument(change.document.uri, change.document.getText());
        }
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
// This handler provides the initial list of the completion items.
connection.onCompletion((_textDocumentPosition) => {
    try {
        if (settings.get("systemverilog.disableCompletionProvider")) {
            return [];
        }
        return svcompleter.completionItems(documents.get(_textDocumentPosition.textDocument.uri), _textDocumentPosition.position); //TBD try-catch
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return [];
    }
});
connection.onCompletionResolve((item) => {
    return item;
});
connection.onDocumentSymbol((documentSymbolParams) => {
    try {
        return svindexer.getDocumentSymbols(documents.get(documentSymbolParams.textDocument.uri));
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve([]);
    }
});
connection.onWorkspaceSymbol((workspaceSymbolParams) => {
    try {
        return svindexer.getWorkspaceSymbols(workspaceSymbolParams.query);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve([]);
    }
});
connection.onDefinition((textDocumentPosition) => {
    try {
        return svdefprovider.getDefinitionSymbolLocation(documents.get(textDocumentPosition.textDocument.uri), textDocumentPosition.position);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve([]);
    }
});
connection.onHover((hoverParams) => {
    try {
        if (settings.get("systemverilog.disableHoverProvider")) {
            return Promise.resolve(undefined);
        }
        let defText = svdefprovider.getDefinitionText(documents.get(hoverParams.textDocument.uri), hoverParams.position);
        if ((defText[0] == undefined) || (defText[1] == undefined)) {
            return Promise.resolve(undefined);
        }
        return Promise.resolve({
            contents: {
                kind: node_1.MarkupKind.Markdown,
                value: (defText[0].length > 0 ? [`*${defText[0]}*`] : []).concat([(clientName == "coc.nvim") ? "```systemverilog" : "```"]).concat(defText[1]).concat(["```"]).join('\n'),
            },
        });
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve(undefined);
    }
});
function lintDocument(uri, text) {
    if (settings.get("systemverilog.disableLinting")) {
        return;
    }
    diagnostics.lint(genutils_1.uriToPath(uri), text)
        .then((diagnostics) => {
        connection.sendDiagnostics({ uri: uri, diagnostics });
    })
        .catch((error) => {
        genutils_1.ConnectionLogger.error(error);
    });
}
documents.onDidOpen((event) => {
    try {
        svindexer.indexOpenDocument(event.document);
        lintDocument(event.document.uri);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
documents.onDidSave((event) => {
    try {
        svindexer.indexOpenDocument(event.document);
        svindexer.updateFileInfoOnSave(event.document);
        lintDocument(event.document.uri);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
connection.onSignatureHelp((textDocumentPosition) => {
    try {
        if (settings.get("systemverilog.disableSignatureHelpProvider")) {
            return undefined;
        }
        return svsignhelper.getSignatures(documents.get(textDocumentPosition.textDocument.uri), textDocumentPosition.position.line, textDocumentPosition.position.character);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return undefined;
    }
});
connection.onExecuteCommand((commandParams) => {
    try {
        if (commandParams.command == BuildIndexCommand) {
            svindexer.index(settings.get("systemverilog.includeIndexing"), settings.get("systemverilog.mustIncludeIndexing"), settings.get("systemverilog.excludeIndexing"));
        }
        else if (commandParams.command == ReportHierarchyCommand) {
            if ((commandParams.arguments == undefined) || (commandParams.arguments.length != 1)) {
                genutils_1.ConnectionLogger.error(`${ReportHierarchyCommand} needs the module/interface name as an argument`);
                return;
            }
            const fileUri = svhiercalculator.calcHier(commandParams.arguments[0]);
            genutils_1.ConnectionLogger.log(`Hierarchy for ${commandParams.arguments[0]} available at ${fileUri}`);
            if (hasShowWindowCapability) {
                connection.sendRequest("window/showDocument", { uri: fileUri, takeFocus: true });
            }
            else {
                const editParams = {
                    label: "Hierarchy",
                    edit: {
                        documentChanges: [
                            vscode_languageserver_types_1.TextDocumentEdit.create({ uri: fileUri, version: null }, []),
                        ]
                    }
                };
                connection.sendRequest("workspace/applyEdit", editParams);
            }
        }
        else {
            genutils_1.ConnectionLogger.error(`Unhandled command ${commandParams.command}`);
        }
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
    }
});
connection.onDocumentFormatting((formatParams) => {
    try {
        return svformatter.format(documents.get(formatParams.textDocument.uri), null, formatParams.options);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve([]);
    }
});
connection.onDocumentRangeFormatting((rangeFormatParams) => {
    try {
        return svformatter.format(documents.get(rangeFormatParams.textDocument.uri), rangeFormatParams.range, rangeFormatParams.options);
    }
    catch (error) {
        genutils_1.ConnectionLogger.error(error);
        return Promise.resolve([]);
    }
});
connection.onShutdown((token) => {
    diagnostics.cleanupTmpFiles();
    svindexer.saveIndexOnExit();
});
// Save index on exit
connection.onExit(() => {
    diagnostics.cleanupTmpFiles();
    svindexer.saveIndexOnExit();
});
process.on('exit', () => {
    diagnostics.cleanupTmpFiles();
    svindexer.saveIndexOnExit();
});
process.on('SIGTERM', () => {
    diagnostics.cleanupTmpFiles();
    svindexer.saveIndexOnExit();
});
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
// Listen on the connection
connection.listen();
