@imc-trading/svlangserver
Version:
A language server for systemverilog
1,082 lines (1,081 loc) • 65.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemVerilogIndexer = void 0;
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const svpreprocessor_1 = require("./svpreprocessor");
const svparser_1 = require("./svparser");
const svutils_1 = require("./svutils");
const grammar_engine_1 = require("./grammar_engine");
const svcompletion_grammar_1 = require("./svcompletion_grammar");
const genutils_1 = require("./genutils");
const { fork } = require('child_process');
const glob = require('glob');
const path = require('path');
const { performance } = require('perf_hooks');
const CACHED_INDEX_FILE_VERSION = "1.1.0";
const RETRY_DELAY_MS = 1000;
const MAX_RETRY_COUNT = 60;
var IndexProgressType;
(function (IndexProgressType) {
IndexProgressType[IndexProgressType["None"] = 0] = "None";
IndexProgressType[IndexProgressType["Starting"] = 1] = "Starting";
IndexProgressType[IndexProgressType["Ongoing"] = 2] = "Ongoing";
IndexProgressType[IndexProgressType["Done"] = 3] = "Done";
IndexProgressType[IndexProgressType["Halting"] = 4] = "Halting";
IndexProgressType[IndexProgressType["Halted"] = 5] = "Halted";
})(IndexProgressType || (IndexProgressType = {}));
class SystemVerilogIndexer {
constructor(aRootPath) {
this._mustSrcFiles = new Set();
this._libFiles = [];
this._preprocCache = new Map();
this._indexedFilesInfo = new Map();
this._pkgToFiles = new Map();
this._moduleToFiles = new Map();
this._interfaceToFiles = new Map();
this._fileToContainers = new Map();
this._lastSavedFilesInfo = new Map();
this._indexProgress = IndexProgressType.None;
this._cacheLoadingDone = false;
this._indexIsSaveable = false;
this._completionGrammarEngine = new grammar_engine_1.GrammarEngine(svcompletion_grammar_1.svcompletion_grammar, "meta.invalid.systemverilog");
this._filesCompletionInfo = new Map();
this._userDefines = [];
this._optionsFileContent = [];
this.NUM_FILES = 250;
this.mostRecentSymbols = [];
this.setRoot(aRootPath);
}
setRoot(aRootPath) {
this._rootPath = aRootPath || null;
}
setClientDir(aClientDir) {
this._clientDir = aClientDir || ".vim";
}
getIndexFile() {
return this._rootPath + "/" + this._clientDir + "/svlangserver/index.json";
}
getLinterOptionsFile() {
return this._rootPath + "/" + this._clientDir + "/svlangserver/linter.vc";
}
setDefines(defines) {
this._userDefines = defines.map(s => s.split("=", 2)).map(s => { return { name: s[0], def: s[1], tokens: s[1] == undefined ? [] : svpreprocessor_1.SystemVerilogPreprocessor.tokenize(s[1]) }; });
}
_indexGlob(includes, excludes, globOptions, func) {
const pattern = includes.length == 1 ? includes[0] : '{' + includes.join(",") + '}';
return glob(pattern, !!globOptions ? globOptions : { cwd: this._rootPath, ignore: excludes, follow: true, realpath: true }, func);
}
setLibraries(libraries, excludes) {
this._indexGlob(libraries, excludes, undefined, (err, files) => {
if (err) {
genutils_1.ConnectionLogger.error(err);
}
else if (files.length > 0) {
if (!genutils_1.isStringListEqual(files, this._libFiles)) {
this._libFiles = files;
this._generateVerilatorOptionsFile();
}
}
else {
genutils_1.ConnectionLogger.log("No library files found");
this._libFiles = [];
}
});
}
_waitForHalt(retryCount = 0) {
if (this._indexProgress == IndexProgressType.Halting) {
return new Promise(resolve => {
setTimeout(resolve, RETRY_DELAY_MS);
}).then(() => {
if (retryCount < MAX_RETRY_COUNT) {
return this._waitForHalt(retryCount + 1);
}
else {
throw 'Timeout trying to halt indexing';
}
return;
});
}
return Promise.resolve();
}
index(includes, mustIncludes, excludes) {
let _filesGlob = (callBack) => {
let _incFiles = undefined;
let _mustFiles = undefined;
let _incGlob = this._indexGlob(includes, excludes, undefined, (err, files) => {
if (err) {
callBack(err, [], []);
return;
}
_incFiles = files;
if (_mustFiles != undefined) {
callBack(err, _incFiles, _mustFiles);
}
});
this._indexGlob(mustIncludes, excludes, _incGlob.options, (err, files) => {
if (err) {
callBack(err, [], []);
return;
}
_mustFiles = files;
if (_incFiles != undefined) {
callBack(err, _incFiles, _mustFiles);
}
});
};
let _index = (srcFiles) => {
this._indexProgress = IndexProgressType.Ongoing;
this._srcFiles = srcFiles;
if (!this._cacheLoadingDone) {
this._loadCachedIndex();
}
this._buildIndex();
};
if (this._rootPath) {
if (this._indexProgress == IndexProgressType.Starting) {
this._indexProgress = IndexProgressType.Halting;
this._waitForHalt().then(() => {
this.index(includes, mustIncludes, excludes);
}).catch((err) => {
genutils_1.ConnectionLogger.error(err);
});
return;
}
else if (this._indexProgress == IndexProgressType.Ongoing) {
_filesGlob((err, files, mustFiles) => {
if (err) {
genutils_1.ConnectionLogger.error(err);
return;
}
let newMustSrcFiles = new Set(mustFiles);
let allFiles = files.concat(mustFiles);
if (allFiles.length > 0) {
if (!genutils_1.isStringListEqual(allFiles, this._srcFiles)) {
this._indexProgress = IndexProgressType.Halting;
this._waitForHalt().then(() => {
this._mustSrcFiles = newMustSrcFiles;
_index(allFiles);
}).catch((err) => {
genutils_1.ConnectionLogger.error(err);
});
}
else if ([...newMustSrcFiles].sort().join() != [...this._mustSrcFiles].sort().join()) {
this._mustSrcFiles = newMustSrcFiles;
this._generateVerilatorOptionsFile();
}
}
else {
genutils_1.ConnectionLogger.log("No files to index");
}
});
return;
}
this._indexProgress = IndexProgressType.Starting;
_filesGlob((err, files, mustFiles) => {
if (this._indexProgress == IndexProgressType.Halting) {
this._indexProgress = IndexProgressType.Halted;
return;
}
if (err) {
this._indexProgress = IndexProgressType.Done;
genutils_1.ConnectionLogger.error(err);
this._mustSrcFiles = new Set();
this._srcFiles = [];
return;
}
let allFiles = files.concat(mustFiles);
if (allFiles.length > 0) {
this._mustSrcFiles = new Set(mustFiles);
_index(allFiles);
}
else {
this._indexProgress = IndexProgressType.Done;
genutils_1.ConnectionLogger.log("No files to index");
this._srcFiles = [];
}
});
}
}
_loadCachedIndex() {
const forkedCachedIndexLoader = fork(genutils_1.resolvedPath('./cached_index_loader.js'), [], { silent: true });
forkedCachedIndexLoader.on('message', (rawCachedFilesInfo) => {
try {
if (!rawCachedFilesInfo.version || (rawCachedFilesInfo.version != CACHED_INDEX_FILE_VERSION)) {
this._indexIsSaveable = true;
this._cacheLoadingDone = true;
return;
}
let cachedFilesInfo = new Map(rawCachedFilesInfo.info.map(info => [
info[0],
({
symbolsInfo: svparser_1.SystemVerilogParser.jsonToFileSymbolsInfo(info[0], info[1][0]),
pkgdeps: info[1][1],
rank: info[1][2]
})
]));
let cacheFileCount = 0;
for (let file of this._srcFiles) {
if (!this._indexedFilesInfo.has(file) && cachedFilesInfo.has(file)) {
let info = cachedFilesInfo.get(file);
this._updateFileInfo(file, info.symbolsInfo, info.pkgdeps);
cacheFileCount++;
}
}
genutils_1.ConnectionLogger.log(`INFO: Loaded cached index for ${cacheFileCount} files`);
forkedCachedIndexLoader.send('done');
this._cacheLoadingDone = true;
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
}
});
forkedCachedIndexLoader.stdout.on('data', genutils_1.childProcessStdoutRedir);
forkedCachedIndexLoader.stderr.on('data', genutils_1.childProcessStderrRedir);
forkedCachedIndexLoader.send(this.getIndexFile());
}
_setHeaderSymbols(file, preprocIncInfo) {
if (this._indexedFilesInfo.has(file) && (this._indexedFilesInfo.get(file).pkgdeps != null)) {
this._incrementalUpdateFileInfo(file, {}, []);
}
this._indexedFilesInfo.set(file, { symbolsInfo: svparser_1.SystemVerilogParser.preprocToFileSymbolsInfo(preprocIncInfo.symbols, preprocIncInfo.includes), pkgdeps: null, rank: 0 });
}
_buildIndex() {
if (!this._srcFiles) {
return;
}
genutils_1.ConnectionLogger.log(`INFO: Indexing ${this._srcFiles.length} files ...`);
var startTime = performance.now();
const forkedIndexBuilder = fork(genutils_1.resolvedPath('./svindex_builder.js'), [], { silent: true });
let offset = 0;
let waitPreprocCache = false;
forkedIndexBuilder.on('message', ([jsonFileSymbolsInfo, pkgdeps]) => {
try {
if (this._indexProgress == IndexProgressType.Halting) {
forkedIndexBuilder.send(['exit', '']);
this._indexProgress = IndexProgressType.Halted;
return;
}
if (waitPreprocCache) {
for (let [k, v] of svparser_1.SystemVerilogParser.preprocCacheFromJSON(jsonFileSymbolsInfo)) {
this._preprocCache.set(k, v);
this._setHeaderSymbols(v.file, v.info);
}
genutils_1.ConnectionLogger.log(`INFO: Done indexing ${this._srcFiles.length + this._preprocCache.size} files!!!`);
this._indexProgress = IndexProgressType.Done;
var endTime = performance.now();
genutils_1.ConnectionLogger.log(`INFO: Took ${endTime - startTime} milliseconds`);
forkedIndexBuilder.send(['exit', '']);
this._updatePkgFilesInfo();
this.saveIndex();
this._generateVerilatorOptionsFile();
}
else {
//DBG ConnectionLogger.log(`DEBUG: Received ${jsonFileSymbolsInfo.length} symbols and ${pkgdeps.length} pkgdeps for ${this._srcFiles[offset]}`);
let symbolsInfo = svparser_1.SystemVerilogParser.jsonToFileSymbolsInfo(genutils_1.pathToUri(this._srcFiles[offset]), jsonFileSymbolsInfo);
if (this._indexedFilesInfo.has(this._srcFiles[offset])) {
this._incrementalUpdateFileInfo(this._srcFiles[offset], symbolsInfo, pkgdeps);
}
else {
this._updateFileInfo(this._srcFiles[offset], symbolsInfo, pkgdeps);
}
this._indexIsSaveable = true;
if (offset == this._srcFiles.length - 1) {
waitPreprocCache = true;
forkedIndexBuilder.send(['done', '']);
}
else {
offset++;
forkedIndexBuilder.send(['index', this._srcFiles[offset]]);
}
}
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
}
});
forkedIndexBuilder.stdout.on('data', genutils_1.childProcessStdoutRedir);
forkedIndexBuilder.stderr.on('data', genutils_1.childProcessStderrRedir);
forkedIndexBuilder.send(['config', this._srcFiles, this._userDefines.map(d => [d.name, d.tokens])]);
forkedIndexBuilder.send(['index', this._srcFiles[offset]]);
}
_getContainers(symbolsInfo) {
let pkgs = [];
let modules = [];
let interfaces = [];
for (let symbol of svparser_1.SystemVerilogParser.fileAllContainers(symbolsInfo)) {
if (symbol.type[0] == "package") {
pkgs.push(symbol.name);
}
else if (symbol.type[0] == "module") {
modules.push(symbol.name);
}
else if (symbol.type[0] == "interface") {
interfaces.push(symbol.name);
}
}
return { pkgs: pkgs, modules: modules, interfaces: interfaces };
}
_updateFileInfo(file, symbolsInfo, pkgdeps) {
let cntnrs = this._getContainers(symbolsInfo);
for (let pkg of cntnrs.pkgs) {
if (!this._pkgToFiles.has(pkg)) {
this._pkgToFiles.set(pkg, new Set());
}
this._pkgToFiles.get(pkg).add(file);
}
for (let mod of cntnrs.modules) {
if (!this._moduleToFiles.has(mod)) {
this._moduleToFiles.set(mod, new Set());
}
this._moduleToFiles.get(mod).add(file);
}
for (let intf of cntnrs.interfaces) {
if (!this._interfaceToFiles.has(intf)) {
this._interfaceToFiles.set(intf, new Set());
}
this._interfaceToFiles.get(intf).add(file);
}
this._indexedFilesInfo.set(file, { symbolsInfo: symbolsInfo, pkgdeps: cntnrs.pkgs.length > 0 ? pkgdeps : null, rank: 0 });
this._fileToContainers.set(file, cntnrs);
}
_updatePkgFilesInfo() {
let fileDepsInfo = new Map();
this._indexedFilesInfo.forEach((info, file) => {
info.rank = 0;
if (info.pkgdeps != null) {
let fileDeps = new Set();
for (let pkg of info.pkgdeps) {
if (this._pkgToFiles.has(pkg)) {
for (let fileDep of this._pkgToFiles.get(pkg)) {
if (fileDep == file) {
continue;
}
fileDeps.add(fileDep);
}
}
}
fileDepsInfo.set(file, fileDeps);
}
});
this._indexedFilesInfo.forEach((info, file) => {
if (info.pkgdeps != null) {
let visitedFiles = [file];
this._incDepsRank(file, visitedFiles, fileDepsInfo);
}
});
}
_incDepsRank(file, visitedFiles, fileDepsInfo, indent = 0) {
for (let fileDep of fileDepsInfo.get(file)) {
if (visitedFiles.indexOf(fileDep) >= 0) {
genutils_1.ConnectionLogger.error(`ERROR: Found cycle in ${visitedFiles}`);
return;
}
let fileDepInfo = this._indexedFilesInfo.get(fileDep);
let fileInfo = this._indexedFilesInfo.get(file);
if (fileDepInfo.rank <= fileInfo.rank) {
fileDepInfo.rank = fileInfo.rank + 1;
//ConnectionLogger.log(`${' '.repeat(indent)} DEBUG: incrementing rank of ${fileDep} because of ${file}`);
this._incDepsRank(fileDep, [fileDep].concat(visitedFiles), fileDepsInfo, indent + 1);
}
}
}
_generateVerilatorOptionsFile() {
this._optionsFileContent = [];
for (let [file, rank] of [...[...this._indexedFilesInfo.entries()].filter(a => a[1].pkgdeps != null)].sort((a, b) => a[1].rank <= b[1].rank ? 1 : -1)) {
if (this._mustSrcFiles.has(file)) {
this._optionsFileContent.push(file);
}
else {
this._optionsFileContent.push('-v ' + file);
}
}
for (let libfile of [...new Set(this._libFiles.values())]) {
this._optionsFileContent.push('-v ' + libfile);
}
for (let incdir of [...new Set(this._srcFiles.map(file => path.dirname(file))), ...new Set(this._srcFiles.map(file => path.dirname(file)))]) {
this._optionsFileContent.push('+incdir+' + incdir);
}
genutils_1.fsWriteFile(this.getLinterOptionsFile(), this._optionsFileContent.join('\n'))
.catch(error => {
genutils_1.ConnectionLogger.error(error);
});
}
getOptionsFileContent() {
return this._optionsFileContent;
}
fileHasPkg(file) {
return this._indexedFilesInfo.has(file) && (this._indexedFilesInfo.get(file).pkgdeps != null);
}
isMustSrcFile(file) {
return this._mustSrcFiles.has(file);
}
processDocumentChanges(document) {
let text = document.getText();
let tokens = this._completionGrammarEngine.tokenize(text);
let file = genutils_1.uriToPath(document.uri);
this._filesCompletionInfo.set(file, { tokens: tokens });
}
indexOpenDocument(document, retryCount = 0) {
if (this._srcFiles == undefined) {
if (retryCount < MAX_RETRY_COUNT) {
setTimeout(() => {
try {
this.indexOpenDocument(document, retryCount + 1);
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
}
}, RETRY_DELAY_MS);
}
else {
genutils_1.ConnectionLogger.error(`Timeout trying to index document ${document.uri}`);
}
return;
}
let file = genutils_1.uriToPath(document.uri);
if (this._preprocCache.has(file)) {
return;
}
this.processDocumentChanges(document);
if (!this._lastSavedFilesInfo.has(file) && this._indexedFilesInfo.has(file)) {
this._lastSavedFilesInfo.set(file, this._indexedFilesInfo.get(file));
}
let fileSymbolsInfo;
let pkgdeps;
let parser = new svparser_1.SystemVerilogParser();
let userDefinesMacroInfo = new Map(this._userDefines.map(d => [d.name, { args: undefined, default: undefined, definition: d.tokens, symbol: undefined, file: "" }]));
[fileSymbolsInfo, pkgdeps] = parser.parse(document, this._srcFiles, this._preprocCache, userDefinesMacroInfo, "full", undefined);
let rank = this._indexedFilesInfo.has(file) ? this._indexedFilesInfo.get(file).rank : 0;
let pkgs = this._getContainers(fileSymbolsInfo).pkgs;
this._indexedFilesInfo.set(file, { symbolsInfo: fileSymbolsInfo, pkgdeps: pkgs.length > 0 ? pkgdeps : null, rank: rank });
for (let entry of this._preprocCache.values()) {
this._setHeaderSymbols(entry.file, entry.info);
}
}
updateFileInfoOnSave(document) {
let file = genutils_1.uriToPath(document.uri);
let info = this._indexedFilesInfo.get(file);
let symbolsInfo = info.symbolsInfo;
let pkgdeps = info.pkgdeps == null ? [] : info.pkgdeps;
this._indexIsSaveable = true;
if (this._lastSavedFilesInfo.has(file)) {
this._indexedFilesInfo.set(file, this._lastSavedFilesInfo.get(file));
this._lastSavedFilesInfo.delete(file);
}
this._incrementalUpdateFileInfo(file, symbolsInfo, pkgdeps);
for (let entry of this._preprocCache.values()) {
this._indexedFilesInfo.set(entry.file, { symbolsInfo: symbolsInfo, pkgdeps: null, rank: 0 });
}
}
_incrementalUpdateFileInfo(file, symbolsInfo, pkgdeps) {
let cntnrs = this._getContainers(symbolsInfo);
let oldcntnrs = this._fileToContainers.has(file) ? this._fileToContainers.get(file) : { pkgs: [], modules: [], interfaces: [] };
let pkgs = cntnrs.pkgs;
let oldpkgs = oldcntnrs.pkgs;
let addedPkgs = [];
for (let pkg of pkgs) {
if (oldpkgs.indexOf(pkg) < 0) {
addedPkgs.push(pkg);
}
}
let deletedPkgs = [];
for (let pkg of oldpkgs) {
if (pkgs.indexOf(pkg) < 0) {
deletedPkgs.push(pkg);
}
}
let newpkgdeps = pkgs.length > 0 ? pkgdeps : null;
let oldpkgdeps = this._indexedFilesInfo.has(file) ? this._indexedFilesInfo.get(file).pkgdeps : null;
let _newpkgdeps = (newpkgdeps == null) ? [] : newpkgdeps;
let _oldpkgdeps = (oldpkgdeps == null) ? [] : oldpkgdeps;
let addedPkgDeps = [];
for (let pkgdep of _newpkgdeps) {
if (_oldpkgdeps.indexOf(pkgdep) < 0) {
addedPkgDeps.push(pkgdep);
}
}
let deletedPkgDeps = [];
for (let pkgdep of _oldpkgdeps) {
if (_newpkgdeps.indexOf(pkgdep) < 0) {
deletedPkgDeps.push(pkgdep);
}
}
this._fileToContainers.set(file, cntnrs);
let rank = this._indexedFilesInfo.has(file) ? this._indexedFilesInfo.get(file).rank : 0;
this._indexedFilesInfo.set(file, { symbolsInfo: symbolsInfo, pkgdeps: newpkgdeps, rank: rank });
if (!!addedPkgs.length || !!deletedPkgs.length || !!addedPkgDeps.length || !!deletedPkgDeps.length) {
for (let addedPkg of addedPkgs) {
if (!this._pkgToFiles.has(addedPkg)) {
this._pkgToFiles.set(addedPkg, new Set());
}
this._pkgToFiles.get(addedPkg).add(file);
}
for (let deletedPkg of deletedPkgs) {
this._pkgToFiles.get(deletedPkg).delete(file);
if (this._pkgToFiles.get(deletedPkg).size <= 0) {
this._pkgToFiles.delete(deletedPkg);
}
}
this._updatePkgFilesInfo();
this._generateVerilatorOptionsFile();
}
for (let mod of oldcntnrs.modules) {
if (this._moduleToFiles.has(mod)) {
this._moduleToFiles.get(mod).delete(file);
if (this._moduleToFiles.get(mod).size <= 0) {
this._moduleToFiles.delete(mod);
}
}
}
for (let mod of cntnrs.modules) {
if (!this._moduleToFiles.has(mod)) {
this._moduleToFiles.set(mod, new Set());
}
this._moduleToFiles.get(mod).add(file);
}
for (let intf of oldcntnrs.interfaces) {
if (this._interfaceToFiles.has(intf)) {
this._interfaceToFiles.get(intf).delete(file);
if (this._interfaceToFiles.get(intf).size <= 0) {
this._interfaceToFiles.delete(intf);
}
}
}
for (let intf of cntnrs.interfaces) {
if (!this._interfaceToFiles.has(intf)) {
this._interfaceToFiles.set(intf, new Set());
}
this._interfaceToFiles.get(intf).add(file);
}
}
_indexToCache() {
return JSON.stringify({
version: CACHED_INDEX_FILE_VERSION,
info: Array.from(this._indexedFilesInfo, info => [info[0], [svparser_1.SystemVerilogParser.fileSymbolsInfoToJson(info[1].symbolsInfo), info[1].pkgdeps, info[1].rank]])
});
}
saveIndex() {
if (!this._indexIsSaveable) {
return;
}
return genutils_1.fsWriteFile(this.getIndexFile(), this._indexToCache())
.then(() => {
this._indexIsSaveable = false;
})
.catch(error => {
genutils_1.ConnectionLogger.error(error);
});
}
saveIndexOnExit() {
if (!this._indexIsSaveable || !this._cacheLoadingDone) {
return;
}
this._indexIsSaveable = false;
genutils_1.fsWriteFileSync(this.getIndexFile(), this._indexToCache());
}
getDocumentSystemVerilogSymbols(documentUri, strict = true) {
if (!this._indexedFilesInfo.has(genutils_1.uriToPath(documentUri))) {
return [];
}
return svparser_1.SystemVerilogParser.fileAllSymbols(this._indexedFilesInfo.get(genutils_1.uriToPath(documentUri)).symbolsInfo, strict);
}
getDocumentSymbols(document, retryCount = 0) {
if (!this._indexedFilesInfo.has(genutils_1.uriToPath(document.uri))) {
return new Promise(resolve => {
setTimeout(resolve, RETRY_DELAY_MS);
}).then(() => {
if (retryCount < MAX_RETRY_COUNT) {
return this.getDocumentSymbols(document, retryCount + 1);
}
else {
genutils_1.ConnectionLogger.error(`Timeout trying to get document symbols for ${document.uri}`);
}
return [];
}).catch(error => {
genutils_1.ConnectionLogger.error(error);
return [];
});
}
return Promise.resolve(this.getDocumentSystemVerilogSymbols(document.uri).map(symbol => symbol.toSymbolInformation(document.uri)));
}
_getWorkspaceSymbols(query, svtype) {
const pattern = new RegExp(".*" + query.replace(" ", "").split("").map((c) => c).join(".*") + ".*", 'i');
let results = new Array();
let exactMatch = false;
if (query.startsWith("¤")) {
exactMatch = true;
query = query.substr(1);
}
this._indexedFilesInfo.forEach((info, file) => {
let symbols = svparser_1.SystemVerilogParser.fileAllSymbols(info.symbolsInfo);
symbols.forEach(symbol => {
if (exactMatch === true) {
if (symbol.name == query) {
if (svtype) {
results.push(symbol);
}
else {
results.push(symbol.toSymbolInformation(genutils_1.pathToUri(file)));
}
}
}
else if (symbol.name.match(pattern)) {
if (svtype) {
results.push(symbol);
}
else {
results.push(symbol.toSymbolInformation(genutils_1.pathToUri(file)));
}
}
});
});
return results;
}
getWorkspaceSystemVerilogSymbols(query) {
return new Promise((resolve, reject) => {
if (query == undefined || query.length === 0) {
genutils_1.ConnectionLogger.error(`getWorkspaceSystemVerilogSymbols does not accept empty/undefined query`);
resolve([]);
}
else {
resolve((this._getWorkspaceSymbols(query, true)));
}
});
}
getWorkspaceSymbols(query) {
return new Promise((resolve, reject) => {
if (query == undefined || query.length === 0) {
resolve(this.mostRecentSymbols);
}
else {
let results = (this._getWorkspaceSymbols(query, false));
this.updateMostRecentSymbols(results.slice(0)); //pass a shallow copy of the array
resolve(results);
}
}).catch(error => {
genutils_1.ConnectionLogger.error(error);
return [];
});
}
/**
Updates `mostRecentSymbols` with the most recently used symbols
When `mostRecentSymbols` is undefined, add the top `this.NUM_FILES` symbol from `this._indexedFilesInfo`
When `mostRecentSymbols` is defined, add the symbols in `recentSymbols` one by one to the top of the array
@param recentSymbols the recent symbols
*/
updateMostRecentSymbols(recentSymbols) {
if (this.mostRecentSymbols) {
if (!recentSymbols) {
return;
}
while (recentSymbols.length > 0) {
let currentSymbol = recentSymbols.pop();
//if symbol already exists, remove it
for (let i = 0; i < this.mostRecentSymbols.length; i++) {
let symbol = this.mostRecentSymbols[i];
if (symbol == currentSymbol) {
this.mostRecentSymbols.splice(i, 1);
break;
}
}
//if the array has reached maximum capacity, remove the last element
if (this.mostRecentSymbols.length >= this.NUM_FILES) {
this.mostRecentSymbols.pop();
}
//add the symbol to the top of the array
this.mostRecentSymbols.unshift(currentSymbol);
}
}
else {
let maxSymbols = [];
//collect the top symbols in `this.symbols`
for (var list of this._indexedFilesInfo.entries()) {
let symbols = svparser_1.SystemVerilogParser.fileAllSymbols(list[1].symbolsInfo);
if (maxSymbols.length + symbols.length >= this.NUM_FILES) {
let limit = this.NUM_FILES - maxSymbols.length;
maxSymbols = maxSymbols.concat(symbols.slice(-1 * limit).map(symbol => symbol.toSymbolInformation(genutils_1.pathToUri(list[0]))));
break;
}
else {
maxSymbols = maxSymbols.concat(symbols.map(symbol => symbol.toSymbolInformation(genutils_1.pathToUri(list[0]))));
}
}
this.mostRecentSymbols = maxSymbols;
}
}
getSystemVerilogCompletionTokens(uri) {
return this._filesCompletionInfo.get(genutils_1.uriToPath(uri)).tokens || [];
}
leftExtendHierToken(tokenNum, tokens) {
let extTokenNum = tokenNum;
let scope = tokens[tokenNum].scopes[tokens[tokenNum].scopes.length - 1];
if ((!scope.startsWith("identifier.")) || (scope.startsWith("identifier.scoped"))) {
return extTokenNum;
}
let lookingForSeparator = true;
for (let i = tokenNum - 1; i >= 0; i--) {
if (lookingForSeparator) {
if (tokens[i].text == ".") {
lookingForSeparator = false;
}
else {
break;
}
}
else {
let scopeDepth = tokens[i].scopes.length - 1;
scope = tokens[i].scopes[scopeDepth];
if (scope == "meta.whitespace.systemverilog") {
continue;
}
let j = i;
if ((!scope.startsWith("identifier.")) && (tokens[i].text == "]")) {
for (j = i - 1; j >= 0; j--) {
if (scopeDepth == tokens[j].scopes.length) {
break;
}
}
j--;
if (j < 0) {
break;
}
scope = tokens[j].scopes[tokens[j].scopes.length - 1];
}
if (scope.startsWith("identifier.") && (!scope.startsWith("identifier.scoped."))) {
extTokenNum = j;
}
else {
break;
}
lookingForSeparator = true;
}
}
return extTokenNum;
}
getSystemVerilogCompletionTokenNumber(document, line, character) {
let file = genutils_1.uriToPath(document.uri);
let svtokennum = -1;
const svtokens = this._filesCompletionInfo.get(file).tokens || [];
if ((svtokens.length > 0) && (character > 0)) {
//const offset: number = fileCompletionInfo.offsets[line] + character - 1;
const offset = document.offsetAt(vscode_languageserver_types_1.Position.create(line, character - 1));
let firstToken = 0;
let lastToken = svtokens.length - 1;
//ConnectionLogger.log(`DEBUG: Trying to find token at ${offset} with ${firstToken}:${svtokens[firstToken].index} - ${lastToken}:${svtokens[lastToken].index + svtokens[lastToken].text.length}`);
while ((offset >= svtokens[firstToken].index) && (offset < (svtokens[lastToken].index + svtokens[lastToken].text.length))) {
let prevFirstToken = firstToken;
let prevLastToken = lastToken;
let midToken = ~~((firstToken + lastToken) / 2);
//ConnectionLogger.log(`DEBUG: Trying ${midToken} at ${svtokens[midToken].index} - ${svtokens[midToken].index + svtokens[midToken].text.length}`);
if (offset < svtokens[midToken].index) {
lastToken = midToken;
}
else if (offset >= (svtokens[midToken].index + svtokens[midToken].text.length)) {
firstToken = midToken;
}
else {
//ConnectionLogger.log(`DEBUG: Found ${midToken} at ${svtokens[midToken].index} with text "${svtokens[midToken].text}"`);
svtokennum = midToken;
break;
}
if ((prevFirstToken == firstToken) && (prevLastToken == lastToken)) {
genutils_1.ConnectionLogger.error(`Tokenization might be incorrect as token search ran into infinite loop at ${firstToken} - ${lastToken}`);
break;
}
}
}
else if (svtokens.length > 0) {
// DEBUG
//for (let i = 0; i < svtokens.length; i++) {
// const token = svtokens[i];
// const text = svtokens[i].text;
// ConnectionLogger.log(` - token ${i} at ${token.index} (${text}) with scopes ${token.scopes.join(', ')}`
// );
//}
}
return [this.leftExtendHierToken(svtokennum, svtokens), svtokennum];
}
getPackages() {
return Array.from(this._pkgToFiles.keys());
}
getInstFilePath(instType) {
if (this._moduleToFiles.has(instType)) {
return [...this._moduleToFiles.get(instType)][0];
}
else if (this._interfaceToFiles.has(instType)) {
return [...this._interfaceToFiles.get(instType)][0];
}
return undefined;
}
_getContainerSymbols(cntnrToFiles, cntnrName, filter, firstMatch = false) {
let syms = [];
if (cntnrToFiles.has(cntnrName)) {
for (let file of cntnrToFiles.get(cntnrName)) {
if (!this._indexedFilesInfo.has(file)) {
continue;
}
//TBD (implementing temp solution)
let symbols = svparser_1.SystemVerilogParser.fileAllSymbols(this._indexedFilesInfo.get(file).symbolsInfo, false);
for (let symbol of symbols) {
if ((symbol.containers.indexOf(cntnrName) >= 0) && filter(symbol)) {
syms.push(symbol);
}
}
if (firstMatch) {
break;
}
}
}
return syms;
}
getPackageSymbols(pkg) {
return this._getContainerSymbols(this._pkgToFiles, pkg, (symbol) => { return true; });
}
getInstParams(instType, firstMatch = false) {
let result = [];
result = result.concat(this._getContainerSymbols(this._moduleToFiles, instType, (symbol) => { return symbol.type[0] == "parameter-port"; }));
if ((result.length == 0) || (!firstMatch)) {
result = result.concat(this._getContainerSymbols(this._interfaceToFiles, instType, (symbol) => { return symbol.type[0] == "parameter-port"; }));
}
return result;
}
getInstPorts(instType, firstMatch = false) {
let result = [];
result = result.concat(this._getContainerSymbols(this._moduleToFiles, instType, (symbol) => { return symbol.type[0] == "port"; }));
if ((result.length == 0) || (!firstMatch)) {
result = result.concat(this._getContainerSymbols(this._interfaceToFiles, instType, (symbol) => { return symbol.type[0] == "port"; }));
}
return result;
}
_getContainerSymbol(cntnrToFiles, cntnrName) {
if (cntnrToFiles.has(cntnrName)) {
for (let file of cntnrToFiles.get(cntnrName)) {
if (!this._indexedFilesInfo.has(file)) {
continue;
}
for (let symbol of svparser_1.SystemVerilogParser.fileAllContainers(this._indexedFilesInfo.get(file).symbolsInfo)) {
if (symbol.name == cntnrName) {
return [file, symbol];
}
}
}
}
return [undefined, undefined];
}
getContainerSymbol(cntnrName) {
let result = this._getContainerSymbol(this._moduleToFiles, cntnrName)[1];
if (result == undefined) {
result = this._getContainerSymbol(this._interfaceToFiles, cntnrName)[1];
}
return result;
}
getPackageSymbol(pkgName) {
let sym;
let filePath;
[filePath, sym] = this._getContainerSymbol(this._pkgToFiles, pkgName);
return [filePath == undefined ? filePath : genutils_1.pathToUri(filePath), sym];
}
_findPackageSymbol(pkgName, unscopedSymbolName, findContainer) {
// given package and symbol name, get container info
// find through all symbols
// otherwise find through all exports
let pkgQ = [pkgName];
while (pkgQ.length > 0) {
let pkgItem = pkgQ.shift();
if (this._pkgToFiles.has(pkgItem)) {
for (let pkgFilePath of this._pkgToFiles.get(pkgItem)) {
if (this._indexedFilesInfo.has(pkgFilePath)) {
let pkgFileSymbolsInfo = this._indexedFilesInfo.get(pkgFilePath).symbolsInfo;
let pkgContainer;
if (pkgFileSymbolsInfo.containersInfo != undefined) {
for (let cntnr of pkgFileSymbolsInfo.containersInfo) {
if ((cntnr.symbol.name == pkgItem) && (cntnr.symbol.type[0] == "package")) {
pkgContainer = cntnr;
break;
}
}
}
if (pkgContainer != undefined) {
if (!findContainer &&
(pkgContainer.info.symbolsInfo != undefined)) {
let symbol = pkgContainer.info.symbolsInfo.find(sym => {
return sym.name == unscopedSymbolName;
});
if (symbol != undefined) {
return [genutils_1.pathToUri(pkgFilePath), symbol, {}];
}
}
if (pkgContainer.info.containersInfo != undefined) {
let cntnrInfo = pkgContainer.info.containersInfo.find(cntnr => {
return cntnr.symbol.name == unscopedSymbolName;
});
if (cntnrInfo != undefined) {
return [genutils_1.pathToUri(pkgFilePath), cntnrInfo.symbol, cntnrInfo.info];
}
}
let containerExportsInfo = svparser_1.SystemVerilogParser.containerExports(pkgContainer.info);
for (let exportItem of containerExportsInfo) {
if (exportItem.pkg == "*") {
for (let pkgImport of svparser_1.SystemVerilogParser.containerImports(pkgContainer.info)) {
if ((pkgImport.symbolsText.length == 1) && (pkgImport.symbolsText[0] == "*")) {
pkgQ.unshift(pkgImport.pkg);
}
else if (pkgImport.symbolsText.includes(unscopedSymbolName)) {
pkgQ.unshift(pkgImport.pkg);
}
}
}
else if ((exportItem.symbolsText.length == 1) &&
(exportItem.symbolsText[0] == "*")) {
pkgQ.unshift(exportItem.pkg);
}
else if (exportItem.symbolsText.includes(unscopedSymbolName)) {
pkgQ.unshift(exportItem.pkg);
}
}
}
}
}
}
}
return [undefined, undefined, {}];
}
_findSymbol(uri, symbolName, findContainer) {
let scopedParts = symbolName.split('::');
if (scopedParts.length == 1) {
let filePath = genutils_1.uriToPath(uri);
if (!this._indexedFilesInfo.has(filePath)) {
return [undefined, undefined, {}];
}
let symbolsInfo = this._indexedFilesInfo.get(filePath).symbolsInfo;
if (findContainer) {
let containerInfo = (svparser_1.SystemVerilogParser.findSymbol(symbolsInfo, symbolName, true));
if (containerInfo.symbol != undefined) {
return [uri, containerInfo.symbol, containerInfo.info];
}
}
else {
let symbol = (svparser_1.SystemVerilogParser.findSymbol(symbolsInfo, symbolName, false));
if (symbol != undefined) {
return [uri, symbol, {}];
}
}
}
let pkgName;
let unscopedSymbolName;
if (scopedParts.length > 1) {
pkgName = scopedParts[0];
unscopedSymbolName = scopedParts[1];
}
else {
let fileImports = this.getFileImports(uri);
for (let fileImport of fileImports) {
if (fileImport[1].includes(symbolName)) {
pkgName = fileImport[0];
unscopedSymbolName = symbolName;
break;
}
}
}
if ((pkgName == undefined) || (unscopedSymbolName == undefined)) {
return [undefined, undefined, {}];
}
return this._findPackageSymbol(pkgName, unscopedSymbolName, findContainer);
}
getContainerInfo(uri, containerName) {
return this._findSymbol(uri, containerName, true);
}
findSymbol(uri, symbolName) {
return (this._findSymbol(uri, symbolName, false).slice(0, 2));
}
findScopedSymbol(uri, symbolName, position) {
let _isBefore = (pos1, pos2) => {
return ((pos1.line < pos2.line) ||
((pos1.line == pos2.line) && (pos1.character <= pos2.character)));
};
let _getScopedSymbolBeforePosition = (symbols) => {
let scopedSymbol;
let scopedLocation;
symbols.forEach(sym => {
if (sym.name == symbolName) {
let symLocation = sym.getSymbolLocation(uri);
if ((symLocation.uri == uri) && _isBefore(symLocation.range.start, position)) {
if (scopedLocation == undefined) {
scopedSymbol = sym;
scopedLocation = symLocation.range.start;
}
else if (_isBefore(scopedLocation, symLocation.range.start)) {
scopedSymbol = sym;
scopedLocation = symLocation.range.start;
}
}
else if (scopedSymbol == undefined) {
scopedSymbol = sym;
}
}
});
return [scopedSymbol == undefined ? undefined : uri, scopedSymbol];
};
let _getScopedSymbolInContainerContainingPosition = (containersInfo) => {
let scopedFile;
let scopedSymbol;
for (let cntnrInfo of containersInfo) {
let cntnrStart = cntnrInfo.symbol.getSymbolLocation(uri);
let cntnrEnd = cntnrInfo.position.file == undefined ? vscode_languageserver_types_1.Position.create(cntnrInfo.position.line, cntnrInfo.position.character) : undefined;
if ((cntnrStart.uri == uri) && _isBefore(cntnrStart.range.start, position) &&
(cntnrEnd !== undefined) && _isBefore(position, cntnrEnd)) {
let cntnrs = svparser_1.SystemVerilogParser.containerContainers(cntnrInfo);
[scopedFile, scopedSymbol] = _getScopedSymbolInContainerContainingPosition(cntnrs);
if ((scopedFile == undefined) || (scopedSymbol == undefined)) {
[scopedFile, scopedSymbol] = _getScopedSymbolBeforePosition(cntnrs.map(cntnr => cntnr.symbol).concat(svparser_1.SystemVerilogParser.containerTopSymbols(cntnrInfo)));
}
if (scopedSymbol == undefined) {
//imported symbols
let cntnrImports = svparser_1.SystemVerilogParser.containerImports(cntnrInfo.info, true);
let resolvedPkgSymNames = this._resolveImportsIntoSymbolNames(cntnrImports);
for (let pkgSymNames of resolvedPkgSymNames) {
let symIndx = pkgSymNames[1].indexOf(symbolName);
if (symIndx >= 0) {
[scopedFile, scopedSymbol] = this._findPackageSymbol(pkgSymNames[0], symbolName, false);
break;
}
}
}
if ((scopedFile != undefined) && (scopedSymbol != undefined)) {
break;
}
}
}
return [scopedFile, scopedSymbol];
};
let filePath = genutils_1.uriToPath(uri);
if (!this._indexedFilesInfo.has(filePath)) {
return [undefined, undefined];
}
let symbolsInfo = this._indexedFilesInfo.get(filePath).symbolsInfo;
let containerScopedFile;
let containerScopedSymbol;
[containerScopedFile, containerScopedSymbol] = _getScopedSymbolInContainerContainingPosition(svparser_1.SystemVerilogParser.fileContainers(symbolsInfo));
if ((containerScopedFile == undefined) || (containerScopedSymbol == undefined)) {
let scopedFile;
let scopedSymbol;
let fileTopSymbols = svparser_1.SystemVerilogParser.fileTopSymbols(symbolsInfo);
if (fileTopSymbols != undefined) {
[scopedFile, scopedSymbol] = _getScopedSymbolBeforePosition(fileTopSymbols);
}
return [scopedFile, scopedSymbol];
}
return [containerScopedFile, containerScopedSymbol];
}
getMacros(fileUri, macroName) {
let result = [];
let filePath = genutils_1.uriToPath(fileUri);
if (this._indexedFilesInfo.has(filePath)) {
let symbols = [];
let fileSymbolsInfo = this._indexedFilesInfo.get(filePath).symbolsInfo;
if (fileSymbolsInfo.symbolsInfo != undefined) {
if (macroName == undefined) {
symbols = symbols.concat(fileSymbolsInfo.symbolsInfo.filter(sym => { return sym.type[0] == "macro"; }));
}
else {
symbols = fileSymbolsInfo.symbolsInfo.filter(sym => { return (sym