diff --git a/src/function/lsp/linter/modelsim.ts b/src/function/lsp/linter/modelsim.ts new file mode 100644 index 0000000..8b478ea --- /dev/null +++ b/src/function/lsp/linter/modelsim.ts @@ -0,0 +1,146 @@ +import * as vscode from "vscode"; +import * as fs from 'fs'; + +import { LspOutput, ReportType, opeParam } from "../../../global"; +import { Path } from "../../../../resources/hdlParser"; +import { hdlFile, hdlPath } from "../../../hdlFs"; +import { easyExec } from "../../../global/util"; +import { BaseLinter } from "./base"; +import { HdlLangID } from "../../../global/enum"; + +class ModelsimLinter implements BaseLinter { + diagnostic: vscode.DiagnosticCollection; + executableFileMap: Map = new Map(); + executableInvokeNameMap: Map = new Map(); + linterArgsMap: Map = new Map(); + + constructor() { + this.diagnostic = vscode.languages.createDiagnosticCollection(); + + // configure map for executable file name + this.executableFileMap.set(HdlLangID.Verilog, 'xvlog'); + this.executableFileMap.set(HdlLangID.Vhdl, 'xvhdl'); + this.executableFileMap.set(HdlLangID.SystemVerilog, 'xvlog'); + this.executableFileMap.set(HdlLangID.Unknown, undefined); + + // configure map for argruments when lintering + this.linterArgsMap.set(HdlLangID.Verilog, ['--nolog']); + this.linterArgsMap.set(HdlLangID.Vhdl, ['--nolog']); + this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--sv', '--nolog']); + this.linterArgsMap.set(HdlLangID.Unknown, []); + + this.initialise(HdlLangID.Verilog); + this.initialise(HdlLangID.Vhdl); + this.initialise(HdlLangID.SystemVerilog); + } + + + async lint(document: vscode.TextDocument) { + const filePath = hdlPath.toSlash(document.fileName); + const langID = hdlFile.getLanguageId(filePath); + + // acquire install path + const args = [hdlPath.toSlash(filePath), ...this.linterArgsMap]; + const executor = this.executableInvokeNameMap.get(langID); + if (executor !== undefined) { + const { stdout, stderr } = await easyExec(executor, args); + if (stdout.length > 0) { + const diagnostics = this.provideDiagnostics(document, stdout); + this.diagnostic.set(document.uri, diagnostics); + } + } else { + LspOutput.report('linter is not available, please check prj.vivado.install.path in your setting', ReportType.Error); + } + } + + /** + * @param document + * @param stdout stdout from xvlog + * @returns { vscode.Diagnostic[] } linter info + */ + private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] { + const diagnostics = []; + for (const line of stdout.split('\n')) { + const tokens = line.split(/:?\s*(?:\[|\])\s*/); + const headerInfo = tokens[0]; + // const standardInfo = tokens[1]; + const syntaxInfo = tokens[2]; + const parsedPath = tokens[3]; + if (headerInfo === 'ERROR') { + const errorInfos = parsedPath.split(':'); + const errorLine = parseInt(errorInfos[errorInfos.length - 1]); + const range = this.makeCorrectRange(document, errorLine); + const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error); + diagnostics.push(diag); + } + } + return diagnostics; + } + + private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range { + const startPosition = new vscode.Position(line, 0); + const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/); + if (wordRange) { + return wordRange; + } else { + return new vscode.Range(startPosition, startPosition); + } + } + + private getExecutableFilePath(langID: HdlLangID): string | Path | undefined { + // vivado install path stored in prj.vivado.install.path + const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado'); + const vivadoInstallPath = vivadoConfig.get('install.path', ''); + const executorName = this.executableFileMap.get(langID); + if (executorName === undefined) { + return undefined; + } + + // e.g. xvlog.bat in windows, xvlog in linux + const fullExecutorName = opeParam.os === 'win32' ? executorName + '.bat' : executorName; + + if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) { + LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn); + LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn); + return executorName; + } else { + LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`); + + const executorPath = hdlPath.join( + hdlPath.toSlash(vivadoInstallPath), + fullExecutorName + ); + // prevent path like C://stupid name/xxx/xxx/bin + // blank space + const safeExecutorPath = '"' + executorPath + '"'; + return safeExecutorPath; + } + } + + + public async setExecutableFilePath(executorPath: string | Path | undefined, langID: HdlLangID): Promise { + if (executorPath === undefined) { + return false; + } + const { stdout, stderr } = await easyExec(executorPath, []); + if (stderr.length === 0) { + this.executableInvokeNameMap.set(langID, undefined); + LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error); + return false; + } else { + this.executableInvokeNameMap.set(langID, executorPath); + LspOutput.report(`success to verify ${executorPath}, linter from vivado is ready to go!`, ReportType.Launch); + return true; + } + } + + public initialise(langID: HdlLangID) { + const executorPath = this.getExecutableFilePath(langID); + this.setExecutableFilePath(executorPath, langID); + } +} + + +export { + ModelsimLinter +}; diff --git a/src/function/lsp/linter/vivado.ts b/src/function/lsp/linter/vivado.ts index 8d5a69c..21c6ac6 100644 --- a/src/function/lsp/linter/vivado.ts +++ b/src/function/lsp/linter/vivado.ts @@ -1,6 +1,5 @@ import * as vscode from "vscode"; import * as fs from 'fs'; -import * as childProcess from 'child_process'; import { LspOutput, ReportType, opeParam } from "../../../global"; import { Path } from "../../../../resources/hdlParser"; @@ -29,17 +28,20 @@ class VivadoLinter implements BaseLinter { this.linterArgsMap.set(HdlLangID.Vhdl, ['--nolog']); this.linterArgsMap.set(HdlLangID.SystemVerilog, ['--sv', '--nolog']); this.linterArgsMap.set(HdlLangID.Unknown, []); - - const executablePath = this.selectExecutableFilePath(); - this.setExecutablePath(executablePath); + + this.initialise(HdlLangID.Verilog); + this.initialise(HdlLangID.Vhdl); + this.initialise(HdlLangID.SystemVerilog); } - + + async lint(document: vscode.TextDocument) { - const filePath = document.fileName; + const filePath = hdlPath.toSlash(document.fileName); + const langID = hdlFile.getLanguageId(filePath); // acquire install path - const args = [hdlPath.toSlash(filePath), ...this.linterArgs]; - const executor = this.xvlogExecutablePath; + const args = [hdlPath.toSlash(filePath), ...this.linterArgsMap]; + const executor = this.executableInvokeNameMap.get(langID); if (executor !== undefined) { const { stdout, stderr } = await easyExec(executor, args); if (stdout.length > 0) { @@ -85,43 +87,41 @@ class VivadoLinter implements BaseLinter { } } - private selectExecutableFilePath(langID: HdlLangID): string | Path { + private getExecutableFilePath(langID: HdlLangID): string | Path | undefined { // vivado install path stored in prj.vivado.install.path const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado'); const vivadoInstallPath = vivadoConfig.get('install.path', ''); + const executorName = this.executableFileMap.get(langID); + if (executorName === undefined) { + return undefined; + } + + // e.g. xvlog.bat in windows, xvlog in linux + const fullExecutorName = opeParam.os === 'win32' ? executorName + '.bat' : executorName; + if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) { - LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use xvlog in default.`, ReportType.Warn); + LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn); LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn); - return 'xvlog'; + return executorName; } else { LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`); - const xvlogPath = hdlPath.join( + const executorPath = hdlPath.join( hdlPath.toSlash(vivadoInstallPath), - // this.selectExecutableBasicName(langID) + fullExecutorName ); // prevent path like C://stupid name/xxx/xxx/bin // blank space - const safeXvlogPath = '"' + xvlogPath + '"'; - return safeXvlogPath; + const safeExecutorPath = '"' + executorPath + '"'; + return safeExecutorPath; } } - private selectExecutableBasicName(langID: HdlLangID): string | undefined { - const basicName = this.executableFileMap.get(langID); - if (!basicName) { - return basicName; - } - if (opeParam.os === 'win32') { - // e.g. xvlog.bat - return basicName + '.bat'; - } else { - // e.g. xvlog - return basicName; - } - } - public async setExecutablePath(executorPath: string | Path, langID: HdlLangID): Promise { + public async setExecutableFilePath(executorPath: string | Path | undefined, langID: HdlLangID): Promise { + if (executorPath === undefined) { + return false; + } const { stdout, stderr } = await easyExec(executorPath, []); if (stderr.length === 0) { this.executableInvokeNameMap.set(langID, undefined); @@ -133,116 +133,14 @@ class VivadoLinter implements BaseLinter { return true; } } -} -class XvhdlLinter implements BaseLinter { - diagnostic: vscode.DiagnosticCollection; - xvhdlExecutablePath: string | undefined; - linterArgs: string[] = ['--nolog']; - - constructor() { - this.diagnostic = vscode.languages.createDiagnosticCollection(); - const executablePath = this.selectExecutableFilePath(); - this.setExecutablePath(executablePath); - } - - async lint(document: vscode.TextDocument): Promise { - const filePath = document.fileName; - - // acquire install path - const args = [hdlPath.toSlash(filePath), ...this.linterArgs]; - const executor = this.xvhdlExecutablePath; - if (executor !== undefined) { - const { stdout, stderr } = await easyExec(executor, args); - if (stdout.length > 0) { - const diagnostics = this.provideDiagnostics(document, stdout); - this.diagnostic.set(document.uri, diagnostics); - } - } else { - LspOutput.report('linter is not available, please check prj.vivado.install.path in your setting', ReportType.Error); - } - } - - - /** - * @param document - * @param stdout stdout from xvlog - * @returns { vscode.Diagnostic[] } linter info - */ - private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] { - const diagnostics = []; - for (const line of stdout.split('\n')) { - const tokens = line.split(/:?\s*(?:\[|\])\s*/); - const headerInfo = tokens[0]; - // const standardInfo = tokens[1]; - const syntaxInfo = tokens[2]; - const parsedPath = tokens[3]; - if (headerInfo === 'ERROR') { - const errorInfos = parsedPath.split(':'); - const errorLine = parseInt(errorInfos[errorInfos.length - 1]); - const range = this.makeCorrectRange(document, errorLine); - const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error); - diagnostics.push(diag); - } - } - return diagnostics; - } - - private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range { - const startPosition = new vscode.Position(line, 0); - const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/); - if (wordRange) { - return wordRange; - } else { - return new vscode.Range(startPosition, startPosition); - } - } - - private selectExecutableFilePath(): string | Path { - // vivado install path stored in prj.vivado.install.path - const vivadoConfig = vscode.workspace.getConfiguration('prj.vivado'); - const vivadoInstallPath = vivadoConfig.get('install.path', ''); - if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) { - LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid. Use xvhdl in default.`, ReportType.Warn); - LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn); - return 'xvhdl'; - } else { - LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`); - - const xvhdlPath = hdlPath.join( - hdlPath.toSlash(vivadoInstallPath), - this.selectXvhdlFilename() - ); - // prevent path like C://stupid name/xxx/xxx/bin - // blank space - const safeXvhdlPath = '"' + xvhdlPath + '"'; - return safeXvhdlPath; - } - } - - private selectXvhdlFilename(): string { - if (opeParam.os === 'win32') { - return 'xvhdl.bat'; - } else { - return 'xvhdl'; - } - } - - public async setExecutablePath(path: string | Path): Promise { - const { stdout, stderr } = await easyExec(path, []); - if (stderr.length === 0) { - this.xvhdlExecutablePath = undefined; - LspOutput.report(`fail to execute ${path}! Reason: ${stderr}`, ReportType.Error); - return false; - } else { - this.xvhdlExecutablePath = path; - LspOutput.report(`success to verify ${path}, linter from vivado is ready to go!`, ReportType.Launch); - return true; - } + public initialise(langID: HdlLangID) { + const executorPath = this.getExecutableFilePath(langID); + this.setExecutableFilePath(executorPath, langID); } } + export { - VivadoLinter, - XvhdlLinter -}; \ No newline at end of file + VivadoLinter +};