update
This commit is contained in:
parent
8a480c52af
commit
bc08d9a9c4
@ -14,8 +14,8 @@ Bug 修复
|
|||||||
Feat
|
Feat
|
||||||
- 增加对 XDC,TCL 等脚本的 LSP 支持
|
- 增加对 XDC,TCL 等脚本的 LSP 支持
|
||||||
- 增加 verilog, vhdl, xdc, tcl 等语言的图标
|
- 增加 verilog, vhdl, xdc, tcl 等语言的图标
|
||||||
- 增加对于 vivado 的支持,用户可以通过添加 vivado 路径的方式来使用 vivado 的仿真和自动纠错
|
- 增加对于 vivado 的支持,用户可以通过添加 vivado 路径的方式(或者将 bin 文件夹添加到环境变量,默认路径为 C:\Xilinx\Vivado\2018.3\bin)来使用 vivado 的仿真和自动纠错
|
||||||
|
- 增加对于 modelsim 的支持,用户可以通过添加 modelsim 安装路径(或者将 bin 文件夹添加到环境变量,默认路径为 C:\modeltech64_10.4\win64)来使用 vivado 的仿真和自动纠错
|
||||||
|
|
||||||
## [0.1.23] - 2022-12-24
|
## [0.1.23] - 2022-12-24
|
||||||
- Finish the css of documentation, see `./css/documentation.css` for detail.
|
- Finish the css of documentation, see `./css/documentation.css` for detail.
|
||||||
|
80
package.json
80
package.json
@ -60,7 +60,7 @@
|
|||||||
"scope": "window",
|
"scope": "window",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "set the xilinx install path. \n e.g. : D:/APP/vivado_18_3/Vivado/2018.3/bin \n This applies only to WIN For other systems, add it to environment variables"
|
"description": "set the xilinx install path. \n e.g. : D:/APP/vivado_18_3/Vivado/2018.3/bin \n Default path is C:/Xilinx/Vivado/2018.3/bin \n This applies only to WIN For other systems, add it to environment variables"
|
||||||
},
|
},
|
||||||
"prj.xilinx.IP.repo.path": {
|
"prj.xilinx.IP.repo.path": {
|
||||||
"scope": "window",
|
"scope": "window",
|
||||||
@ -221,6 +221,51 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"function.lsp.linter.vlog.diagnostor": {
|
||||||
|
"type": "string",
|
||||||
|
"enumDescriptions": [
|
||||||
|
"use diagnostor in vivado",
|
||||||
|
"use diagnostor in modelsim",
|
||||||
|
"use our buildin diagnostor"
|
||||||
|
],
|
||||||
|
"enum": [
|
||||||
|
"vivado",
|
||||||
|
"modelsim",
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"default": "default",
|
||||||
|
"description": "choose diagnostor to do linter in editing verilog"
|
||||||
|
},
|
||||||
|
"function.lsp.linter.vhdl.diagnostor": {
|
||||||
|
"type": "string",
|
||||||
|
"enumDescriptions": [
|
||||||
|
"use diagnostor in vivado",
|
||||||
|
"use diagnostor in modelsim",
|
||||||
|
"use our buildin diagnostor"
|
||||||
|
],
|
||||||
|
"enum": [
|
||||||
|
"vivado",
|
||||||
|
"modelsim",
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"default": "default",
|
||||||
|
"description": "choose diagnostor to do linter in editing vhdl"
|
||||||
|
},
|
||||||
|
"function.lsp.linter.systemverilog.diagnostor": {
|
||||||
|
"type": "string",
|
||||||
|
"enumDescriptions": [
|
||||||
|
"use diagnostor in vivado",
|
||||||
|
"use diagnostor in modelsim",
|
||||||
|
"use our buildin diagnostor"
|
||||||
|
],
|
||||||
|
"enum": [
|
||||||
|
"vivado",
|
||||||
|
"modelsim",
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"default": "default",
|
||||||
|
"description": "choose diagnostor to do linter in editing systemverilog"
|
||||||
|
},
|
||||||
"function.instantiation.addComment": {
|
"function.instantiation.addComment": {
|
||||||
"description": "add comment like // ports, // input, // output when doing instantiation, including completion for module invoking",
|
"description": "add comment like // ports, // input, // output when doing instantiation, including completion for module invoking",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -452,39 +497,6 @@
|
|||||||
"title": "%digital-ide.lsp.tool.transformOldPropertyFile.title%",
|
"title": "%digital-ide.lsp.tool.transformOldPropertyFile.title%",
|
||||||
"category": "Digital-IDE"
|
"category": "Digital-IDE"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"command": "digital-ide.lsp.verilog.linter",
|
|
||||||
"title": "%digital-ide.lsp.verilog.linter.title%",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"vivado",
|
|
||||||
"modelsim",
|
|
||||||
"default"
|
|
||||||
],
|
|
||||||
"category": "Digital-IDE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "digital-ide.lsp.vhdl.linter",
|
|
||||||
"title": "%digital-ide.lsp.vhdl.linter.title%",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"vivado",
|
|
||||||
"modelsim",
|
|
||||||
"default"
|
|
||||||
],
|
|
||||||
"category": "Digital-IDE"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "digital-ide.lsp.systemverilog.linter",
|
|
||||||
"title": "%digital-ide.lsp.systemverilog.linter.title%",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"vivado",
|
|
||||||
"modelsim",
|
|
||||||
"default"
|
|
||||||
],
|
|
||||||
"category": "Digital-IDE"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"command": "digital-ide.vhdl2vlog",
|
"command": "digital-ide.vhdl2vlog",
|
||||||
"title": "%digital-ide.vhdl2vlog.title%",
|
"title": "%digital-ide.vhdl2vlog.title%",
|
||||||
|
@ -40,8 +40,5 @@
|
|||||||
"digital-ide.lsp.tool.transformOldPropertyFile.title": "Transform configure file from previous version to new version",
|
"digital-ide.lsp.tool.transformOldPropertyFile.title": "Transform configure file from previous version to new version",
|
||||||
"digital-ide.vhdl2vlog.title": "Translate vhdl code to verilog code",
|
"digital-ide.vhdl2vlog.title": "Translate vhdl code to verilog code",
|
||||||
"digital-ide.fsm.show.title": "Show FSM graph of current file",
|
"digital-ide.fsm.show.title": "Show FSM graph of current file",
|
||||||
"digital-ide.netlist.show.title": "Show netlist of current file",
|
"digital-ide.netlist.show.title": "Show netlist of current file"
|
||||||
"digital-ide.lsp.verilog.linter.title": "type of diagnotor for verilog",
|
|
||||||
"digital-ide.lsp.vhdl.linter.title": "type of diagnotor for vhdl",
|
|
||||||
"digital-ide.lsp.systemverilog.linter.title": "type of diagnotor for systemverilog"
|
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
15
src/function/lsp/linter/base.ts
Normal file
15
src/function/lsp/linter/base.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
interface BaseLinter {
|
||||||
|
diagnostic: vscode.DiagnosticCollection;
|
||||||
|
lint(document: vscode.TextDocument): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BaseManager {
|
||||||
|
initialise(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
BaseLinter,
|
||||||
|
BaseManager
|
||||||
|
};
|
165
src/function/lsp/linter/default.ts
Normal file
165
src/function/lsp/linter/default.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { All } from '../../../../resources/hdlParser';
|
||||||
|
import { isVerilogFile, isVhdlFile } from '../../../hdlFs/file';
|
||||||
|
import { Position, Range } from '../../../hdlParser/common';
|
||||||
|
import { hdlSymbolStorage } from '../core';
|
||||||
|
import { BaseLinter } from './base';
|
||||||
|
import { LspOutput, ReportType } from '../../../global';
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultVlogLinter implements BaseLinter {
|
||||||
|
diagnostic: vscode.DiagnosticCollection;
|
||||||
|
constructor() {
|
||||||
|
this.diagnostic = vscode.languages.createDiagnosticCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
async lint(document: vscode.TextDocument): Promise<void> {
|
||||||
|
const filePath = document.fileName;
|
||||||
|
const vlogAll = await hdlSymbolStorage.getSymbol(filePath);
|
||||||
|
// console.log('lint all finish');
|
||||||
|
|
||||||
|
if (vlogAll) {
|
||||||
|
const diagnostics = this.provideDiagnostics(document, vlogAll);
|
||||||
|
this.diagnostic.set(document.uri, diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
|
||||||
|
const diagnostics: vscode.Diagnostic[] = [];
|
||||||
|
if (all.error && all.error.length > 0) {
|
||||||
|
for (const hdlError of all.error) {
|
||||||
|
const range = this.makeCorrectRange(document, hdlError.range);
|
||||||
|
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
|
||||||
|
diag.source = hdlError.source;
|
||||||
|
diagnostics.push(diag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
|
||||||
|
range.line --;
|
||||||
|
if (range.character === 0 && range.line > 0) {
|
||||||
|
range.line --;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (range.line > 0) {
|
||||||
|
const lineContent = document.lineAt(range.line).text;
|
||||||
|
if (lineContent.trim().length > 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
range.line --;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLine = document.lineAt(range.line).text;
|
||||||
|
if (range.character === 0 && currentLine.trim().length > 0) {
|
||||||
|
range.character = currentLine.trimEnd().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = new vscode.Position(range.line, range.character);
|
||||||
|
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
|
||||||
|
if (wordRange) {
|
||||||
|
return wordRange;
|
||||||
|
} else {
|
||||||
|
const errorEnd = new vscode.Position(range.line, range.character + 1);
|
||||||
|
const errorRange = new vscode.Range(position, errorEnd);
|
||||||
|
return errorRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(uri: vscode.Uri) {
|
||||||
|
this.diagnostic.delete(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initialise() {
|
||||||
|
for (const doc of vscode.workspace.textDocuments) {
|
||||||
|
if (isVerilogFile(doc.fileName)) {
|
||||||
|
// TODO : check performance
|
||||||
|
await this.lint(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultVHDLLinter implements BaseLinter {
|
||||||
|
diagnostic: vscode.DiagnosticCollection;
|
||||||
|
constructor() {
|
||||||
|
this.diagnostic = vscode.languages.createDiagnosticCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
async lint(document: vscode.TextDocument): Promise<void> {
|
||||||
|
const filePath = document.fileName;
|
||||||
|
const vhdlAll = await hdlSymbolStorage.getSymbol(filePath);
|
||||||
|
// console.log('lint all finish');
|
||||||
|
|
||||||
|
if (vhdlAll) {
|
||||||
|
const diagnostics = this.provideDiagnostics(document, vhdlAll);
|
||||||
|
this.diagnostic.set(document.uri, diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
|
||||||
|
const diagnostics: vscode.Diagnostic[] = [];
|
||||||
|
if (all.error && all.error.length > 0) {
|
||||||
|
for (const hdlError of all.error) {
|
||||||
|
const range = this.makeCorrectRange(document, hdlError.range);
|
||||||
|
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
|
||||||
|
diag.source = hdlError.source;
|
||||||
|
diagnostics.push(diag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
|
||||||
|
range.line --;
|
||||||
|
if (range.character === 0 && range.line > 0) {
|
||||||
|
range.line --;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (range.line > 0) {
|
||||||
|
const lineContent = document.lineAt(range.line).text;
|
||||||
|
if (lineContent.trim().length > 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
range.line --;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLine = document.lineAt(range.line).text;
|
||||||
|
if (range.character === 0 && currentLine.trim().length > 0) {
|
||||||
|
range.character = currentLine.trimEnd().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = new vscode.Position(range.line, range.character);
|
||||||
|
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
|
||||||
|
if (wordRange) {
|
||||||
|
return wordRange;
|
||||||
|
} else {
|
||||||
|
const errorEnd = new vscode.Position(range.line, range.character + 1);
|
||||||
|
const errorRange = new vscode.Range(position, errorEnd);
|
||||||
|
return errorRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(uri: vscode.Uri) {
|
||||||
|
this.diagnostic.delete(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initialise() {
|
||||||
|
for (const doc of vscode.workspace.textDocuments) {
|
||||||
|
if (isVhdlFile(doc.fileName)) {
|
||||||
|
// TODO : check performance
|
||||||
|
await this.lint(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
DefaultVlogLinter,
|
||||||
|
DefaultVHDLLinter
|
||||||
|
};
|
@ -1,5 +1,8 @@
|
|||||||
import { vlogLinter } from './vlog';
|
import { vlogLinterManager } from './vlog';
|
||||||
|
import { vhdlLinterManager } from './vhdl';
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
vlogLinter
|
vlogLinterManager,
|
||||||
|
vhdlLinterManager
|
||||||
};
|
};
|
18
src/function/lsp/linter/vhdl.ts
Normal file
18
src/function/lsp/linter/vhdl.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { BaseManager } from './base';
|
||||||
|
|
||||||
|
class VhdlLinterManager implements BaseManager {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialise(): Promise<void> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const vhdlLinterManager = new VhdlLinterManager();
|
||||||
|
|
||||||
|
export {
|
||||||
|
vhdlLinterManager
|
||||||
|
};
|
@ -1,18 +1,248 @@
|
|||||||
import * as vscode from "vscode";
|
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";
|
||||||
|
import { hdlFile, hdlPath } from "../../../hdlFs";
|
||||||
|
import { easyExec } from "../../../global/util";
|
||||||
|
import { BaseLinter } from "./base";
|
||||||
|
import { HdlLangID } from "../../../global/enum";
|
||||||
|
|
||||||
|
class VivadoLinter implements BaseLinter {
|
||||||
class VivadoLinter {
|
|
||||||
diagnostic: vscode.DiagnosticCollection;
|
diagnostic: vscode.DiagnosticCollection;
|
||||||
|
executableFileMap: Map<HdlLangID, string | undefined> = new Map<HdlLangID, string>();
|
||||||
|
executableInvokeNameMap: Map<HdlLangID, string | undefined> = new Map<HdlLangID, string>();
|
||||||
|
linterArgsMap: Map<HdlLangID, string[]> = new Map<HdlLangID, string[]>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.diagnostic = vscode.languages.createDiagnosticCollection();
|
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, []);
|
||||||
|
|
||||||
|
const executablePath = this.selectExecutableFilePath();
|
||||||
|
this.setExecutablePath(executablePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async lint(document: vscode.TextDocument) {
|
async lint(document: vscode.TextDocument) {
|
||||||
const filePath = document.fileName;
|
const filePath = document.fileName;
|
||||||
|
|
||||||
// acquire install path
|
// acquire install path
|
||||||
const name = "prj.vivado.install.path";
|
const args = [hdlPath.toSlash(filePath), ...this.linterArgs];
|
||||||
|
const executor = this.xvlogExecutablePath;
|
||||||
|
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(langID: HdlLangID): 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 xvlog in default.`, ReportType.Warn);
|
||||||
|
LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn);
|
||||||
|
return 'xvlog';
|
||||||
|
} else {
|
||||||
|
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`);
|
||||||
|
|
||||||
|
const xvlogPath = hdlPath.join(
|
||||||
|
hdlPath.toSlash(vivadoInstallPath),
|
||||||
|
// this.selectExecutableBasicName(langID)
|
||||||
|
);
|
||||||
|
// prevent path like C://stupid name/xxx/xxx/bin
|
||||||
|
// blank space
|
||||||
|
const safeXvlogPath = '"' + xvlogPath + '"';
|
||||||
|
return safeXvlogPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<boolean> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<void> {
|
||||||
|
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<boolean> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
VivadoLinter,
|
||||||
|
XvhdlLinter
|
||||||
|
};
|
@ -1,87 +1,19 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { All } from '../../../../resources/hdlParser';
|
import { BaseManager } from './base';
|
||||||
import { isVerilogFile } from '../../../hdlFs/file';
|
|
||||||
import { Position, Range } from '../../../hdlParser/common';
|
|
||||||
import { hdlSymbolStorage } from '../core';
|
|
||||||
|
|
||||||
|
class VlogLinterManager implements BaseManager {
|
||||||
class VlogLinter {
|
|
||||||
diagnostic: vscode.DiagnosticCollection;
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.diagnostic = vscode.languages.createDiagnosticCollection();
|
const vlogLspConfig = vscode.workspace.getConfiguration('digital-ide.lsp.verilog.linter');
|
||||||
|
const
|
||||||
}
|
}
|
||||||
|
|
||||||
async lint(document: vscode.TextDocument) {
|
async initialise(): Promise<void> {
|
||||||
const filePath = document.fileName;
|
|
||||||
const vlogAll = await hdlSymbolStorage.getSymbol(filePath);
|
|
||||||
// console.log('lint all finish');
|
|
||||||
|
|
||||||
if (vlogAll) {
|
|
||||||
const diagnostics = this.provideDiagnostics(document, vlogAll);
|
|
||||||
this.diagnostic.set(document.uri, diagnostics);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private provideDiagnostics(document: vscode.TextDocument, all: All): vscode.Diagnostic[] {
|
const vlogLinterManager = new VlogLinterManager();
|
||||||
const diagnostics: vscode.Diagnostic[] = [];
|
|
||||||
if (all.error && all.error.length > 0) {
|
|
||||||
for (const hdlError of all.error) {
|
|
||||||
const range = this.makeCorrectRange(document, hdlError.range);
|
|
||||||
const diag = new vscode.Diagnostic(range, hdlError.message, hdlError.severity);
|
|
||||||
diag.source = hdlError.source;
|
|
||||||
diagnostics.push(diag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diagnostics;
|
|
||||||
}
|
|
||||||
|
|
||||||
private makeCorrectRange(document: vscode.TextDocument, range: Position): vscode.Range {
|
|
||||||
range.line --;
|
|
||||||
if (range.character === 0 && range.line > 0) {
|
|
||||||
range.line --;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (range.line > 0) {
|
|
||||||
const lineContent = document.lineAt(range.line).text;
|
|
||||||
if (lineContent.trim().length > 0) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
range.line --;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentLine = document.lineAt(range.line).text;
|
|
||||||
if (range.character === 0 && currentLine.trim().length > 0) {
|
|
||||||
range.character = currentLine.trimEnd().length;
|
|
||||||
}
|
|
||||||
|
|
||||||
const position = new vscode.Position(range.line, range.character);
|
|
||||||
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
|
|
||||||
if (wordRange) {
|
|
||||||
return wordRange;
|
|
||||||
} else {
|
|
||||||
const errorEnd = new vscode.Position(range.line, range.character + 1);
|
|
||||||
const errorRange = new vscode.Range(position, errorEnd);
|
|
||||||
return errorRange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(uri: vscode.Uri) {
|
|
||||||
this.diagnostic.delete(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initialise() {
|
|
||||||
for (const doc of vscode.workspace.textDocuments) {
|
|
||||||
if (isVerilogFile(doc.fileName)) {
|
|
||||||
// TODO : check performance
|
|
||||||
await this.lint(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const vlogLinter = new VlogLinter();
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
vlogLinter
|
vlogLinterManager
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { opeParam, OpeParamDefaults } from './opeParam';
|
import { opeParam, OpeParamDefaults } from './opeParam';
|
||||||
import { PrjInfo, PrjInfoDefaults } from './prjInfo';
|
import { PrjInfo, PrjInfoDefaults } from './prjInfo';
|
||||||
import { MainOutput, YosysOutput, ReportType } from './outputChannel';
|
import { MainOutput, LspOutput, YosysOutput, ReportType } from './outputChannel';
|
||||||
|
|
||||||
import * as Enum from './enum';
|
import * as Enum from './enum';
|
||||||
import * as Lang from './lang';
|
import * as Lang from './lang';
|
||||||
@ -20,6 +20,7 @@ export {
|
|||||||
AbsPath,
|
AbsPath,
|
||||||
RelPath,
|
RelPath,
|
||||||
MainOutput,
|
MainOutput,
|
||||||
|
LspOutput,
|
||||||
YosysOutput,
|
YosysOutput,
|
||||||
ReportType,
|
ReportType,
|
||||||
AllowNull
|
AllowNull
|
||||||
|
@ -74,10 +74,12 @@ class Output {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MainOutput = new Output('Digital-IDE');
|
const MainOutput = new Output('Digital-IDE');
|
||||||
|
const LspOutput = new Output('Digital-IDE Language Server');
|
||||||
const YosysOutput = new Output('Yosys');
|
const YosysOutput = new Output('Yosys');
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ReportType,
|
ReportType,
|
||||||
MainOutput,
|
MainOutput,
|
||||||
|
LspOutput,
|
||||||
YosysOutput
|
YosysOutput
|
||||||
};
|
};
|
@ -1,4 +1,5 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as childProcess from 'child_process';
|
||||||
|
|
||||||
import { AbsPath } from ".";
|
import { AbsPath } from ".";
|
||||||
|
|
||||||
@ -34,8 +35,34 @@ function isSameSet<T>(setA: Set<T>, setB: Set<T>): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ExecutorOutput {
|
||||||
|
stdout: string
|
||||||
|
stderr: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* more elegant function to execute command
|
||||||
|
* @param executor executor
|
||||||
|
* @param args argruments
|
||||||
|
* @returns { Promise<ExecutorOutput> }
|
||||||
|
*/
|
||||||
|
async function easyExec(executor: string, args: string[]): Promise<ExecutorOutput> {
|
||||||
|
const allArguments = [executor, ...args];
|
||||||
|
const command = allArguments.join(' ');
|
||||||
|
|
||||||
|
const p = new Promise<ExecutorOutput>( ( resolve, _ ) => {
|
||||||
|
childProcess.exec(command, ( _, stdout, stderr ) => {
|
||||||
|
resolve({ stdout, stderr });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
PathSet,
|
PathSet,
|
||||||
isSameSet
|
isSameSet,
|
||||||
|
easyExec
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user