@imc-trading/svlangserver
Version:
A language server for systemverilog
388 lines (387 loc) • 15.7 kB
JavaScript
;
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();