UNPKG

@imc-trading/svlangserver

Version:
187 lines (186 loc) 7.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VerilatorDiagnostics = void 0; const vscode_languageserver_1 = require("vscode-languageserver"); const genutils_1 = require("./genutils"); const child = require("child_process"); const path = require('path'); class VerilatorDiagnostics { constructor(indexer) { this._command = ""; this._defines = []; this._optionsFile = ""; this._alreadyRunning = new Map(); this._fileWaiting = new Map(); this._freeTmpFileNums = []; this._totalTmpFileNums = 0; this._indexer = indexer; this._tmpDir = genutils_1.getTmpDirSync(); } setCommand(cmd) { this._command = cmd; } setOptionsFile(file) { this._optionsFile = file; } setDefines(defines) { this._defines = defines || []; } _getFreeTmpFileNum() { if (this._freeTmpFileNums.length <= 0) { this._freeTmpFileNums.push(this._totalTmpFileNums++); } return this._freeTmpFileNums.shift(); } _lintImmediate(file, text) { let _kill = () => { let [proc, statusRef] = this._alreadyRunning.get(file); statusRef[0] = false; proc.kill(); }; if (this._alreadyRunning.has(file)) { //ConnectionLogger.log(`DEBUG: Killing already running command to start a new one`); _kill(); } return new Promise((resolve) => { let actFile = text == undefined ? file : path.join(this._tmpDir.name, "sources", file); let optionsFile = this._optionsFile; let vcTmpFileNum; if (text != undefined) { genutils_1.fsWriteFileSync(actFile, text); // if file write takes too long and another process started in the interim if (this._alreadyRunning.has(file)) { //ConnectionLogger.log(`DEBUG: Killing already running command to start a new one`); _kill(); } if (this._indexer.fileHasPkg(file)) { let vcFileContent = this._indexer.getOptionsFileContent() .map(line => { return (line == file) ? actFile : line; }) .join('\n'); vcTmpFileNum = this._getFreeTmpFileNum(); let tmpVcFile = path.join(this._tmpDir.name, "vcfiles", `lint${vcTmpFileNum}.vc`); genutils_1.fsWriteFileSync(tmpVcFile, vcFileContent); optionsFile = tmpVcFile; // if file write takes too long and another process started in the interim if (this._alreadyRunning.has(file)) { //ConnectionLogger.log(`DEBUG: Killing already running command to start a new one`); _kill(); } } } let definesArg = this._defines.length > 0 ? this._defines.map(d => ` +define+${d}`).join('') : ""; let optionsFileArg = optionsFile ? ' -f ' + optionsFile : ""; let actFileArg = (this._indexer.fileHasPkg(file)) ? "" : " " + actFile; let command = this._command + definesArg + optionsFileArg + actFileArg; let statusRef = [true]; //ConnectionLogger.log(`DEBUG: verilator command ${command}`); this._alreadyRunning.set(file, [ child.exec(command, (error, stdout, stderr) => { if (optionsFile != this._optionsFile) { this._freeTmpFileNums.push(vcTmpFileNum); } if (statusRef[0]) { this._alreadyRunning.delete(file); resolve(this._parseDiagnostics(error, stdout, stderr, actFile)); } else { resolve([]); } }), statusRef ]); }); } lint(file, text) { try { if (text == undefined) { return this._lintImmediate(file, text) .catch(error => { genutils_1.ConnectionLogger.error(error); return []; }); } else { if (this._fileWaiting.has(file)) { let [waitTimer, resolver] = this._fileWaiting.get(file); clearTimeout(waitTimer); resolver(false); } return new Promise(resolve => { this._fileWaiting.set(file, [setTimeout(resolve, 1000, true), resolve]); }).then((success) => { if (!!success) { this._fileWaiting.delete(file); return this._lintImmediate(file, text); } return []; }).catch(error => { genutils_1.ConnectionLogger.error(error); return []; }); } } catch (error) { genutils_1.ConnectionLogger.error(error); return Promise.resolve([]); } } _parseDiagnostics(error, stdout, stderr, file) { let diagnostics = []; let lines = stderr.split(/\r?\n/g); // RegExp expression for matching Verilator messages // Group 1: Severity // Group 2: Type (optional) // Group 3: Filename // Group 4: Line number // Group 5: Column number (optional) // Group 6: Message let regex = new RegExp(String.raw `%(Error|Warning)(-[A-Z0-9_]+)?: (` + file.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + String.raw `):(\d+):(?:(\d+):)? (.*)`, 'i'); // Parse output lines lines.forEach((line, i) => { let terms = line.match(regex); if (terms != null) { let severity = this._getSeverity(terms[1]); let message = ""; let lineNum = parseInt(terms[4]) - 1; let colNum = 0; if (terms[5]) { colNum = parseInt(terms[5]) - 1; } message = terms[6]; for (let whitelistedMessage of VerilatorDiagnostics._whitelistedMessages) { if (message.search(whitelistedMessage) >= 0) { return; } } if ((lineNum != NaN) && (colNum != NaN)) { diagnostics.push({ severity: severity, range: vscode_languageserver_1.Range.create(lineNum, colNum, lineNum, Number.MAX_VALUE), message: message, code: 'verilator', source: 'verilator' }); } } }); return diagnostics; } _getSeverity(severityString) { let result = vscode_languageserver_1.DiagnosticSeverity.Information; if (severityString.startsWith('Error')) { result = vscode_languageserver_1.DiagnosticSeverity.Error; } else if (severityString.startsWith('Warning')) { result = vscode_languageserver_1.DiagnosticSeverity.Warning; } return result; } cleanupTmpFiles() { this._tmpDir.removeCallback(); } } exports.VerilatorDiagnostics = VerilatorDiagnostics; VerilatorDiagnostics._whitelistedMessages = [ /Unsupported: Interfaced port on top level module/i ];