UNPKG

@imc-trading/svlangserver

Version:
1,082 lines (1,081 loc) 65.4 kB
"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