UNPKG

@imc-trading/svlangserver

Version:
445 lines (444 loc) 23 kB
"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;