add icon of vvp | pass vivado lsp | pass modelsim lsp

This commit is contained in:
锦恢 2023-11-27 20:05:24 +08:00
parent 010a4b6bec
commit 6773a3df94
21 changed files with 899 additions and 130 deletions

View File

@ -7,15 +7,20 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [0.3.2] - 2023-11-01 ## [0.3.2] - 2023-11-01
Bug 修复 Bug 修复
- 修复文档化input, output处注释无法正常显示到文档的 bug - 修复文档化 input, output 处注释无法正常显示到文档的 bug
- 修复 iverilog 仿真功能中,将重复的路径作为编译参数编译的 bug - 修复 iverilog 仿真功能中,将重复的路径作为编译参数编译的 bug
- 修复 iverilog 仿真功能中,将 `include 加入或去除后,无法通过仿真编译的 bug (没有更新 instance 的 instModPathStatus 属性) - 修复 iverilog 仿真功能中,将 <code>`include</code> 加入或去除后,无法通过仿真编译的 bug (没有更新 instance 的 instModPathStatus 属性)
- 修复其他已知 bug
Feat Change
- 将插件的工作状态显示在 vscode 下侧的状态栏上,利于用户了解目前的设置状态
- 优化项目配置目录
Feature
- 增加对 XDCTCL 等脚本的 LSP 支持 - 增加对 XDCTCL 等脚本的 LSP 支持
- 增加 verilog, vhdl, xdc, tcl 等语言的图标 - 增加 verilog, vhdl, xdc, tcl, vvp 等语言的工作区图标
- 增加对于 vivado 的支持,用户可以通过添加 vivado 路径的方式(或者将 bin 文件夹添加到环境变量,默认路径为 C:\Xilinx\Vivado\2018.3\bin来使用 vivado 的仿真和自动纠错 - 增加对于 vivado, modelsim, verilator 的支持,用户可以通过设置 `function.lsp.linter.vhdl.diagnostor`(设置 vhdl) 和 `function.lsp.linter.vlog.diagnostor`(设置 verilog) 来使用这些第三方工具的仿真和自动纠错。
- 增加对于 modelsim 的支持,用户可以通过添加 modelsim 安装路径(或者将 bin 文件夹添加到环境变量,默认路径为 C:\modeltech64_10.4\win64来使用 vivado 的仿真和自动纠错 - 增加对于 TCL, XDC, VVP 等脚本的 LSP 和 语法高亮 支持
## [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.

View File

@ -0,0 +1,17 @@
{
"comments": {
"lineComment": "#"
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
{ "open": "(", "close": ")" },
{ "open": "[", "close": "]" },
{ "open": "'", "close": "'", "notIn": ["string"] },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "/*", "close": " */", "notIn": ["string"] }
]
}

View File

@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.86 4.057H5.14c-.612 0-1.113.5-1.113 1.112v5.721c0 .612.5 1.112 1.113 1.112h5.72c.612 0 1.113-.5 1.113-1.112V5.17c0-.612-.5-1.113-1.113-1.113Zm-.934 5.931H6.17V6.231h3.756v3.757ZM6.274 14h-.7v-1.682h.7V14Zm1.403 0h-.7v-1.682h.7V14Zm1.404 0h-.7v-1.682h.7V14Zm1.403 0h-.7v-1.682h.7V14ZM3.712 6.273H2v-.7h1.712v.7Zm0 1.403H2v-.7h1.712v.7Zm0 1.404H2v-.7h1.712v.7Zm0 1.403H2v-.7h1.712v.7ZM14 6.273h-1.712v-.7H14v.7Zm0 1.403h-1.712v-.7H14v.7Zm0 1.404h-1.712v-.7H14v.7Zm0 1.403h-1.712v-.7H14v.7Zm-7.726-6.8h-.7V2h.7v1.682Zm1.403 0h-.7V2h.7v1.682Zm1.404 0h-.7V2h.7v1.682Zm1.403 0h-.7V2h.7v1.682Zm-4.932 7.296a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" fill="#FFCB6B"/></svg>

After

Width:  |  Height:  |  Size: 754 B

1
images/svg/dark/vvp.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701000026046" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10173" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M810.666667 128a85.333333 85.333333 0 0 1 85.333333 85.333333v597.333334a85.333333 85.333333 0 0 1-85.333333 85.333333H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333333V213.333333a85.333333 85.333333 0 0 1 85.333333-85.333333h597.333334zM384 330.965333L202.965333 512 384 693.034667l60.330667-60.373334L323.669333 512l120.661334-120.661333L384 330.965333z m256 0l-60.330667 60.373334L700.330667 512l-120.661334 120.661333L640 693.034667 821.034667 512 640 330.965333z" fill="#98C379" p-id="10174"></path></svg>

After

Width:  |  Height:  |  Size: 851 B

1
images/svg/light/vvp.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701000026046" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10173" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M810.666667 128a85.333333 85.333333 0 0 1 85.333333 85.333333v597.333334a85.333333 85.333333 0 0 1-85.333333 85.333333H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333333V213.333333a85.333333 85.333333 0 0 1 85.333333-85.333333h597.333334zM384 330.965333L202.965333 512 384 693.034667l60.330667-60.373334L323.669333 512l120.661334-120.661333L384 330.965333z m256 0l-60.330667 60.373334L700.330667 512l-120.661334 120.661333L640 693.034667 821.034667 512 640 330.965333z" fill="#98C379" p-id="10174"></path></svg>

After

Width:  |  Height:  |  Size: 851 B

View File

@ -33,11 +33,6 @@
"Snippets" "Snippets"
], ],
"activationEvents": [ "activationEvents": [
"onLanguage:verilog",
"onLanguage:vhdl",
"onLanguage:systemverilog",
"onCommand:digital-ide.property-json.generate",
"onCommand:digital-ide.property-json.overwrite",
"workspaceContains:.vscode/property.json" "workspaceContains:.vscode/property.json"
], ],
"contributes": { "contributes": {
@ -831,6 +826,21 @@
], ],
"configuration": "./config/link.configuration.json" "configuration": "./config/link.configuration.json"
}, },
{
"id": "vvp",
"aliases": [
"VivadoVerificationPlan"
],
"extensions": [
".vvp",
".VVP"
],
"configuration": "./config/vvp.configuration.json",
"icon": {
"dark": "./images/svg/dark/vvp.svg",
"light": "./images/svg/light/vvp.svg"
}
},
{ {
"id": "digital-ide-output", "id": "digital-ide-output",
"mimetypes": [ "mimetypes": [
@ -863,7 +873,7 @@
{ {
"language": "vhdl", "language": "vhdl",
"scopeName": "source.vhdl", "scopeName": "source.vhdl",
"path": "./syntaxes/vhdl.json" "path": "./syntaxes/vhdl.tmLanguage.json"
}, },
{ {
"language": "verilog", "language": "verilog",
@ -875,6 +885,11 @@
"scopeName": "source.systemverilog", "scopeName": "source.systemverilog",
"path": "./syntaxes/systemverilog.json" "path": "./syntaxes/systemverilog.json"
}, },
{
"language": "vvp",
"scopeName": "source.vvp",
"path": "./syntaxes/vvp.tmLanguage.json"
},
{ {
"language": "digital-ide-output", "language": "digital-ide-output",
"scopeName": "digital-ide.output", "scopeName": "digital-ide.output",

View File

@ -78,16 +78,23 @@ function registerLsp(context: vscode.ExtensionContext) {
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`'); vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`');
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.'); vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider); vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider);
vscode.languages.registerDocumentSemanticTokensProvider(vlogSelector, lspDocSemantic.vlogDocSenmanticProvider, lspDocSemantic.vlogLegend); // vscode.languages.registerDocumentSemanticTokensProvider(vlogSelector, lspDocSemantic.vlogDocSenmanticProvider, lspDocSemantic.vlogLegend);
// vhdl lsp
vscode.languages.registerCompletionItemProvider(vhdlSelector, lspCompletion.vhdlCompletionProvider);
// tcl lsp // tcl lsp
vscode.languages.registerCompletionItemProvider(tclSelector, lspCompletion.tclCompletionProvider); vscode.languages.registerCompletionItemProvider(tclSelector, lspCompletion.tclCompletionProvider);
lspLinter.vlogLinter.initialise(); // lsp linter
// make first symbols in workspace
lspCore.hdlSymbolStorage.initialise(); lspCore.hdlSymbolStorage.initialise();
lspLinter.vlogLinterManager.initialise();
// vhdl lsp lspLinter.vhdlLinterManager.initialise();
} }

View File

@ -1,4 +1,5 @@
import { vlogCompletionProvider, vlogIncludeCompletionProvider, vlogMacroCompletionProvider, vlogPositionPortProvider } from './vlog'; import { vlogCompletionProvider, vlogIncludeCompletionProvider, vlogMacroCompletionProvider, vlogPositionPortProvider } from './vlog';
import { vhdlCompletionProvider } from './vhdl';
import { tclCompletionProvider } from './tcl'; import { tclCompletionProvider } from './tcl';
export { export {
@ -6,5 +7,6 @@ export {
vlogIncludeCompletionProvider, vlogIncludeCompletionProvider,
vlogMacroCompletionProvider, vlogMacroCompletionProvider,
vlogPositionPortProvider, vlogPositionPortProvider,
vhdlCompletionProvider,
tclCompletionProvider tclCompletionProvider
}; };

View File

@ -0,0 +1,212 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as util from '../util';
import { hdlParam } from '../../../hdlParser';
import { AbsPath, MainOutput, RelPath, ReportType } from '../../../global';
import { Define, Include, RawSymbol } from '../../../hdlParser/common';
import { HdlInstance, HdlModule } from '../../../hdlParser/core';
import { vhdlKeyword } from '../util/keyword';
import { hdlPath } from '../../../hdlFs';
import { hdlSymbolStorage } from '../core';
class VhdlCompletionProvider implements vscode.CompletionItemProvider {
public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Promise<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem> | null | undefined> {
// console.log('VhdlCompletionProvider');
try {
const filePath = hdlPath.toSlash(document.fileName);
// 1. provide keyword
const completions = this.makeKeywordItems(document, position);
completions.push(...this.makeCompilerKeywordItems(document, position));
completions.push(...this.makeSystemKeywordItems(document, position));
const symbolResult = await hdlSymbolStorage.getSymbol(filePath);
if (!symbolResult) {
return completions;
}
console.log('vhdl symbol result');
// locate at one module
const scopeSymbols = util.filterSymbolScope(position, symbolResult.content);
if (!scopeSymbols ||
!scopeSymbols.module ||
!hdlParam.hasHdlModule(filePath, scopeSymbols.module.name)) {
// MainOutput.report('Fail to get HdlModule ' + filePath + ' ' + scopeSymbols?.module.name, ReportType.Debug);
return completions;
}
// find wrapper module
const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name);
if (!currentModule) {
return completions;
}
// 3. provide modules
const suggestModulesPromise = this.provideModules(document, position, filePath, symbolResult.macro.includes);
// 4. provide params and ports of wrapper module
const suggestParamsPortsPromise = this.provideParamsPorts(currentModule);
// 5. provide nets
const suggestNetsPromise = this.provideNets(scopeSymbols.symbols);
// collect
completions.push(...await suggestModulesPromise);
completions.push(...await suggestParamsPortsPromise);
completions.push(...await suggestNetsPromise);
return completions;
} catch (err) {
console.log(err);
}
}
private makeKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
const vhdlKeywordItem = [];
for (const keyword of vhdlKeyword.keys()) {
const clItem = this.makekeywordCompletionItem(keyword);
vhdlKeywordItem.push(clItem);
}
return vhdlKeywordItem;
}
private makeCompilerKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
const items = [];
const targetRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
const targetWord = document.getText(targetRange);
const prefix = targetWord.startsWith('`') ? '' : '`';
for (const keyword of vhdlKeyword.compilerKeys()) {
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
clItem.insertText = new vscode.SnippetString(prefix + keyword);
clItem.detail = 'compiler directive';
items.push(clItem);
}
return items;
}
private makeSystemKeywordItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
const items = [];
for (const keyword of vhdlKeyword.systemKeys()) {
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Method);
clItem.insertText = new vscode.SnippetString('\\$' + keyword + '($1);');
clItem.detail = 'system task';
items.push(clItem);
}
return items;
}
private makekeywordCompletionItem(keyword: string): vscode.CompletionItem {
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
clItem.detail = 'keyword';
switch (keyword) {
case 'begin': clItem.insertText = new vscode.SnippetString("begin$1\nend"); break;
case 'function': clItem.insertText = new vscode.SnippetString("function ${1:name}\n\nendfunction"); break;
default: break;
}
return clItem;
}
private async provideModules(document: vscode.TextDocument, position: vscode.Position, filePath: AbsPath, includes: Include[]): Promise<vscode.CompletionItem[]> {
const suggestModules: vscode.CompletionItem[] = [];
const lspVhdlConfig = vscode.workspace.getConfiguration('function.lsp.completion.vhdl');
const autoAddInclude: boolean = lspVhdlConfig.get('autoAddInclude', true);
const completeWholeInstante: boolean = lspVhdlConfig.get('completeWholeInstante', true);
const includePaths = new Set<AbsPath>();
let lastIncludeLine = 0;
for (const include of includes) {
const absIncludePath = hdlPath.rel2abs(filePath, include.path);
includePaths.add(absIncludePath);
lastIncludeLine = Math.max(include.range.end.line, lastIncludeLine);
}
const insertPosition = new vscode.Position(lastIncludeLine, 0);
const insertRange = new vscode.Range(insertPosition, insertPosition);
const fileFolder = hdlPath.resolve(filePath, '..');
// used only when completeWholeInstante is true
let completePrefix = '';
if (completeWholeInstante) {
const wordRange = document.getWordRangeAtPosition(position);
const countStart = wordRange ? wordRange.start.character : position.character;
const spaceNumber = Math.floor(countStart / 4) * 4;
console.log(wordRange, countStart, spaceNumber);
completePrefix = ' '.repeat(spaceNumber);
}
// for (const module of hdlParam.getAllHdlModules()) {
// const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class);
// // feature 1 : auto add include path if there's no corresponding include path
// if (autoAddInclude && !includePaths.has(module.path)) {
// const relPath: RelPath = hdlPath.relative(fileFolder, module.path);
// const includeString = '`include "' + relPath + '"\n';
// const textEdit = new vscode.TextEdit(insertRange, includeString);
// clItem.additionalTextEdits = [textEdit];
// }
// // feature 2 : auto complete instance
// if (completeWholeInstante) {
// const snippetString = instanceVhdlCode(module, '', true);
// clItem.insertText = new vscode.SnippetString(snippetString);
// }
// clItem.detail = 'module';
// suggestModules.push(clItem);
// }
return suggestModules;
}
private async provideParamsPorts(module: HdlModule): Promise<vscode.CompletionItem[]> {
if (!module) {
return [];
}
const suggestParamsPorts = [];
for (const param of module.params) {
const clItem = new vscode.CompletionItem(param.name, vscode.CompletionItemKind.Constant);
clItem.detail = 'param';
suggestParamsPorts.push(clItem);
}
for (const port of module.ports) {
const clItem = new vscode.CompletionItem(port.name, vscode.CompletionItemKind.Interface);
clItem.detail = 'port';
suggestParamsPorts.push(clItem);
}
return suggestParamsPorts;
}
private async provideNets(symbols: RawSymbol[]): Promise<vscode.CompletionItem[]> {
if (!symbols) {
return [];
}
const suggestNets = [];
for (const symbol of symbols) {
if (symbol.type === 'wire' || symbol.type === 'reg') {
const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable);
clItem.sortText = '';
clItem.detail = symbol.type;
suggestNets.push(clItem);
}
}
return suggestNets;
}
};
const vhdlCompletionProvider = new VhdlCompletionProvider();
export {
vhdlCompletionProvider
};

View File

@ -1,12 +1,16 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { HdlLangID } from '../../../global/enum';
interface BaseLinter { interface BaseLinter {
diagnostic: vscode.DiagnosticCollection; diagnostic: vscode.DiagnosticCollection;
lint(document: vscode.TextDocument): Promise<void>; lint(document: vscode.TextDocument): Promise<void>;
remove(uri: vscode.Uri): Promise<void>;
initialise(langID: HdlLangID): Promise<boolean>;
} }
interface BaseManager { interface BaseManager {
initialise(): Promise<void>; initialise(): Promise<void>;
updateLinter(): Promise<boolean>;
} }

View File

@ -73,13 +73,8 @@ class DefaultVlogLinter implements BaseLinter {
} }
public async initialise() { public async initialise() {
for (const doc of vscode.workspace.textDocuments) { // move code to outer layer
if (isVerilogFile(doc.fileName)) { return true;
// TODO : check performance
await this.lint(doc);
}
}
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
} }
} }
@ -149,13 +144,8 @@ class DefaultVHDLLinter implements BaseLinter {
} }
public async initialise() { public async initialise() {
for (const doc of vscode.workspace.textDocuments) { // move code to outer layer
if (isVhdlFile(doc.fileName)) { return true;
// TODO : check performance
await this.lint(doc);
}
}
LspOutput.report('finish initialization of default vlog linter', ReportType.Launch);
} }
} }

View File

@ -51,10 +51,14 @@ class ModelsimLinter implements BaseLinter {
this.diagnostic.set(document.uri, diagnostics); this.diagnostic.set(document.uri, diagnostics);
} }
} else { } else {
LspOutput.report('linter is not available, please check prj.modelsim.install.path in your setting', ReportType.Error); LspOutput.report('modelsim linter is not available, please check prj.modelsim.install.path in your setting!', ReportType.Error, true);
} }
} }
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
/** /**
* @param document * @param document
* @param stdout stdout from xvlog * @param stdout stdout from xvlog
@ -62,21 +66,24 @@ class ModelsimLinter implements BaseLinter {
*/ */
private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] { private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] {
const diagnostics = []; const diagnostics = [];
for (const line of stdout.split('\n')) { for (const line of stdout.split(/\r?\n/g)) {
const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm); const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm);
const headerInfo = tokens[1];
const headerInfo = tokens[0];
if (headerInfo === 'Error') { if (headerInfo === 'Error') {
const errorLine = parseInt(tokens[2]) - 1; const errorLine = parseInt(tokens[2]) - 1;
const syntaxInfo = tokens[5]; const syntaxInfo = tokens[5];
const range = this.makeCorrectRange(document, errorLine); LspOutput.report(`<vlog linter> line: ${errorLine}, info: ${syntaxInfo}`, ReportType.Run);
const range = this.makeCorrectRange(document, errorLine, syntaxInfo);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error); const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
diagnostics.push(diag); diagnostics.push(diag);
} else if (headerInfo == 'Warning') { } else if (headerInfo === 'Warning') {
const errorLine = parseInt(tokens[2]) - 1; const errorLine = parseInt(tokens[2]) - 1;
const syntaxInfo = tokens[5]; const syntaxInfo = tokens[5];
const range = this.makeCorrectRange(document, errorLine); LspOutput.report(`<vlog linter> line: ${errorLine}, info: ${syntaxInfo}`, ReportType.Run);
const range = this.makeCorrectRange(document, errorLine, syntaxInfo);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Warning); const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Warning);
diagnostics.push(diag); diagnostics.push(diag);
} }
@ -84,8 +91,32 @@ class ModelsimLinter implements BaseLinter {
return diagnostics; return diagnostics;
} }
private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range { private makeCorrectRange(document: vscode.TextDocument, line: number, syntaxInfo: string): vscode.Range {
// extract all the words like 'adawwd' in a syntax info
const singleQuoteWords = syntaxInfo.match(/'([^']*)'/g);
if (singleQuoteWords && singleQuoteWords.length > 0) {
const targetWord = singleQuoteWords.map(val => val.replace(/'/g, ''))[0];
// find range of target word
const textLine = document.lineAt(line);
const text = textLine.text;
const startCharacter = text.indexOf(targetWord);
if (startCharacter > -1) {
const endCharacter = startCharacter + targetWord.length;
const range = new vscode.Range(
new vscode.Position(line, startCharacter),
new vscode.Position(line, endCharacter)
);
return range;
}
}
// else target the first word in the line
return this.makeCommonRange(document, line, syntaxInfo);
}
private makeCommonRange(document: vscode.TextDocument, line: number, syntaxInfo: string): vscode.Range {
const startPosition = new vscode.Position(line, 0); const startPosition = new vscode.Position(line, 0);
const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/); const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/);
if (wordRange) { if (wordRange) {
return wordRange; return wordRange;
@ -107,11 +138,11 @@ class ModelsimLinter implements BaseLinter {
const fullExecutorName = opeParam.os === 'win32' ? executorName + '.exe' : executorName; const fullExecutorName = opeParam.os === 'win32' ? executorName + '.exe' : executorName;
if (modelsimInstallPath.trim() === '' || !fs.existsSync(modelsimInstallPath)) { if (modelsimInstallPath.trim() === '' || !fs.existsSync(modelsimInstallPath)) {
LspOutput.report(`User's modelsim Install Path ${modelsimInstallPath}, which is invalid. Use ${executorName} in default.`, ReportType.Warn); LspOutput.report(`User's modelsim Install Path "${modelsimInstallPath}", which is invalid. Use ${executorName} in default.`, ReportType.Warn);
LspOutput.report('If you have doubts, check prj.modelsim.install.path in setting', ReportType.Warn); LspOutput.report('If you have doubts, check prj.modelsim.install.path in setting', ReportType.Warn);
return executorName; return executorName;
} else { } else {
LspOutput.report(`User's modelsim Install Path ${modelsimInstallPath}, which is invalid`); LspOutput.report(`User's modelsim Install Path "${modelsimInstallPath}", which is invalid`);
const executorPath = hdlPath.join( const executorPath = hdlPath.join(
hdlPath.toSlash(modelsimInstallPath), hdlPath.toSlash(modelsimInstallPath),
@ -131,23 +162,23 @@ class ModelsimLinter implements BaseLinter {
} }
const { stderr } = await easyExec(executorPath, []); const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) { 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); this.executableInvokeNameMap.set(langID, executorPath);
LspOutput.report(`success to verify ${executorPath}, linter from modelsim is ready to go!`, ReportType.Launch); LspOutput.report(`success to verify ${executorPath}, linter from modelsim is ready to go!`, ReportType.Launch);
return true; return true;
} else {
this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error, true);
return false;
} }
} }
public initialise(langID: HdlLangID) { public async initialise(langID: HdlLangID): Promise<boolean> {
const executorPath = this.getExecutableFilePath(langID); const executorPath = this.getExecutableFilePath(langID);
this.setExecutableFilePath(executorPath, langID); return this.setExecutableFilePath(executorPath, langID);
} }
} }
const modelsimLinter = new ModelsimLinter() const modelsimLinter = new ModelsimLinter();
export { export {
modelsimLinter, modelsimLinter,

View File

@ -43,47 +43,59 @@ class VerilatorLinter implements BaseLinter {
const args = [filePath, ...linterArgs]; const args = [filePath, ...linterArgs];
const executor = this.executableInvokeNameMap.get(langID); const executor = this.executableInvokeNameMap.get(langID);
if (executor !== undefined) { if (executor !== undefined) {
const { stdout } = await easyExec(executor, args); const { stderr } = await easyExec(executor, args);
if (stdout.length > 0) { if (stderr.length > 0) {
const diagnostics = this.provideDiagnostics(document, stdout); const diagnostics = this.provideDiagnostics(document, stderr);
this.diagnostic.set(document.uri, diagnostics); this.diagnostic.set(document.uri, diagnostics);
} }
} else { } else {
LspOutput.report('linter is not available, please check prj.verilator.install.path in your setting', ReportType.Error); LspOutput.report('verilator linter is not available, please check prj.verilator.install.path in your setting', ReportType.Error, true);
} }
} }
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
/** /**
* @param document * @param document
* @param stdout stdout from xvlog * @param stdout stdout from xvlog
* @returns { vscode.Diagnostic[] } linter info * @returns { vscode.Diagnostic[] } linter info
*/ */
private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] { private provideDiagnostics(document: vscode.TextDocument, stderr: string): vscode.Diagnostic[] {
const diagnostics = []; const diagnostics = [];
for (const line of stdout.split('\n')) { for (let line of stderr.split(/\r?\n/g)) {
const tokens = line.split(/(Error|Warning).+?(?: *?(?:.+?(?:\\|\/))+.+?\((\d+?)\):|)(?: *?near "(.+?)":|)(?: *?\((.+?)\)|) +?(.+)/gm); if (!line.startsWith('%')) {
// TODO : make parser of output info from verilator continue;
} else {
line = line.substring(1);
}
const tokens = line.split(':');
if (tokens.length < 3) {
continue;
}
const header = tokens[0].toLowerCase();
const fileName = tokens[1];
const lineNo = parseInt(tokens[2]) - 1;
const characterNo = parseInt(tokens[3]) - 1;
const syntaxInfo = tokens[4];
const headerInfo = tokens[0]; if (header.startsWith('warning')) {
if (headerInfo === 'Error') { const range = this.makeCorrectRange(document, lineNo, characterNo);
const errorLine = parseInt(tokens[2]) - 1; const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Warning);
const syntaxInfo = tokens[5]; diagnostics.push(diag);
const range = this.makeCorrectRange(document, errorLine); } else if (header.startsWith('error')) {
const range = this.makeCorrectRange(document, lineNo, characterNo);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error); const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
diagnostics.push(diag); diagnostics.push(diag);
} else if (headerInfo == 'Warning') {
const errorLine = parseInt(tokens[2]) - 1;
const syntaxInfo = tokens[5];
const range = this.makeCorrectRange(document, errorLine);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Warning);
diagnostics.push(diag);
} }
} }
return diagnostics; return diagnostics;
} }
private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range { private makeCorrectRange(document: vscode.TextDocument, line: number, character: number): vscode.Range {
const startPosition = new vscode.Position(line, 0); const startPosition = new vscode.Position(line, character);
const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/); const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/);
if (wordRange) { if (wordRange) {
return wordRange; return wordRange;
@ -129,19 +141,20 @@ class VerilatorLinter implements BaseLinter {
} }
const { stderr } = await easyExec(executorPath, []); const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) { 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); this.executableInvokeNameMap.set(langID, executorPath);
LspOutput.report(`success to verify ${executorPath}, linter from verilator is ready to go!`, ReportType.Launch); LspOutput.report(`success to verify ${executorPath}, linter from verilator is ready to go!`, ReportType.Launch);
return true; return true;
} else {
this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error, true);
return false;
} }
} }
public initialise(langID: HdlLangID) { public async initialise(langID: HdlLangID): Promise<boolean> {
const executorPath = this.getExecutableFilePath(langID); const executorPath = this.getExecutableFilePath(langID);
this.setExecutableFilePath(executorPath, langID); return this.setExecutableFilePath(executorPath, langID);
} }
} }

View File

@ -5,6 +5,7 @@ import { BaseLinter, BaseManager } from './base';
import { defaultVlogLinter } from './default'; import { defaultVlogLinter } from './default';
import { modelsimLinter } from './modelsim'; import { modelsimLinter } from './modelsim';
import { vivadoLinter } from './vivado'; import { vivadoLinter } from './vivado';
import { hdlFile, hdlPath } from '../../../hdlFs';
class VhdlLinterManager implements BaseManager { class VhdlLinterManager implements BaseManager {
currentLinter: BaseLinter | undefined; currentLinter: BaseLinter | undefined;
@ -17,21 +18,37 @@ class VhdlLinterManager implements BaseManager {
this.activateList.set('default', false); this.activateList.set('default', false);
this.activateLinterName = 'default'; this.activateLinterName = 'default';
this.updateLinter();
// update when user's config is changed // update when user's config is changed
vscode.workspace.onDidChangeConfiguration(() => { vscode.workspace.onDidChangeConfiguration(() => {
const diagnostor = this.getUserDiagnostorSelection(); const diagnostor = this.getUserDiagnostorSelection();
const lastDiagnostor = this.activateLinterName; const lastDiagnostor = this.activateLinterName;
if (diagnostor !== lastDiagnostor) { if (diagnostor !== lastDiagnostor) {
LspOutput.report(`[vhdl lsp manager] detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, ); LspOutput.report(`<vhdl lsp manager> detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, );
this.updateLinter(); this.updateLinter();
} }
}); });
} }
async initialise(): Promise<void> { async initialise(): Promise<void> {
const success = await this.updateLinter();
if (!success) {
return;
}
for (const doc of vscode.workspace.textDocuments) {
const fileName = hdlPath.toSlash(doc.fileName);
if (hdlFile.isVhdlFile(fileName)) {
await this.lint(doc);
}
}
LspOutput.report('<vhdl lsp manager> finish initialization of vhdl linter. Linter name: ' + this.activateLinterName, ReportType.Launch);
}
async lint(document: vscode.TextDocument) {
await this.currentLinter?.lint(document);
}
async remove(uri: vscode.Uri): Promise<void> {
this.currentLinter?.remove(uri);
} }
public getUserDiagnostorSelection() { public getUserDiagnostorSelection() {
@ -40,53 +57,59 @@ class VhdlLinterManager implements BaseManager {
return diagnostor; return diagnostor;
} }
public updateLinter() { public async updateLinter(): Promise<boolean> {
const diagnostor = this.getUserDiagnostorSelection(); const diagnostor = this.getUserDiagnostorSelection();
switch (diagnostor) { switch (diagnostor) {
case 'vivado': this.activateVivado(); break; case 'vivado': return this.activateVivado();
case 'modelsim': this.activateModelsim(); break; case 'modelsim': return this.activateModelsim();
case 'default': this.activateDefault(); break; case 'default': return this.activateDefault();
case default: this.activateDefault(); break; default: return this.activateDefault();
} }
} }
public activateVivado() { public async activateVivado(): Promise<boolean> {
const selectedLinter = vivadoLinter; const selectedLinter = vivadoLinter;
let launch = true;
if (this.activateList.get('vivado') === false) { if (this.activateList.get('vivado') === false) {
selectedLinter.initialise(HdlLangID.Verilog); launch = await selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('vivado', true); this.activateList.set('vivado', true);
LspOutput.report('[vhdl lsp manager] vivado linter has been activated', ReportType.Info); LspOutput.report('<vhdl lsp manager> vivado linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'vivado'; this.activateLinterName = 'vivado';
return launch;
} }
public activateModelsim() { public async activateModelsim(): Promise<boolean> {
const selectedLinter = modelsimLinter; const selectedLinter = modelsimLinter;
let launch = true;
if (this.activateList.get('modelsim') === false) { if (this.activateList.get('modelsim') === false) {
selectedLinter.initialise(HdlLangID.Verilog); launch = await selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('modelsim', true); this.activateList.set('modelsim', true);
LspOutput.report('[vhdl lsp manager] modelsim linter has been activated', ReportType.Info); LspOutput.report('<vhdl lsp manager> modelsim linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'modelsim'; this.activateLinterName = 'modelsim';
return launch;
} }
public activateDefault() { public async activateDefault(): Promise<boolean> {
const selectedLinter = defaultVlogLinter; const selectedLinter = defaultVlogLinter;
let launch = true;
if (this.activateList.get('default') === false) { if (this.activateList.get('default') === false) {
this.activateList.set('default', true); this.activateList.set('default', true);
LspOutput.report('[vhdl lsp manager] default build-in linter has been activated', ReportType.Info); LspOutput.report('<vhdl lsp manager> default build-in linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'default'; this.activateLinterName = 'default';
return launch;
} }
} }

View File

@ -54,10 +54,14 @@ class VivadoLinter implements BaseLinter {
this.diagnostic.set(document.uri, diagnostics); this.diagnostic.set(document.uri, diagnostics);
} }
} else { } else {
LspOutput.report('linter is not available, please check prj.vivado.install.path in your setting', ReportType.Error); LspOutput.report('vivado linter is not available, please check prj.vivado.install.path in your setting', ReportType.Error, true);
} }
} }
async remove(uri: vscode.Uri) {
this.diagnostic.delete(uri);
}
/** /**
* @param document * @param document
* @param stdout stdout from xvlog * @param stdout stdout from xvlog
@ -65,7 +69,7 @@ class VivadoLinter implements BaseLinter {
*/ */
private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] { private provideDiagnostics(document: vscode.TextDocument, stdout: string): vscode.Diagnostic[] {
const diagnostics = []; const diagnostics = [];
for (const line of stdout.split('\n')) { for (const line of stdout.split(/\r?\n/g)) {
const tokens = line.split(/:?\s*(?:\[|\])\s*/); const tokens = line.split(/:?\s*(?:\[|\])\s*/);
const headerInfo = tokens[0]; const headerInfo = tokens[0];
// const standardInfo = tokens[1]; // const standardInfo = tokens[1];
@ -73,8 +77,10 @@ class VivadoLinter implements BaseLinter {
const parsedPath = tokens[3]; const parsedPath = tokens[3];
if (headerInfo === 'ERROR') { if (headerInfo === 'ERROR') {
const errorInfos = parsedPath.split(':'); const errorInfos = parsedPath.split(':');
const errorLine = parseInt(errorInfos[errorInfos.length - 1]); const errorLine = Math.max(parseInt(errorInfos[errorInfos.length - 1]) - 1, 0);
const range = this.makeCorrectRange(document, errorLine); LspOutput.report(`<xvlog linter> line: ${errorLine}, info: ${syntaxInfo}`, ReportType.Run);
const range = this.makeCorrectRange(document, errorLine, syntaxInfo);
const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error); const diag = new vscode.Diagnostic(range, syntaxInfo, vscode.DiagnosticSeverity.Error);
diagnostics.push(diag); diagnostics.push(diag);
} }
@ -82,8 +88,32 @@ class VivadoLinter implements BaseLinter {
return diagnostics; return diagnostics;
} }
private makeCorrectRange(document: vscode.TextDocument, line: number): vscode.Range { private makeCorrectRange(document: vscode.TextDocument, line: number, syntaxInfo: string): vscode.Range {
// extract all the words like 'adawwd' in a syntax info
const singleQuoteWords = syntaxInfo.match(/'([^']*)'/g);
if (singleQuoteWords && singleQuoteWords.length > 0) {
const targetWord = singleQuoteWords.map(val => val.replace(/'/g, ''))[0];
// find range of target word
const textLine = document.lineAt(line);
const text = textLine.text;
const startCharacter = text.indexOf(targetWord);
if (startCharacter > -1) {
const endCharacter = startCharacter + targetWord.length;
const range = new vscode.Range(
new vscode.Position(line, startCharacter),
new vscode.Position(line, endCharacter)
);
return range;
}
}
// else target the first word in the line
return this.makeCommonRange(document, line, syntaxInfo);
}
private makeCommonRange(document: vscode.TextDocument, line: number, syntaxInfo: string): vscode.Range {
const startPosition = new vscode.Position(line, 0); const startPosition = new vscode.Position(line, 0);
const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/); const wordRange = document.getWordRangeAtPosition(startPosition, /[`_0-9a-zA-Z]+/);
if (wordRange) { if (wordRange) {
return wordRange; return wordRange;
@ -105,11 +135,11 @@ class VivadoLinter implements BaseLinter {
const fullExecutorName = opeParam.os === 'win32' ? executorName + '.bat' : executorName; const fullExecutorName = opeParam.os === 'win32' ? executorName + '.bat' : executorName;
if (vivadoInstallPath.trim() === '' || !fs.existsSync(vivadoInstallPath)) { 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(`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); LspOutput.report('If you have doubts, check prj.vivado.install.path in setting', ReportType.Warn);
return executorName; return executorName;
} else { } else {
LspOutput.report(`User's Vivado Install Path ${vivadoInstallPath}, which is invalid`); LspOutput.report(`User's Vivado Install Path "${vivadoInstallPath}", which is invalid`);
const executorPath = hdlPath.join( const executorPath = hdlPath.join(
hdlPath.toSlash(vivadoInstallPath), hdlPath.toSlash(vivadoInstallPath),
@ -129,19 +159,19 @@ class VivadoLinter implements BaseLinter {
} }
const { stderr } = await easyExec(executorPath, []); const { stderr } = await easyExec(executorPath, []);
if (stderr.length === 0) { 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); this.executableInvokeNameMap.set(langID, executorPath);
LspOutput.report(`success to verify ${executorPath}, linter from vivado is ready to go!`, ReportType.Launch); LspOutput.report(`success to verify ${executorPath}, linter from vivado is ready to go!`, ReportType.Launch);
return true; return true;
} else {
this.executableInvokeNameMap.set(langID, undefined);
LspOutput.report(`fail to execute ${executorPath}! Reason: ${stderr}`, ReportType.Error, true);
return false;
} }
} }
public initialise(langID: HdlLangID) { public async initialise(langID: HdlLangID): Promise<boolean> {
const executorPath = this.getExecutableFilePath(langID); const executorPath = this.getExecutableFilePath(langID);
this.setExecutableFilePath(executorPath, langID); return this.setExecutableFilePath(executorPath, langID);
} }
} }

View File

@ -5,6 +5,7 @@ import { BaseLinter, BaseManager } from './base';
import { defaultVlogLinter } from './default'; import { defaultVlogLinter } from './default';
import { modelsimLinter } from './modelsim'; import { modelsimLinter } from './modelsim';
import { vivadoLinter } from './vivado'; import { vivadoLinter } from './vivado';
import { hdlFile, hdlPath } from '../../../hdlFs';
class VlogLinterManager implements BaseManager { class VlogLinterManager implements BaseManager {
currentLinter: BaseLinter | undefined; currentLinter: BaseLinter | undefined;
@ -17,21 +18,39 @@ class VlogLinterManager implements BaseManager {
this.activateList.set('default', false); this.activateList.set('default', false);
this.activateLinterName = 'default'; this.activateLinterName = 'default';
this.updateLinter();
// update when user's config is changed // update when user's config is changed
vscode.workspace.onDidChangeConfiguration(() => { vscode.workspace.onDidChangeConfiguration(() => {
const diagnostor = this.getUserDiagnostorSelection(); const diagnostor = this.getUserDiagnostorSelection();
const lastDiagnostor = this.activateLinterName; const lastDiagnostor = this.activateLinterName;
if (diagnostor !== lastDiagnostor) { if (diagnostor !== lastDiagnostor) {
LspOutput.report(`detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, ); LspOutput.report(`<vlog lsp manager> detect linter setting changes, switch ${lastDiagnostor} to ${diagnostor}.`, );
this.updateLinter(); this.updateLinter();
} }
}); });
} }
async initialise(): Promise<void> { async initialise(): Promise<void> {
const success = await this.updateLinter();
if (!success) {
return;
}
for (const doc of vscode.workspace.textDocuments) {
const fileName = hdlPath.toSlash(doc.fileName);
if (hdlFile.isVerilogFile(fileName)) {
await this.lint(doc);
}
}
LspOutput.report('<vlog lsp manager> finish initialization of vlog linter. Linter name: ' + this.activateLinterName, ReportType.Launch);
}
async lint(document: vscode.TextDocument) {
await this.currentLinter?.lint(document);
}
async remove(uri: vscode.Uri): Promise<void> {
this.currentLinter?.remove(uri);
} }
public getUserDiagnostorSelection() { public getUserDiagnostorSelection() {
@ -40,53 +59,59 @@ class VlogLinterManager implements BaseManager {
return diagnostor; return diagnostor;
} }
public updateLinter() { public async updateLinter() {
const diagnostor = this.getUserDiagnostorSelection(); const diagnostor = this.getUserDiagnostorSelection();
switch (diagnostor) { switch (diagnostor) {
case 'vivado': this.activateVivado(); break; case 'vivado': return this.activateVivado();
case 'modelsim': this.activateModelsim(); break; case 'modelsim': return this.activateModelsim();
case 'default': this.activateDefault(); break; case 'default': return this.activateDefault();
case default: this.activateDefault(); break; default: return this.activateDefault();
} }
} }
public activateVivado() { public async activateVivado(): Promise<boolean> {
const selectedLinter = vivadoLinter; const selectedLinter = vivadoLinter;
let launch = true;
if (this.activateList.get('vivado') === false) { if (this.activateList.get('vivado') === false) {
selectedLinter.initialise(HdlLangID.Verilog); launch = await selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('vivado', true); this.activateList.set('vivado', true);
LspOutput.report('vivado linter has been activated', ReportType.Info); LspOutput.report('<vlog lsp manager> vivado linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'vivado'; this.activateLinterName = 'vivado';
return launch;
} }
public activateModelsim() { public async activateModelsim(): Promise<boolean> {
const selectedLinter = modelsimLinter; const selectedLinter = modelsimLinter;
let launch = true;
if (this.activateList.get('modelsim') === false) { if (this.activateList.get('modelsim') === false) {
selectedLinter.initialise(HdlLangID.Verilog); launch = await selectedLinter.initialise(HdlLangID.Verilog);
this.activateList.set('modelsim', true); this.activateList.set('modelsim', true);
LspOutput.report('modelsim linter has been activated', ReportType.Info); LspOutput.report('<vlog lsp manager> modelsim linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'modelsim'; this.activateLinterName = 'modelsim';
return launch;
} }
public activateDefault() { public async activateDefault(): Promise<boolean> {
const selectedLinter = defaultVlogLinter; const selectedLinter = defaultVlogLinter;
let launch = true;
if (this.activateList.get('default') === false) { if (this.activateList.get('default') === false) {
this.activateList.set('default', true); this.activateList.set('default', true);
LspOutput.report('default build-in linter has been activated', ReportType.Info); LspOutput.report('<vlog lsp manager> default build-in linter has been activated', ReportType.Info);
} }
this.currentLinter = selectedLinter; this.currentLinter = selectedLinter;
this.activateLinterName = 'default'; this.activateLinterName = 'default';
return launch;
} }
} }

View File

@ -12,9 +12,9 @@ import { hdlParam, HdlSymbol } from '../hdlParser';
import { prjManage } from '../manager'; import { prjManage } from '../manager';
import { libManage } from '../manager/lib'; import { libManage } from '../manager/lib';
import type { HdlMonitor } from './index'; import type { HdlMonitor } from './index';
import { ToolChainType } from '../global/enum'; import { HdlLangID, ToolChainType } from '../global/enum';
import { hdlSymbolStorage } from '../function/lsp/core'; import { hdlSymbolStorage } from '../function/lsp/core';
import { vlogLinter } from '../function/lsp/linter'; import { vlogLinterManager, vhdlLinterManager } from '../function/lsp/linter';
import { isVerilogFile } from '../hdlFs/file'; import { isVerilogFile } from '../hdlFs/file';
enum Event { enum Event {
@ -98,7 +98,12 @@ class HdlAction extends BaseAction {
} }
const uri = vscode.Uri.file(path); const uri = vscode.Uri.file(path);
vlogLinter.remove(uri); const langID = hdlFile.getLanguageId(path);
if (langID === HdlLangID.Verilog) {
vlogLinterManager.remove(uri);
} else if (langID === HdlLangID.Vhdl) {
vhdlLinterManager.remove(uri);
}
} }
async unlinkDir(path: string, m: HdlMonitor): Promise<void> { async unlinkDir(path: string, m: HdlMonitor): Promise<void> {
@ -128,10 +133,16 @@ class HdlAction extends BaseAction {
} }
async updateLinter(path: string) { async updateLinter(path: string) {
if (isVerilogFile(path)) { const uri = vscode.Uri.file(path);
const uri = vscode.Uri.file(path); const document = await vscode.workspace.openTextDocument(uri);
const document = await vscode.workspace.openTextDocument(uri); const langID = hdlFile.getLanguageId(path);
vlogLinter.lint(document);
if (langID === HdlLangID.Verilog) {
vlogLinterManager.lint(document);
} else if (langID === HdlLangID.Vhdl) {
vhdlLinterManager.lint(document);
} else if (langID === HdlLangID.SystemVerilog) {
// TODO
} }
} }

View File

@ -71,6 +71,18 @@
} }
} }
}, },
{
"name": "digital-ide.Launch",
"match": "(\\[Launch - (.*)\\])([\\s\\S]*)",
"captures": {
"1": {
"name": "keyword.launch-token"
},
"2": {
"name": "string"
}
}
},
{ {
"name": "string.quoted.double", "name": "string.quoted.double",
"begin": "\"", "begin": "\"",

View File

@ -0,0 +1,361 @@
{
"fileTypes": [
"v",
"vh"
],
"keyEquivalent": "^~V",
"name": "Verilog",
"patterns": [
{
"include": "#comments"
},
{
"include": "#module_pattern"
},
{
"include": "#keywords"
},
{
"include": "#constants"
},
{
"include": "#strings"
},
{
"include": "#operators"
},
{
"include": "#macro_definition"
},
{
"include": "#macro_quote"
},
{
"include": "#common_variable"
}
],
"repository": {
"comments": {
"patterns": [
{
"begin": "(^[ \\t]+)?(?=#)",
"beginCaptures": {
"1": {
"name": "punctuation.whitespace.comment.leading.verilog"
}
},
"end": "(?!\\G)",
"patterns": [
{
"begin": "#",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.verilog"
}
},
"end": "\\n",
"name": "comment.line.double-slash.verilog"
}
]
},
{
"begin": "/\\*",
"end": "\\*/",
"name": "comment.block.c-style.verilog"
}
]
},
"constants": {
"patterns": [
{
"match": "\\b[0-9]+'[bBoOdDhH][a-fA-F0-9_xXzZ]+\\b",
"name": "constant.numeric.sized_integer.verilog"
},
{
"captures": {
"1": {
"name": "constant.numeric.integer.verilog"
},
"2": {
"name": "punctuation.separator.range.verilog"
},
"3": {
"name": "constant.numeric.integer.verilog"
}
},
"match": "\\b(\\d+)(:)(\\d+)\\b",
"name": "meta.block.numeric.range.verilog"
},
{
"match": "\\b\\d+(?i:e\\d+)?\\b",
"name": "constant.numeric.integer.verilog"
},
{
"match": "\\b\\d+\\.\\d+(?i:e\\d+)?\\b",
"name": "constant.numeric.real.verilog"
},
{
"match": "#\\d+",
"name": "constant.numeric.delay.verilog"
},
{
"match": "\\b[01xXzZ]+\\b",
"name": "constant.numeric.logic.verilog"
}
]
},
"instantiation_patterns": {
"patterns": [
{
"include": "#keywords"
},
{
"begin": "^\\s*([a-zA-Z][a-zA-Z0-9_]*)\\s+([a-zA-Z][a-zA-Z0-9_]*)(?<!begin|if)\\s*(?=\\(|$)",
"beginCaptures": {
"1": {
"name": "entity.name.class.module.reference.verilog"
},
"2": {
"name": "entity.name.function.module.identifier.verilog"
}
},
"end": ";",
"endCaptures": {
"0": {
"name": "punctuation.terminator.expression.verilog"
}
},
"name": "variable.other.constant.instantiation.parameterless.verilog",
"patterns": [
{
"include": "#comments"
},
{
"include": "#constants"
},
{
"include": "#strings"
}
]
},
{
"begin": "^\\s*([a-zA-Z][a-zA-Z0-9_]*)\\s*(#)(?=\\s*\\()",
"beginCaptures": {
"1": {
"name": "entity.name.class.module.reference.verilog"
}
},
"end": ";",
"endCaptures": {
"0": {
"name": "punctuation.terminator.expression.verilog"
}
},
"name": "support.block.instantiation.with.parameters.verilog",
"patterns": [
{
"include": "#parenthetical_list"
},
{
"match": "[a-zA-Z][a-zA-Z0-9_]*",
"name": "entity.name.function.module.identifier.verilog"
}
]
}
]
},
"keywords": {
"patterns": [
{
"match": "\\b(always|and|assign|attribute|begin|buf|bufif0|bufif1|case(xz)?|cmos|deassign|default|defparam|disable|edge|else|end(attribute|case|function|generate|module|primitive|specify|table|task)?|event|for|force|forever|fork|function|generate|genvar|highz(01)|if(none)?|initial|inout|input|integer|join|localparam|medium|module|large|macromodule|nand|negedge|nmos|nor|not|notif(01)|or|output|parameter|pmos|posedge|primitive|pull0|pull1|pulldown|pullup|rcmos|real|realtime|reg|release|repeat|rnmos|rpmos|rtran|rtranif(01)|scalared|signed|small|specify|specparam|strength|strong0|strong1|supply0|supply1|table|task|time|tran|tranif(01)|tri(01)?|tri(and|or|reg)|unsigned|vectored|wait|wand|weak(01)|while|wire|wor|xnor|xor)\\b",
"name": "keyword.other.verilog"
},
{
"match": "^\\s*`((cell)?define|default_(decay_time|nettype|trireg_strength)|delay_mode_(path|unit|zero)|ifdef|include|end(if|celldefine)|else|(no)?unconnected_drive|resetall|timescale|undef)\\b",
"name": "keyword.other.compiler.directive.verilog"
},
{
"match": "\\$(f(open|close)|readmem(b|h)|timeformat|printtimescale|stop|finish|(s|real)?time|realtobits|bitstoreal|rtoi|itor|(f)?(display|write(h|b)))\\b",
"name": "support.function.system.console.tasks.verilog"
},
{
"match": "\\$(random|dist_(chi_square|erlang|exponential|normal|poisson|t|uniform))\\b",
"name": "support.function.system.random_number.tasks.verilog"
},
{
"match": "\\$((a)?sync\\$((n)?and|(n)or)\\$(array|plane))\\b",
"name": "support.function.system.pld_modeling.tasks.verilog"
},
{
"match": "\\$(q_(initialize|add|remove|full|exam))\\b",
"name": "support.function.system.stochastic.tasks.verilog"
},
{
"match": "\\$(hold|nochange|period|recovery|setup(hold)?|skew|width)\\b",
"name": "support.function.system.timing.tasks.verilog"
},
{
"match": "\\$(dump(file|vars|off|on|all|limit|flush))\\b",
"name": "support.function.system.vcd.tasks.verilog"
},
{
"match": "\\$(countdrivers|list|input|scope|showscopes|(no)?(key|log)|reset(_count|_value)?|(inc)?save|restart|showvars|getpattern|sreadmem(b|h)|scale)",
"name": "support.function.non-standard.tasks.verilog"
}
]
},
"module_pattern": {
"patterns": [
{
"begin": "\\b(module)\\s+([a-zA-Z][a-zA-Z0-9_]*)",
"beginCaptures": {
"1": {
"name": "storage.type.module.verilog"
},
"2": {
"name": "entity.name.type.module.verilog"
}
},
"end": "\\bendmodule\\b",
"endCaptures": {
"0": {
"name": "storage.type.module.verilog"
}
},
"name": "meta.block.module.verilog",
"patterns": [
{
"include": "#comments"
},
{
"include": "#keywords"
},
{
"include": "#constants"
},
{
"include": "#strings"
},
{
"include": "#instantiation_patterns"
},
{
"include": "#operators"
},
{
"include": "#position_ports"
},
{
"include": "#macro_quote"
},
{
"include": "#common_variable"
}
]
}
]
},
"operators": {
"patterns": [
{
"match": "\\+|-|\\*|/|%|(<|>)=?|(!|=)?==?|!|&&?|\\|\\|?|\\^?~|~\\^?",
"name": "keyword.operator.verilog"
}
]
},
"parenthetical_list": {
"patterns": [
{
"begin": "\\(",
"beginCaptures": {
"0": {
"name": "punctuation.section.list.verilog"
}
},
"end": "\\)",
"endCaptures": {
"0": {
"name": "punctuation.section.list.verilog"
}
},
"name": "support.block.parenthetical_list.verilog",
"patterns": [
{
"include": "#parenthetical_list"
},
{
"include": "#comments"
},
{
"include": "#keywords"
},
{
"include": "#constants"
},
{
"include": "#strings"
},
{
"include": "#position_ports"
},
{
"include": "#macro_quote"
},
{
"include": "#common_variable"
}
]
}
]
},
"position_ports": {
"patterns": [
{
"match": "\\.[a-zA-Z_][a-zA-Z_0-9]*",
"name": "variable.inst.port.verilog"
}
]
},
"strings": {
"patterns": [
{
"begin": "\"",
"end": "\"",
"name": "string.quoted.double.verilog",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escape.verilog"
}
]
}
]
},
"macro_definition": {
"patterns": [
{
"match": "(?<=`define\\s)\\w+",
"name": "support.function.macro.definition.verilog"
}
]
},
"macro_quote": {
"patterns": [
{
"match": "`[a-zA-Z_][a-zA-Z_0-9]*",
"name": "support.function.macro.quote.verilog"
}
]
},
"common_variable": {
"patterns": [
{
"match": "[a-zA-Z_][a-zA-Z_0-9]*",
"name": "variable.other.constant.verilog"
}
]
}
},
"scopeName": "source.vvp",
"uuid": "7F4396B3-A33E-44F0-8502-98CA6C25971F"
}

8
test/vlogFast.js Normal file
View File

@ -0,0 +1,8 @@
const { vlogFast } = require('../resources/hdlParser');
const testFile = 'c:/Users/11934/Project/Digital-IDE/Digital-Test/Verilog/dependence_test/parent.v';
(async () => {
const fast = vlogFast(testFile);
console.log(JSON.stringify(fast, null, ' '));
})();