@imc-trading/svlangserver
Version:
A language server for systemverilog
266 lines (265 loc) • 12.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemVerilogDefinitionProvider = void 0;
const node_1 = require("vscode-languageserver/node");
const genutils_1 = require("./genutils");
class SystemVerilogDefinitionProvider {
constructor(indexer) {
this._indexer = indexer;
}
_findNamedArg(tokenNum, svtokens) {
let scopeDepth = svtokens[tokenNum].scopes.length - 1;
if ((tokenNum == 0) || (scopeDepth == 0)) {
return [undefined, undefined];
}
let dotLocation;
for (let i = tokenNum - 1; i >= 0; i--) {
if (svtokens[i].text == ".") {
dotLocation = i;
break;
}
else if (svtokens[i].scopes[svtokens[i].scopes.length - 1] != "meta.whitespace.systemverilog") {
return [undefined, undefined];
}
}
if (dotLocation == undefined) {
return [undefined, undefined];
}
for (let i = 0; i < svtokens[tokenNum].scopes.length; i++) {
if (!svtokens[tokenNum].scopes[i].startsWith("identifier.") &&
(svtokens[tokenNum].scopes[i] != "parantheses.block.systemverilog") &&
(svtokens[tokenNum].scopes[i] != "case.body.systemverilog") &&
(svtokens[tokenNum].scopes[i] != "begin.block.systemverilog") &&
(svtokens[tokenNum].scopes[i] != "generate.block.systemverilog") &&
(svtokens[tokenNum].scopes[i] != "source.systemverilog")) {
for (let j = tokenNum - 1; j >= 0; j--) {
if (svtokens[j].scopes[scopeDepth - 1].startsWith("identifier.")) {
return [svtokens[j].text, true];
}
}
return [undefined, undefined];
}
}
let instNameFound = false;
let openParenFound = false;
let hashOpenParenFound = false;
for (let i = dotLocation - 1; i >= 0; i--) {
if (svtokens[i].scopes[scopeDepth - 1].startsWith("identifier.")) {
if (instNameFound || hashOpenParenFound) {
return [svtokens[i].text, false];
}
else {
instNameFound = true;
}
}
else if ((svtokens[i].text == "#") && openParenFound) {
hashOpenParenFound = true;
}
else if (!openParenFound && (svtokens[i].text == "(")) {
openParenFound = true;
}
else if (!svtokens[i].scopes[svtokens[i].scopes.length - 1].startsWith("comment.") &&
(svtokens[i].scopes[svtokens[i].scopes.length - 1] != "meta.whitespace.systemverilog")) {
openParenFound = false;
}
}
return [undefined, undefined];
}
_getIncludeFileName(tokens, tokenNum) {
let startTokenNum;
for (let i = tokenNum; i >= 0; i--) {
let scope = tokens[i].scopes[tokens[i].scopes.length - 1];
if (scope == "string.begin.systemverilog") {
startTokenNum = i;
break;
}
}
if (startTokenNum === undefined) {
return undefined;
}
let endTokenNum;
for (let i = tokenNum; i < tokens.length; i++) {
let scope = tokens[i].scopes[tokens[i].scopes.length - 1];
if (scope == "string.end.systemverilog") {
endTokenNum = i;
break;
}
}
if (endTokenNum === undefined) {
return undefined;
}
return tokens.slice(startTokenNum + 1, endTokenNum).map(t => t.text).join('');
}
_getDefinition(document, position, includeUserDefines, checkPrevPosition = false) {
let svtokens = this._indexer.getSystemVerilogCompletionTokens(document.uri);
let extTokenNums = this._indexer.getSystemVerilogCompletionTokenNumber(document, position.line, position.character + 1);
let tokenNum = extTokenNums[1];
if (tokenNum == undefined) {
return [undefined, undefined];
}
let scope = svtokens[tokenNum].scopes[svtokens[tokenNum].scopes.length - 1];
let parentScope = svtokens[tokenNum].scopes.length > 1 ? svtokens[tokenNum].scopes[svtokens[tokenNum].scopes.length - 2] : undefined;
if (scope.startsWith("macro.")) {
let defText = svtokens[tokenNum].text.slice(1).replace(/\s*\($/, "");
let result = this._indexer.getMacros(document.uri, defText);
if (result.length > 0) {
return [result[0][0], result[0][1][0]];
}
if (includeUserDefines) {
let userDefineNum = this._indexer.findUserDefine(defText);
if (userDefineNum >= 0) {
//DBG ConnectionLogger.log(`DEBUG: HERE with ${userDefineNum} for ${defText}`);
return ["", userDefineNum];
}
}
return [undefined, undefined];
}
else if (scope.startsWith("identifier.hierarchical.") || (extTokenNums[0] != tokenNum)) {
let filePath;
let symbol;
let idTokens = svtokens.slice(extTokenNums[0], tokenNum + 1);
[filePath, symbol] = this._indexer.getHierarchicalSymbol(document.uri, this._indexer.getHierParts(idTokens.map(t => t.text).join(''), idTokens, document.offsetAt(position) - svtokens[extTokenNums[0]].index));
if (symbol != undefined) {
return [filePath, symbol];
}
}
else if (parentScope == "string.body.systemverilog") {
let incFileName = this._getIncludeFileName(svtokens, tokenNum);
if (incFileName == undefined) {
return [undefined, undefined];
}
return this._indexer.getIncFilePathAndSymbol(incFileName);
}
else if (!scope.startsWith("identifier.")) {
if (checkPrevPosition && (position.character > 0)) {
return this._getDefinition(document, node_1.Position.create(position.line, position.character - 1), includeUserDefines, false);
}
return [undefined, undefined];
}
let containerName;
let isRoutine;
[containerName, isRoutine] = this._findNamedArg(tokenNum, svtokens);
if ((containerName == undefined) || (isRoutine == undefined)) {
let symText = svtokens[tokenNum].text;
if (scope.startsWith("identifier.scoped.")) {
let endPos = document.offsetAt(position) - svtokens[tokenNum].index;
endPos = svtokens[tokenNum].text.indexOf("::", endPos > 0 ? endPos - 1 : 0);
endPos = endPos < 0 ? svtokens[tokenNum].text.length : endPos;
symText = svtokens[tokenNum].text.slice(0, endPos);
symText = symText.replace(/::\*$/, '');
if (symText == "*") {
return [undefined, undefined];
}
else if (symText.indexOf("::") < 0) {
return this._indexer.getPackageSymbol(symText);
}
}
let filePath;
let symbol;
[filePath, symbol] = scope.startsWith("identifier.scoped.") ? this._indexer.findSymbol(document.uri, symText) : this._indexer.findScopedSymbol(document.uri, symText, position);
if ((filePath == undefined) || (symbol == undefined)) {
symbol = this._indexer.getContainerSymbol(svtokens[tokenNum].text);
if (symbol == undefined) {
return this._indexer.getPackageSymbol(svtokens[tokenNum].text);
}
filePath = this._indexer.getInstFilePath(svtokens[tokenNum].text);
if (filePath == undefined) {
return [undefined, undefined];
}
return [genutils_1.pathToUri(filePath), symbol];
}
return [filePath, symbol];
}
else if (isRoutine) {
let filePath;
let symbol;
let containerSymbolsInfo;
[filePath, symbol, containerSymbolsInfo] = this._indexer.getContainerInfo(document.uri, containerName);
if ((filePath == undefined) || (symbol == undefined) || (containerSymbolsInfo == undefined) ||
(containerSymbolsInfo.symbolsInfo == undefined)) {
return [undefined, undefined];
}
let argSymbols = containerSymbolsInfo.symbolsInfo;
let filteredArgSymbols = argSymbols.filter(sym => { return sym.name == svtokens[tokenNum].text; });
if (filteredArgSymbols.length > 0) {
return [filePath, filteredArgSymbols[0]];
}
return [undefined, undefined];
}
else {
let filePath;
let symbol;
let containerSymbolsInfo;
let instFilePath = this._indexer.getInstFilePath(containerName);
if (instFilePath == undefined) {
return [undefined, undefined];
}
[filePath, symbol, containerSymbolsInfo] = this._indexer.getContainerInfo(genutils_1.pathToUri(instFilePath), containerName);
if ((filePath == undefined) || (symbol == undefined) || (containerSymbolsInfo == undefined) ||
(containerSymbolsInfo.symbolsInfo == undefined)) {
return [undefined, undefined];
}
let containerSymbols = containerSymbolsInfo.symbolsInfo;
let filteredContainerSymbols = containerSymbols.filter(sym => { return (sym.name == svtokens[tokenNum].text) && ((sym.type[0] == "port") || (sym.type[0] == "parameter-port")); });
if (filteredContainerSymbols.length > 0) {
return [filePath, filteredContainerSymbols[0]];
}
return [undefined, undefined];
}
}
getDefinitionSymbolLocation(document, position) {
try {
let symbolInfo = this._getDefinition(document, position, false, true);
if (symbolInfo[0] == undefined) {
return Promise.resolve([]);
}
if (symbolInfo[0] == "") {
return Promise.resolve([node_1.Location.create("", node_1.Range.create(symbolInfo[1], 0, 0, 0))]);
}
return Promise.resolve([(symbolInfo[1]).getSymbolLocation(symbolInfo[0])]);
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
return Promise.resolve([]);
}
}
getDefinitionText(document, position) {
try {
let symbolInfo = this._getDefinition(document, position, true);
if (symbolInfo[0] == undefined) {
return [undefined, undefined];
}
let header;
let code;
if (symbolInfo[0] == "") {
header = 'User Define';
code = this._indexer.getUserDefine((symbolInfo[1]));
}
else if ((typeof symbolInfo[1] !== 'number') && ((symbolInfo[1]).type.indexOf("includefile") >= 0)) {
header = (symbolInfo[1]).name;
code = '';
}
else {
header = (document.uri == symbolInfo[0]) ? '' : `File: ${genutils_1.uriToPath(symbolInfo[0])}`;
code = (symbolInfo[1]).getDefinition(symbolInfo[0]);
}
let trimLength = 0;
let codeLines = code.split(/\r?\n/).map((codeLine, i) => {
let lineTrimLength = codeLine.search(/\S/);
lineTrimLength = (lineTrimLength < 0) ? 0 : lineTrimLength;
;
if (i == 0) {
trimLength = lineTrimLength;
}
let actTrimLength = (trimLength < lineTrimLength) ? trimLength : lineTrimLength;
return codeLine.slice(actTrimLength);
});
return [header, codeLines];
}
catch (error) {
// ConnectionLogger.error(error); // Too much noise in VSCode console
return [undefined, undefined];
}
}
}
exports.SystemVerilogDefinitionProvider = SystemVerilogDefinitionProvider;