@imc-trading/svlangserver
Version:
A language server for systemverilog
445 lines (444 loc) • 23 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemVerilogSignatureHelpProvider = void 0;
const svsymbol_1 = require("./svsymbol");
const svutils_1 = require("./svutils");
const genutils_1 = require("./genutils");
var ScopeType;
(function (ScopeType) {
ScopeType[ScopeType["None"] = 0] = "None";
ScopeType[ScopeType["InstanceParamsPortsNamed"] = 1] = "InstanceParamsPortsNamed";
ScopeType[ScopeType["InstanceParamsOrdered"] = 2] = "InstanceParamsOrdered";
ScopeType[ScopeType["InstancePortsOrdered"] = 3] = "InstancePortsOrdered";
ScopeType[ScopeType["Macro"] = 4] = "Macro";
ScopeType[ScopeType["RoutineNamed"] = 5] = "RoutineNamed";
ScopeType[ScopeType["RoutineOrdered"] = 6] = "RoutineOrdered";
ScopeType[ScopeType["SystemTask"] = 7] = "SystemTask";
})(ScopeType || (ScopeType = {}));
const systemTasksInfo = new Map([
[/(?:display|write)[boh]?/, [[["format", "format string"], ["...", "arguments"]], 0]],
]);
class SystemVerilogSignatureHelpProvider {
constructor(indexer) {
this._indexer = indexer;
}
_getScopeType(tokenNum, svtokens) {
let scopes = svtokens[tokenNum].scopes;
if (scopes.length > 0) {
let topScope = scopes[scopes.length - 1];
if (topScope == "system.task.systemverilog") {
return [ScopeType.SystemTask, tokenNum];
}
else if (topScope == "macro.call.systemverilog") {
return [ScopeType.Macro, tokenNum];
}
for (let i = scopes.length - 1; i >= 0; i--) {
if ((scopes[i] == "system.args.systemverilog") ||
(scopes[i] == "macro.args.systemverilog")) {
let startTokenNum = tokenNum - 1;
for (; startTokenNum >= 0; startTokenNum--) {
if ((svtokens[startTokenNum].scopes.length >= i) &&
((svtokens[startTokenNum].scopes[i] == "system.task.systemverilog") ||
(svtokens[startTokenNum].scopes[i] == "macro.call.systemverilog"))) {
break;
}
}
if (startTokenNum >= 0) {
return [scopes[i] == "system.args.systemverilog" ? ScopeType.SystemTask : ScopeType.Macro, startTokenNum];
}
else {
return [ScopeType.None, undefined];
}
}
else if ((scopes[i] == "parantheses.begin.systemverilog") ||
(scopes[i] == "parantheses.block.systemverilog")) {
let startTokenNum;
if (scopes[i] == "parantheses.begin.systemverilog") {
startTokenNum = tokenNum;
}
else {
for (let j = tokenNum - 1; j >= 0; j--) {
if (svtokens[j].scopes.length <= i) {
break;
}
else if (svtokens[j].scopes[i] == "parantheses.begin.systemverilog") {
startTokenNum = j;
break;
}
}
}
if (startTokenNum != undefined) {
let hashOpenParenFound = false;
for (let j = startTokenNum - 1; j >= 0; j--) {
let scope = svtokens[j].scopes[svtokens[j].scopes.length - 1];
if (scope.startsWith("identifier.")) {
if (svutils_1.SystemVerilogUtils.keywordsList.has(svtokens[j].text)) {
break;
}
else if ((svtokens[j].scopes.length < 2) ||
(svtokens[j].scopes[svtokens[j].scopes.length - 2] == "container.header.systemverilog") ||
(svtokens[j].scopes[svtokens[j].scopes.length - 2] == "routine.header.systemverilog")) {
break;
}
else if (scope == "identifier.scoped.systemverilog") {
return [ScopeType.RoutineOrdered, j];
}
else {
let namedArg = false;
for (let k = j - 1; k >= 0; k--) {
if (svtokens[k].text == ".") {
namedArg = true;
break;
}
else if (!svtokens[k].scopes[svtokens[k].scopes.length - 1].startsWith("comment.") &&
(svtokens[k].scopes[svtokens[k].scopes.length - 1] != "meta.whitespace.systemverilog")) {
break;
}
}
for (let k = 0; k < svtokens[j].scopes.length; k++) {
if (!svtokens[j].scopes[k].startsWith("identifier.") &&
(svtokens[j].scopes[k] != "parantheses.block.systemverilog") &&
(svtokens[j].scopes[k] != "case.body.systemverilog") &&
(svtokens[j].scopes[k] != "begin.block.systemverilog") &&
(svtokens[j].scopes[k] != "generate.block.systemverilog") &&
(svtokens[j].scopes[k] != "source.systemverilog")) {
return [namedArg ? ScopeType.RoutineNamed : ScopeType.RoutineOrdered, j];
}
}
if (namedArg) {
return [ScopeType.InstanceParamsPortsNamed, j];
}
if (hashOpenParenFound) {
return [ScopeType.InstanceParamsOrdered, j];
}
let scopeDepth = svtokens[j].scopes.length - 1;
for (let k = j - 1; k >= 0; k--) {
if (svtokens[k].scopes[scopeDepth].startsWith("identifier.")) {
return [ScopeType.InstancePortsOrdered, k];
}
}
}
}
else if (!scope.startsWith("comment.") && (scope != "meta.whitespace.systemverilog")) {
if (!hashOpenParenFound && (svtokens[j].text == "#")) {
hashOpenParenFound = true;
}
else {
break;
}
}
}
}
}
}
}
return [ScopeType.None, undefined];
}
_getArgNum(scopeType, startTokenNum, tokenNum, svtokens) {
let scopeDepth = svtokens[startTokenNum].scopes.length;
if (scopeType == ScopeType.InstanceParamsPortsNamed) {
scopeDepth--;
}
let argNum = 0;
for (let i = startTokenNum + 1; i <= tokenNum; i++) {
if ((svtokens[i].scopes.length > scopeDepth) &&
(svtokens[i].scopes[scopeDepth] == "operator.comma.systemverilog")) {
argNum++;
}
}
return argNum;
}
getSignatures(document, line, character) {
let nullSignatureHelp = {
signatures: [],
activeSignature: null,
activeParameter: null
};
try {
const svtokens = this._indexer.getSystemVerilogCompletionTokens(document.uri);
const tokenNum = this._indexer.getSystemVerilogCompletionTokenNumber(document, line, character)[1];
const svtoken = (tokenNum > 0) && (tokenNum < svtokens.length) ? svtokens[tokenNum] : null;
if (!svtoken) {
return nullSignatureHelp;
}
let scopeType;
let startTokenNum;
[scopeType, startTokenNum] = this._getScopeType(tokenNum, svtokens);
if (scopeType == ScopeType.None) {
return nullSignatureHelp;
}
let argNum = this._getArgNum(scopeType, startTokenNum, tokenNum, svtokens);
if (scopeType == ScopeType.SystemTask) {
let taskInfo;
for (let task of systemTasksInfo.keys()) {
let regEx = new RegExp(String.raw `^\$${task.source}\s*\($`);
if (regEx.exec(svtokens[startTokenNum].text)) {
taskInfo = systemTasksInfo.get(task);
break;
}
}
if (taskInfo != undefined) {
let params = [];
for (let i = 0; i < taskInfo[0].length; i++) {
let label = taskInfo[0][i][0];
if (i == taskInfo[1]) {
label = `[${label}`;
}
if ((i == (taskInfo[0].length - 1)) &&
(taskInfo[1] < taskInfo[0].length)) {
label = `${label}]`;
}
params.push({
label: label,
documentation: taskInfo[0][i][1]
});
}
let signLabel = svtokens[startTokenNum].text.concat(params.map(param => { return param.label; }).join(', ')).concat(')');
return {
signatures: [
{
label: signLabel,
documentation: "system task",
parameters: params
}
],
activeSignature: 0,
activeParameter: argNum >= params.length ? params.length - 1 : argNum
};
}
}
else if (scopeType == ScopeType.Macro) {
let macroName = svtokens[startTokenNum].text.slice(1, -1).trim();
let macroFilesInfo = this._indexer.getMacros(document.uri, svtokens[startTokenNum].text.slice(1).replace(/\s*\($/, ""));
if (macroFilesInfo.length > 0) {
let macroDefinition = macroFilesInfo[0][1][0].getDefinition(macroFilesInfo[0][0]);
let macroArgsStartRegEx = new RegExp(String.raw `^\s*\`define\s+${macroName}\s*\(`);
let macroArgsStart = macroArgsStartRegEx.exec(macroDefinition);
if (!macroArgsStart) {
return nullSignatureHelp;
}
let params = [];
let paramLabel = "";
let nestDepth = 0;
let charIndex = macroArgsStart[0].length;
for (; charIndex < macroDefinition.length; charIndex++) {
if (((macroDefinition[charIndex] == ",") || (macroDefinition[charIndex] == ")")) && (nestDepth == 0)) {
params.push({ label: paramLabel.trim() });
if (macroDefinition[charIndex] == ")") {
charIndex++;
break;
}
else {
paramLabel = "";
}
}
else {
paramLabel = paramLabel.concat(macroDefinition[charIndex]);
if ((macroDefinition[charIndex] == "(") ||
(macroDefinition[charIndex] == "[") ||
(macroDefinition[charIndex] == "{")) {
nestDepth++;
}
else if ((macroDefinition[charIndex] == ")") ||
(macroDefinition[charIndex] == "]") ||
(macroDefinition[charIndex] == "}")) {
nestDepth--;
}
}
}
let macroLabel = `\`${macroName}(`.concat(params.map(param => { return param.label; }).join(', ')).concat(')');
let macroDoc = macroDefinition.slice(charIndex).trim();
return {
signatures: [
{
label: macroLabel,
documentation: macroDoc,
parameters: params
}
],
activeSignature: 0,
activeParameter: argNum >= params.length ? params.length - 1 : argNum
};
}
}
else if (scopeType == ScopeType.RoutineNamed) {
let scopeDepth = svtokens[startTokenNum].scopes.length - 2;
if (scopeDepth <= 0) {
return nullSignatureHelp;
}
let routineToken;
for (let i = startTokenNum - 1; i >= 0; i--) {
if (svtokens[i].scopes[scopeDepth].startsWith("identifier.")) {
routineToken = i;
break;
}
}
if (routineToken == undefined) {
return nullSignatureHelp;
}
let routineFile;
let routineSymbol;
let containerSymbolsInfo;
[routineFile, routineSymbol, containerSymbolsInfo] = this._indexer.getContainerInfo(document.uri, svtokens[routineToken].text);
if ((routineFile == undefined) || (routineSymbol == undefined) || (containerSymbolsInfo == undefined) ||
(containerSymbolsInfo.symbolsInfo == undefined)) {
return nullSignatureHelp;
}
let routineArgSymbols = containerSymbolsInfo.symbolsInfo;
let symbols = routineArgSymbols.filter(sym => { return sym.name == svtokens[startTokenNum].text; });
if (symbols.length <= 0) {
return nullSignatureHelp;
}
let label = `${svtokens[routineToken].text}(${routineArgSymbols.map(sym => sym.name).join(', ')})`;
let definition = symbols[0].getDefinition(routineFile);
return {
signatures: [
{
label: definition,
documentation: routineSymbol.getDefinition(routineFile),
parameters: []
}
],
activeSignature: 0,
activeParameter: null
};
}
else if (scopeType == ScopeType.RoutineOrdered) {
let routineFile;
let routineSymbol;
let containerSymbolsInfo;
[routineFile, routineSymbol, containerSymbolsInfo] = this._indexer.getContainerInfo(document.uri, svtokens[startTokenNum].text);
//DBG ConnectionLogger.log(`DEBUG: HERE with ${svtoken.text} and ${scopeType} and ${svtokens[startTokenNum].text} and ${routineFile} and ${routineSymbol.name} and ${containerSymbolsInfo == undefined}`);
if ((routineFile == undefined) || (routineSymbol == undefined) || (containerSymbolsInfo == undefined)) {
return nullSignatureHelp;
}
if (containerSymbolsInfo.symbolsInfo == undefined) {
return {
signatures: [
{
label: `${svtokens[startTokenNum].text}()`,
documentation: routineSymbol.getDefinition(routineFile),
parameters: []
}
],
activeSignature: 0,
activeParameter: undefined
};
}
let routineArgSymbols = containerSymbolsInfo.symbolsInfo;
let label = `${svtokens[startTokenNum].text}(${routineArgSymbols.map(sym => sym.name).join(', ')})`;
let definitions = svsymbol_1.SystemVerilogSymbol.getDefinitions(routineFile, routineArgSymbols);
let params = [];
for (let i = 0; i < routineArgSymbols.length; i++) {
params.push({
label: routineArgSymbols[i].name,
documentation: definitions[i]
});
}
return {
signatures: [
{
label: label,
documentation: routineSymbol.getDefinition(routineFile),
parameters: params
}
],
activeSignature: 0,
activeParameter: argNum >= params.length ? params.length - 1 : argNum
};
}
else if (scopeType == ScopeType.InstanceParamsPortsNamed) {
// Find the instance type
let scopeDepth = svtokens[startTokenNum].scopes.length - 2;
if (scopeDepth <= 0) {
return nullSignatureHelp;
}
let instTypeToken;
let openParenFound = false;
let hashOpenParenFound = false;
let portScope = false;
for (let i = startTokenNum - 1; i >= 0; i--) {
if (svtokens[i].scopes[scopeDepth].startsWith("identifier.")) {
if (portScope || hashOpenParenFound) {
instTypeToken = i;
break;
}
else if (openParenFound) {
portScope = true;
}
}
else if (!svtokens[i].scopes[svtokens[i].scopes.length - 1].startsWith("comment.") &&
(svtokens[i].scopes[svtokens[i].scopes.length - 1] != "meta.whitespace.systemverilog")) {
if (hashOpenParenFound) {
openParenFound = false;
hashOpenParenFound = false;
}
else if (openParenFound) {
if (svtokens[i].text == "#") {
hashOpenParenFound = true;
}
openParenFound = false;
}
else if (svtokens[i].text == "(") {
openParenFound = true;
}
}
}
if ((instTypeToken == undefined) || (!openParenFound && !hashOpenParenFound)) {
return nullSignatureHelp;
}
let symbols = (portScope ? this._indexer.getInstPorts(svtokens[instTypeToken].text)
: this._indexer.getInstParams(svtokens[instTypeToken].text)).filter(sym => { return sym.name == svtokens[startTokenNum].text; });
if (symbols.length <= 0) {
return nullSignatureHelp;
}
let instTypeSymbol = this._indexer.getContainerSymbol(svtokens[instTypeToken].text);
let instFilePath = this._indexer.getInstFilePath(svtokens[instTypeToken].text);
let definition = symbols[0].getDefinition(instFilePath);
return {
signatures: [
{
label: definition,
documentation: instTypeSymbol.getDefinition(instFilePath),
parameters: []
}
],
activeSignature: 0,
activeParameter: null
};
}
else if ((scopeType == ScopeType.InstanceParamsOrdered) ||
(scopeType == ScopeType.InstancePortsOrdered)) {
let symbols = (scopeType == ScopeType.InstanceParamsOrdered) ? this._indexer.getInstParams(svtokens[startTokenNum].text) : this._indexer.getInstPorts(svtokens[startTokenNum].text);
let label = `${svtokens[startTokenNum].text}(${symbols.map(sym => sym.name).join(', ')})`;
let instTypeSymbol = this._indexer.getContainerSymbol(svtokens[startTokenNum].text);
let instFilePath = this._indexer.getInstFilePath(svtokens[startTokenNum].text);
let definitions = svsymbol_1.SystemVerilogSymbol.getDefinitions(instFilePath, symbols);
let params = [];
for (let i = 0; i < symbols.length; i++) {
params.push({
label: symbols[i].name,
documentation: definitions[i]
});
}
return {
signatures: [
{
label: label,
documentation: instTypeSymbol.getDefinition(instFilePath),
parameters: params
}
],
activeSignature: 0,
activeParameter: argNum >= params.length ? params.length - 1 : argNum
};
}
return nullSignatureHelp;
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
return nullSignatureHelp;
}
}
}
exports.SystemVerilogSignatureHelpProvider = SystemVerilogSignatureHelpProvider;