finish vlog completion | definition | hover | docSymbol | formatter
This commit is contained in:
parent
b16f9bf104
commit
4fd79de4e4
199
format.json
199
format.json
@ -1,199 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"modules": [
|
||||
{
|
||||
"instances": [
|
||||
{
|
||||
"instparams": null,
|
||||
"instports": {
|
||||
"end": {
|
||||
"character": 10,
|
||||
"line": 21
|
||||
},
|
||||
"start": {
|
||||
"character": 4,
|
||||
"line": 18
|
||||
}
|
||||
},
|
||||
"name": "dependence_1",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 0,
|
||||
"line": 22
|
||||
},
|
||||
"start": {
|
||||
"character": 13,
|
||||
"line": 17
|
||||
}
|
||||
},
|
||||
"type": "dependence_1"
|
||||
},
|
||||
{
|
||||
"instparams": null,
|
||||
"instports": {
|
||||
"end": {
|
||||
"character": 9,
|
||||
"line": 28
|
||||
},
|
||||
"start": {
|
||||
"character": 4,
|
||||
"line": 25
|
||||
}
|
||||
},
|
||||
"name": "dependence_2",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 0,
|
||||
"line": 29
|
||||
},
|
||||
"start": {
|
||||
"character": 13,
|
||||
"line": 24
|
||||
}
|
||||
},
|
||||
"type": "dependence_2"
|
||||
}
|
||||
],
|
||||
"name": "Main",
|
||||
"params": [],
|
||||
"ports": [
|
||||
{
|
||||
"name": "a",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 17,
|
||||
"line": 11
|
||||
},
|
||||
"start": {
|
||||
"character": 16,
|
||||
"line": 11
|
||||
}
|
||||
},
|
||||
"type": "input",
|
||||
"width": "[3:0]"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 17,
|
||||
"line": 12
|
||||
},
|
||||
"start": {
|
||||
"character": 16,
|
||||
"line": 12
|
||||
}
|
||||
},
|
||||
"type": "input",
|
||||
"width": "[2:0]"
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 11,
|
||||
"line": 13
|
||||
},
|
||||
"start": {
|
||||
"character": 10,
|
||||
"line": 13
|
||||
}
|
||||
},
|
||||
"type": "input",
|
||||
"width": "Unknown"
|
||||
},
|
||||
{
|
||||
"name": "Qus",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 14,
|
||||
"line": 14
|
||||
},
|
||||
"start": {
|
||||
"character": 11,
|
||||
"line": 14
|
||||
}
|
||||
},
|
||||
"type": "output",
|
||||
"width": "Unknown"
|
||||
},
|
||||
{
|
||||
"name": "Qs",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 18,
|
||||
"line": 14
|
||||
},
|
||||
"start": {
|
||||
"character": 16,
|
||||
"line": 14
|
||||
}
|
||||
},
|
||||
"type": "output",
|
||||
"width": "Unknown"
|
||||
},
|
||||
{
|
||||
"name": "`main_o",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 27,
|
||||
"line": 14
|
||||
},
|
||||
"start": {
|
||||
"character": 20,
|
||||
"line": 14
|
||||
}
|
||||
},
|
||||
"type": "output",
|
||||
"width": "Unknown"
|
||||
}
|
||||
],
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 0,
|
||||
"line": 31
|
||||
},
|
||||
"start": {
|
||||
"character": 0,
|
||||
"line": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"languageId": "verilog",
|
||||
"macro": {
|
||||
"defines": [
|
||||
{
|
||||
"name": "main_o",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 19,
|
||||
"line": 9
|
||||
},
|
||||
"start": {
|
||||
"character": 1,
|
||||
"line": 9
|
||||
}
|
||||
},
|
||||
"value": "out"
|
||||
}
|
||||
],
|
||||
"includes": [
|
||||
{
|
||||
"path": "child_1.v",
|
||||
"range": {
|
||||
"end": {
|
||||
"character": 9,
|
||||
"line": 8
|
||||
},
|
||||
"start": {
|
||||
"character": 1,
|
||||
"line": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"invalid": []
|
||||
}
|
||||
}
|
11
package-lock.json
generated
11
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"showdown": "^2.1.0",
|
||||
"state-machine-cat": "^9.2.5",
|
||||
"temp": "^0.9.4",
|
||||
"vscode-textmate": "^9.0.0",
|
||||
"wavedrom": "^2.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -2829,6 +2830,11 @@
|
||||
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==",
|
||||
"deprecated": "no longer supported"
|
||||
},
|
||||
"node_modules/vscode-textmate": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz",
|
||||
"integrity": "sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg=="
|
||||
},
|
||||
"node_modules/wavedrom": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmmirror.com/wavedrom/-/wavedrom-2.9.1.tgz",
|
||||
@ -5216,6 +5222,11 @@
|
||||
"resolved": "https://registry.npmmirror.com/viz.js/-/viz.js-1.8.2.tgz",
|
||||
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ=="
|
||||
},
|
||||
"vscode-textmate": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz",
|
||||
"integrity": "sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg=="
|
||||
},
|
||||
"wavedrom": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmmirror.com/wavedrom/-/wavedrom-2.9.1.tgz",
|
||||
|
46
package.json
46
package.json
@ -129,6 +129,51 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "run the simulation command in terminal instead of output"
|
||||
},
|
||||
"function.lsp.formatter.vlog.default.style": {
|
||||
"scope": "window",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"kr",
|
||||
"ansi",
|
||||
"gnu"
|
||||
],
|
||||
"default": "kr",
|
||||
"description": "Select the verilog and systemverilog formatter style."
|
||||
},
|
||||
"function.lsp.formatter.vlog.default.args": {
|
||||
"scope": "window",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Add verilog formatter arguments here (like istyle)."
|
||||
},
|
||||
"function.lsp.formatter.vhdl.default.keyword-case": {
|
||||
"description": "Keyword case",
|
||||
"type": "string",
|
||||
"default": "LowerCase",
|
||||
"enum": [
|
||||
"LowerCase",
|
||||
"UpperCase"
|
||||
]
|
||||
},
|
||||
"function.lsp.formatter.vhdl.default.align-comments": {
|
||||
"description": "Align comments",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"function.lsp.formatter.vhdl.default.type-name-case": {
|
||||
"description": "Type name case",
|
||||
"type": "string",
|
||||
"default": "LowerCase",
|
||||
"enum": [
|
||||
"LowerCase",
|
||||
"UpperCase"
|
||||
]
|
||||
},
|
||||
"function.lsp.formatter.vhdl.default.indentation": {
|
||||
"description": "Indentation",
|
||||
"type": "number",
|
||||
"default": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -749,6 +794,7 @@
|
||||
"showdown": "^2.1.0",
|
||||
"state-machine-cat": "^9.2.5",
|
||||
"temp": "^0.9.4",
|
||||
"vscode-textmate": "^9.0.0",
|
||||
"wavedrom": "^2.9.1"
|
||||
}
|
||||
}
|
8
resources/formatter/index.d.ts
vendored
Normal file
8
resources/formatter/index.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
declare module formatterProvider {
|
||||
export const hdlFormatterProvider: vscode.DocumentFormattingEditProvider;
|
||||
}
|
||||
|
||||
export = formatterProvider;
|
137
resources/formatter/index.js
Normal file
137
resources/formatter/index.js
Normal file
@ -0,0 +1,137 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
"use strict";
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vscode = require('vscode');
|
||||
const temp = require("temp");
|
||||
|
||||
const vlogFormatter = require("./vlogFormatter.js");
|
||||
const vhdlFormatter = require("./vhdlFormatter.js");
|
||||
|
||||
class Formatter {
|
||||
constructor() {
|
||||
this.vlogFormatter = new VlogFormatter();
|
||||
this.vhdlFormatter = new VhdlFormatter();
|
||||
}
|
||||
|
||||
async provideDocumentFormattingEdits(document, options, token) {
|
||||
const edits = [];
|
||||
//Get document code
|
||||
let code_document = document.getText();
|
||||
let selection_document = this.getDocumentRange(document);
|
||||
//Get selected text
|
||||
let editor = vscode.window.activeTextEditor;
|
||||
let selection_selected_text = '';
|
||||
let code_selected_text = '';
|
||||
if (editor !== undefined) {
|
||||
selection_selected_text = editor.selection;
|
||||
code_selected_text = editor.document.getText(editor.selection);
|
||||
}
|
||||
//Code to format
|
||||
let code_to_format = '';
|
||||
let selection_to_format = '';
|
||||
if (code_selected_text !== '') {
|
||||
code_to_format = code_selected_text;
|
||||
selection_to_format = selection_selected_text;
|
||||
} else {
|
||||
code_to_format = code_document;
|
||||
selection_to_format = selection_document;
|
||||
}
|
||||
|
||||
let code_format = await this.format(document.languageId, code_to_format);
|
||||
if (code_format === null) {
|
||||
console.log("Error format code.");
|
||||
return edits;
|
||||
} else {
|
||||
const replacement = vscode.TextEdit.replace(selection_to_format, code_format);
|
||||
edits.push(replacement);
|
||||
return edits;
|
||||
}
|
||||
}
|
||||
|
||||
async format(language, code) {
|
||||
let options = null;
|
||||
let formatted_code = '';
|
||||
try {
|
||||
if (language === "vhdl") {
|
||||
options = this.get_vhdl_config();
|
||||
formatted_code = await this.vhdlFormatter.format_from_code(code, options);
|
||||
}
|
||||
else {
|
||||
options = this.get_vlog_config();
|
||||
formatted_code = await this.vlogFormatter.format_from_code(code, options);
|
||||
}
|
||||
return formatted_code;
|
||||
} catch (error) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
get_vlog_config() {
|
||||
let style = vscode.workspace.getConfiguration("function.lsp.formatter.vlog.default").get("style");
|
||||
let args = vscode.workspace.getConfiguration("function.lsp.formatter.vlog.default").get("args");
|
||||
return `--style=${style} ${args}`;
|
||||
}
|
||||
|
||||
get_vhdl_config() {
|
||||
let configuration = vscode.workspace.getConfiguration('function.lsp.formatter.vhdl.default');
|
||||
let settings = {
|
||||
"RemoveComments": false,
|
||||
"RemoveAsserts": false,
|
||||
"CheckAlias": false,
|
||||
"AlignComments": configuration.get('align-comments'),
|
||||
"SignAlignSettings": {
|
||||
"isRegional": true,
|
||||
"isAll": true,
|
||||
"mode": 'local',
|
||||
"keyWords": [
|
||||
"FUNCTION",
|
||||
"IMPURE FUNCTION",
|
||||
"GENERIC",
|
||||
"PORT",
|
||||
"PROCEDURE"
|
||||
]
|
||||
},
|
||||
"KeywordCase": configuration.get('keyword-case'),
|
||||
"TypeNameCase": configuration.get('type-name-case'),
|
||||
"Indentation": ' '.repeat(configuration.get('indentation')),
|
||||
"NewLineSettings": {
|
||||
"newLineAfter": [
|
||||
";",
|
||||
"then"
|
||||
],
|
||||
"noNewLineAfter": []
|
||||
},
|
||||
"EndOfLine": "\n"
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
getDocumentRange(document) {
|
||||
const lastLineId = document.lineCount - 1;
|
||||
return new vscode.Range(0, 0, lastLineId, document.lineAt(lastLineId).text.length);
|
||||
}
|
||||
}
|
||||
|
||||
class VlogFormatter {
|
||||
async format_from_code(code, options) {
|
||||
let verilogFormatter = await vlogFormatter();
|
||||
verilogFormatter.FS.writeFile("/share/FILE_IN.v", code, { encoding: 'utf8' });
|
||||
verilogFormatter.ccall('run', '', ['string'], [`${options} finish`]);
|
||||
let formatted_code = verilogFormatter.FS.readFile("/share/FILE_OUT.v", { encoding: 'utf8' });
|
||||
return formatted_code;
|
||||
}
|
||||
}
|
||||
|
||||
class VhdlFormatter {
|
||||
async format_from_code(code, options) {
|
||||
let beautifuler = new vhdlFormatter.Beautifuler();
|
||||
let formatted_code = beautifuler.beauty(code, options);
|
||||
return formatted_code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const hdlFormatterProvider = new Formatter();
|
||||
module.exports = { hdlFormatterProvider };
|
910
resources/formatter/vhdlFormatter.js
Normal file
910
resources/formatter/vhdlFormatter.js
Normal file
@ -0,0 +1,910 @@
|
||||
"use strict";
|
||||
|
||||
class Beautifuler {
|
||||
beauty(input, options) {
|
||||
let new_line_after_symbols = new NewLineSettings();
|
||||
new_line_after_symbols.newLineAfter = ["then", ";"];
|
||||
new_line_after_symbols.noNewLineAfter = ["port", "generic"];
|
||||
let settings = this.getDefaultBeautifierSettings(new_line_after_symbols);
|
||||
settings.SignAlignSettings = new signAlignSettings(true, true, "local", ["PORT", "GENERIC"]);
|
||||
|
||||
const result = this.beautifyIntern(input, options);
|
||||
if (result.err !== null) {
|
||||
console.error(`-- [ERROR]: could not beautify`);
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
|
||||
beautifyIntern(input, settings) {
|
||||
try {
|
||||
const data = beautify(input, settings);
|
||||
return {
|
||||
data,
|
||||
err: null,
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
return {
|
||||
data: null,
|
||||
err,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultBeautifierSettings(newLineSettings, signAlignSettings = null, indentation = " ") {
|
||||
return new BeautifierSettings(false, false, false, signAlignSettings, "lowercase", "lowercase", indentation, newLineSettings, "\r\n");
|
||||
}
|
||||
}
|
||||
exports.Beautifuler = Beautifuler;
|
||||
|
||||
// VHDLFormatter
|
||||
const ILEscape = "@@";
|
||||
const ILCommentPrefix = ILEscape + "comments";
|
||||
const ILIndentedReturnPrefix = ILEscape;
|
||||
const ILQuote = "⨵";
|
||||
const ILSingleQuote = "⦼";
|
||||
const ILBackslash = "⨸";
|
||||
const ILSemicolon = "⨴";
|
||||
var FormatMode;
|
||||
(function (FormatMode) {
|
||||
FormatMode[FormatMode["Default"] = 0] = "Default";
|
||||
FormatMode[FormatMode["EndsWithSemicolon"] = 1] = "EndsWithSemicolon";
|
||||
FormatMode[FormatMode["CaseWhen"] = 2] = "CaseWhen";
|
||||
FormatMode[FormatMode["IfElse"] = 3] = "IfElse";
|
||||
FormatMode[FormatMode["PortGeneric"] = 4] = "PortGeneric";
|
||||
})(FormatMode || (FormatMode = {}));
|
||||
let Mode = FormatMode.Default;
|
||||
exports.RemoveAsserts = exports.ApplyNoNewLineAfter = exports.beautify3 = exports.beautifySemicolonBlock = exports.beautifyPackageIsNewBlock = exports.beautifyComponentBlock = exports.beautifyCaseBlock = exports.AlignSign = exports.AlignSigns = exports.beautifyPortGenericBlock = exports.FormattedLineToString = exports.FormattedLine = exports.beautify = exports.BeautifierSettings = exports.signAlignSettings = exports.SetNewLinesAfterSymbols = exports.NewLineSettings = void 0;
|
||||
class NewLineSettings {
|
||||
constructor() {
|
||||
this.newLineAfter = [];
|
||||
this.noNewLineAfter = [];
|
||||
}
|
||||
newLineAfterPush(keyword) {
|
||||
this.newLineAfter.push(keyword);
|
||||
}
|
||||
noNewLineAfterPush(keyword) {
|
||||
this.noNewLineAfter.push(keyword);
|
||||
}
|
||||
push(keyword, addNewLine) {
|
||||
let str = addNewLine.toLowerCase();
|
||||
if (str == "none") {
|
||||
return;
|
||||
}
|
||||
else if (!str.startsWith("no")) {
|
||||
this.newLineAfterPush(keyword);
|
||||
}
|
||||
else {
|
||||
this.noNewLineAfterPush(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.NewLineSettings = NewLineSettings;
|
||||
function ConstructNewLineSettings(dict) {
|
||||
let settings = new NewLineSettings();
|
||||
for (let key in dict) {
|
||||
settings.push(key, dict[key]);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
String.prototype.regexCount = function (pattern) {
|
||||
if (pattern.flags.indexOf("g") < 0) {
|
||||
pattern = new RegExp(pattern.source, pattern.flags + "g");
|
||||
}
|
||||
return (this.match(pattern) || []).length;
|
||||
};
|
||||
String.prototype.count = function (text) {
|
||||
return this.split(text).length - 1;
|
||||
};
|
||||
String.prototype.regexStartsWith = function (pattern) {
|
||||
var searchResult = this.search(pattern);
|
||||
return searchResult == 0;
|
||||
};
|
||||
String.prototype.regexIndexOf = function (pattern, startIndex) {
|
||||
startIndex = startIndex || 0;
|
||||
var searchResult = this.substr(startIndex).search(pattern);
|
||||
return (-1 === searchResult) ? -1 : searchResult + startIndex;
|
||||
};
|
||||
String.prototype.regexLastIndexOf = function (pattern, startIndex) {
|
||||
pattern = (pattern.global) ? pattern :
|
||||
new RegExp(pattern.source, 'g' + (pattern.ignoreCase ? 'i' : '') + (pattern.multiline ? 'm' : ''));
|
||||
if (typeof (startIndex) === 'undefined') {
|
||||
startIndex = this.length;
|
||||
}
|
||||
else if (startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
const stringToWorkWith = this.substring(0, startIndex + 1);
|
||||
let lastIndexOf = -1;
|
||||
let nextStop = 0;
|
||||
let result;
|
||||
while ((result = pattern.exec(stringToWorkWith)) != null) {
|
||||
lastIndexOf = result.index;
|
||||
pattern.lastIndex = ++nextStop;
|
||||
}
|
||||
return lastIndexOf;
|
||||
};
|
||||
String.prototype.reverse = function () {
|
||||
return this.split('').reverse().join('');
|
||||
};
|
||||
String.prototype.convertToRegexBlockWords = function () {
|
||||
let result = new RegExp("(" + this + ")([^\\w]|$)");
|
||||
return result;
|
||||
};
|
||||
Array.prototype.convertToRegexBlockWords = function () {
|
||||
let wordsStr = this.join("|");
|
||||
let result = new RegExp("(" + wordsStr + ")([^\\w]|$)");
|
||||
return result;
|
||||
};
|
||||
function EscapeComments(arr) {
|
||||
var comments = [];
|
||||
var count = 0;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var line = arr[i];
|
||||
var commentStartIndex = line.indexOf("--");
|
||||
if (commentStartIndex >= 0) {
|
||||
comments.push(line.substr(commentStartIndex));
|
||||
arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
var isInComment = false;
|
||||
var commentRegex = new RegExp("(?<=" + ILCommentPrefix + "[\\d]+).");
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var commentStartIndex = 0;
|
||||
var hasComment = true;
|
||||
var commentEndInlineIndex = 0;
|
||||
while (hasComment) {
|
||||
var line = arr[i];
|
||||
if (!isInComment) {
|
||||
commentStartIndex = line.indexOf("/*");
|
||||
var commentEndIndex = line.indexOf("*/", commentStartIndex);
|
||||
if (commentStartIndex >= 0) {
|
||||
if (commentEndIndex >= 0) {
|
||||
commentEndInlineIndex = commentEndIndex + 2;
|
||||
isInComment = false;
|
||||
comments.push(line.substring(commentStartIndex, commentEndInlineIndex));
|
||||
arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count + line.substr(commentEndInlineIndex);
|
||||
count++;
|
||||
hasComment = true;
|
||||
if (commentStartIndex + 2 == line.length) {
|
||||
hasComment = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
isInComment = true;
|
||||
comments.push(line.substr(commentStartIndex));
|
||||
arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count;
|
||||
count++;
|
||||
hasComment = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hasComment = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isInComment) {
|
||||
var lastCommentEndIndex = line.regexLastIndexOf(commentRegex, line.length);
|
||||
if (commentStartIndex == 0) {
|
||||
var commentEndIndex = line.indexOf("*/", lastCommentEndIndex);
|
||||
}
|
||||
else {
|
||||
var commentEndIndex = line.indexOf("*/", commentStartIndex);
|
||||
}
|
||||
if (commentEndIndex >= 0) {
|
||||
isInComment = false;
|
||||
comments.push(line.substr(0, commentEndIndex + 2));
|
||||
arr[i] = ILCommentPrefix + count + line.substr(commentEndIndex + 2);
|
||||
count++;
|
||||
hasComment = true;
|
||||
}
|
||||
else {
|
||||
comments.push(line);
|
||||
arr[i] = ILCommentPrefix + count;
|
||||
count++;
|
||||
hasComment = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
function ToLowerCases(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = arr[i].toLowerCase();
|
||||
}
|
||||
}
|
||||
function ToUpperCases(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = arr[i].toUpperCase();
|
||||
}
|
||||
}
|
||||
function ToCamelCases(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = arr[i].charAt(0) + arr[i].slice(1).toLowerCase();
|
||||
}
|
||||
}
|
||||
function ReplaceKeyWords(text, keywords) {
|
||||
for (var k = 0; k < keywords.length; k++) {
|
||||
text = text.replace(new RegExp("([^a-zA-Z0-9_@]|^)" + keywords[k] + "([^a-zA-Z0-9_]|$)", 'gi'), "$1" + keywords[k] + "$2");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
function SetKeywordCase(input, keywordcase, keywords) {
|
||||
let inputcase = keywordcase.toLowerCase();
|
||||
switch (inputcase) {
|
||||
case "lowercase":
|
||||
ToLowerCases(keywords);
|
||||
break;
|
||||
case "defaultcase":
|
||||
ToCamelCases(keywords);
|
||||
break;
|
||||
case "uppercase":
|
||||
ToUpperCases(keywords);
|
||||
}
|
||||
input = ReplaceKeyWords(input, keywords);
|
||||
return input;
|
||||
}
|
||||
function SetNewLinesAfterSymbols(text, newLineSettings) {
|
||||
if (newLineSettings == null) {
|
||||
return text;
|
||||
}
|
||||
if (newLineSettings.newLineAfter != null) {
|
||||
newLineSettings.newLineAfter.forEach(symbol => {
|
||||
let upper = symbol.toUpperCase();
|
||||
var rexString = "(" + upper + ")[ ]?([^ \r\n@])";
|
||||
let regex = null;
|
||||
if (upper.regexStartsWith(/\w/)) {
|
||||
regex = new RegExp("\\b" + rexString, "g");
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(rexString, "g");
|
||||
}
|
||||
text = text.replace(regex, '$1\r\n$2');
|
||||
if (upper == "PORT") {
|
||||
text = text.replace(/\bPORT\b\s+MAP/, "PORT MAP");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (newLineSettings.noNewLineAfter != null) {
|
||||
newLineSettings.noNewLineAfter.forEach(symbol => {
|
||||
let rexString = "(" + symbol.toUpperCase() + ")[ \r\n]+([^@])";
|
||||
let regex = null;
|
||||
if (symbol.regexStartsWith(/\w/)) {
|
||||
regex = new RegExp("\\b" + rexString, "g");
|
||||
text = text.replace(regex, '$1 $2');
|
||||
}
|
||||
else {
|
||||
regex = new RegExp(rexString, "g");
|
||||
}
|
||||
text = text.replace(regex, '$1 $2');
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
exports.SetNewLinesAfterSymbols = SetNewLinesAfterSymbols;
|
||||
class signAlignSettings {
|
||||
constructor(isRegional, isAll, mode, keyWords) {
|
||||
this.isRegional = isRegional;
|
||||
this.isAll = isAll;
|
||||
this.mode = mode;
|
||||
this.keyWords = keyWords;
|
||||
}
|
||||
}
|
||||
exports.signAlignSettings = signAlignSettings;
|
||||
class BeautifierSettings {
|
||||
constructor(removeComments, removeReport, checkAlias, alignComments, signAlignSettings, keywordCase, typeNameCase, indentation, newLineSettings, endOfLine, addNewLine) {
|
||||
this.RemoveComments = removeComments;
|
||||
this.RemoveAsserts = removeReport;
|
||||
this.CheckAlias = checkAlias;
|
||||
this.AlignComments = alignComments;
|
||||
this.SignAlignSettings = signAlignSettings;
|
||||
this.KeywordCase = keywordCase;
|
||||
this.TypeNameCase = typeNameCase;
|
||||
this.Indentation = indentation;
|
||||
this.NewLineSettings = newLineSettings;
|
||||
this.EndOfLine = endOfLine;
|
||||
this.AddNewLine = addNewLine;
|
||||
}
|
||||
}
|
||||
exports.BeautifierSettings = BeautifierSettings;
|
||||
let KeyWords = ["ABS", "ACCESS", "AFTER", "ALIAS", "ALL", "AND", "ARCHITECTURE", "ARRAY", "ASSERT", "ATTRIBUTE", "BEGIN", "BLOCK", "BODY", "BUFFER", "BUS", "CASE", "COMPONENT", "CONFIGURATION", "CONSTANT", "CONTEXT", "COVER", "DISCONNECT", "DOWNTO", "DEFAULT", "ELSE", "ELSIF", "END", "ENTITY", "EXIT", "FAIRNESS", "FILE", "FOR", "FORCE", "FUNCTION", "GENERATE", "GENERIC", "GROUP", "GUARDED", "IF", "IMPURE", "IN", "INERTIAL", "INOUT", "IS", "LABEL", "LIBRARY", "LINKAGE", "LITERAL", "LOOP", "MAP", "MOD", "NAND", "NEW", "NEXT", "NOR", "NOT", "NULL", "OF", "ON", "OPEN", "OR", "OTHERS", "OUT", "PACKAGE", "PORT", "POSTPONED", "PROCEDURE", "PROCESS", "PROPERTY", "PROTECTED", "PURE", "RANGE", "RECORD", "REGISTER", "REJECT", "RELEASE", "REM", "REPORT", "RESTRICT", "RESTRICT_GUARANTEE", "RETURN", "ROL", "ROR", "SELECT", "SEQUENCE", "SEVERITY", "SHARED", "SIGNAL", "SLA", "SLL", "SRA", "SRL", "STRONG", "SUBTYPE", "THEN", "TO", "TRANSPORT", "TYPE", "UNAFFECTED", "UNITS", "UNTIL", "USE", "VARIABLE", "VMODE", "VPROP", "VUNIT", "WAIT", "WHEN", "WHILE", "WITH", "XNOR", "XOR"];
|
||||
let TypeNames = ["BOOLEAN", "BIT", "CHARACTER", "INTEGER", "TIME", "NATURAL", "POSITIVE", "STD_LOGIC", "STD_LOGIC_VECTOR", "STD_ULOGIC", "STD_ULOGIC_VECTOR", "STRING"];
|
||||
function beautify(input, settings) {
|
||||
input = input.replace(/\r\n/g, "\n");
|
||||
input = input.replace(/\n/g, "\r\n");
|
||||
var arr = input.split("\r\n");
|
||||
var comments = EscapeComments(arr);
|
||||
var backslashes = escapeText(arr, "\\\\[^\\\\]+\\\\", ILBackslash);
|
||||
let quotes = escapeText(arr, '"([^"]+)"', ILQuote);
|
||||
let singleQuotes = escapeText(arr, "'[^']'", ILSingleQuote);
|
||||
RemoveLeadingWhitespaces(arr);
|
||||
input = arr.join("\r\n");
|
||||
if (settings.RemoveComments) {
|
||||
input = input.replace(/\r\n[ \t]*@@comments[0-9]+[ \t]*\r\n/g, '\r\n');
|
||||
input = input.replace(/@@comments[0-9]+/g, '');
|
||||
comments = [];
|
||||
}
|
||||
input = SetKeywordCase(input, "uppercase", KeyWords);
|
||||
input = SetKeywordCase(input, "uppercase", TypeNames);
|
||||
input = RemoveExtraNewLines(input);
|
||||
input = input.replace(/[\t ]+/g, ' ');
|
||||
input = input.replace(/\([\t ]+/g, '\(');
|
||||
input = input.replace(/[ ]+;/g, ';');
|
||||
input = input.replace(/:[ ]*(PROCESS|ENTITY)/gi, ':$1');
|
||||
arr = input.split("\r\n");
|
||||
if (settings.RemoveAsserts) {
|
||||
RemoveAsserts(arr); //RemoveAsserts must be after EscapeQuotes
|
||||
}
|
||||
ReserveSemicolonInKeywords(arr);
|
||||
input = arr.join("\r\n");
|
||||
input = input.replace(/\b(PORT|GENERIC)\b\s+MAP/g, '$1 MAP');
|
||||
input = input.replace(/\b(PORT|PROCESS|GENERIC)\b[\s]*\(/g, '$1 (');
|
||||
let newLineSettings = settings.NewLineSettings;
|
||||
if (newLineSettings != null) {
|
||||
input = SetNewLinesAfterSymbols(input, newLineSettings);
|
||||
arr = input.split("\r\n");
|
||||
ApplyNoNewLineAfter(arr, newLineSettings.noNewLineAfter);
|
||||
input = arr.join("\r\n");
|
||||
}
|
||||
input = input.replace(/([a-zA-Z0-9\); ])\);(@@comments[0-9]+)?@@end/g, '$1\r\n);$2@@end');
|
||||
input = input.replace(/[ ]?([&=:\-\+|\*]|[<>]+)[ ]?/g, ' $1 ');
|
||||
input = input.replace(/(\d+e) +([+\-]) +(\d+)/g, '$1$2$3'); // fix exponential notation format broken by previous step
|
||||
input = input.replace(/[ ]?([,])[ ]?/g, '$1 ');
|
||||
input = input.replace(/[ ]?(['"])(THEN)/g, '$1 $2');
|
||||
input = input.replace(/[ ]?(\?)?[ ]?(<|:|>|\/)?[ ]+(=)?[ ]?/g, ' $1$2$3 ');
|
||||
input = input.replace(/(IF)[ ]?([\(\)])/g, '$1 $2');
|
||||
input = input.replace(/([\(\)])[ ]?(THEN)/gi, '$1 $2');
|
||||
input = input.replace(/(^|[\(\)])[ ]?(AND|OR|XOR|XNOR)[ ]*([\(])/g, '$1 $2 $3');
|
||||
input = input.replace(/ ([\-\*\/=+<>])[ ]*([\-\*\/=+<>]) /g, " $1$2 ");
|
||||
//input = input.replace(/\r\n[ \t]+--\r\n/g, "\r\n");
|
||||
input = input.replace(/[ ]+/g, ' ');
|
||||
input = input.replace(/[ \t]+\r\n/g, "\r\n");
|
||||
input = input.replace(/\r\n\r\n\r\n/g, '\r\n');
|
||||
input = input.replace(/[\r\n\s]+$/g, '');
|
||||
input = input.replace(/[ \t]+\)/g, ')');
|
||||
input = input.replace(/\s*\)\s+RETURN\s+([\w]+;)/g, '\r\n) RETURN $1'); //function(..)\r\nreturn type; -> function(..\r\n)return type;
|
||||
input = input.replace(/\)\s*(@@\w+)\r\n\s*RETURN\s+([\w]+;)/g, ') $1\r\n' + ILIndentedReturnPrefix + 'RETURN $2'); //function(..)\r\nreturn type; -> function(..\r\n)return type;
|
||||
let keywordAndSignRegex = new RegExp("(\\b" + KeyWords.join("\\b|\\b") + "\\b) +([\\-+]) +(\\w)", "g");
|
||||
input = input.replace(keywordAndSignRegex, "$1 $2$3"); // `WHEN - 2` -> `WHEN -2`
|
||||
input = input.replace(/([,|]) +([+\-]) +(\w)/g, '$1 $2$3'); // `1, - 2)` -> `1, -2)`
|
||||
input = input.replace(/(\() +([+\-]) +(\w)/g, '$1$2$3'); // `( - 2)` -> `(-2)`
|
||||
arr = input.split("\r\n");
|
||||
let result = [];
|
||||
beautify3(arr, result, settings, 0, 0);
|
||||
var alignSettings = settings.SignAlignSettings;
|
||||
if (alignSettings != null && alignSettings.isAll) {
|
||||
AlignSigns(result, 0, result.length - 1, alignSettings.mode, settings.AlignComments);
|
||||
}
|
||||
arr = FormattedLineToString(result, settings.Indentation);
|
||||
input = arr.join("\r\n");
|
||||
input = input.replace(/@@RETURN/g, "RETURN");
|
||||
input = SetKeywordCase(input, settings.KeywordCase, KeyWords);
|
||||
input = SetKeywordCase(input, settings.TypeNameCase, TypeNames);
|
||||
input = replaceEscapedWords(input, quotes, ILQuote);
|
||||
input = replaceEscapedWords(input, singleQuotes, ILSingleQuote);
|
||||
input = replaceEscapedComments(input, comments, ILCommentPrefix);
|
||||
input = replaceEscapedWords(input, backslashes, ILBackslash);
|
||||
input = input.replace(new RegExp(ILSemicolon, "g"), ";");
|
||||
input = input.replace(/@@[a-z]+/g, "");
|
||||
var escapedTexts = new RegExp("[" + ILBackslash + ILQuote + ILSingleQuote + "]", "g");
|
||||
input = input.replace(escapedTexts, "");
|
||||
input = input.replace(/\r\n/g, settings.EndOfLine);
|
||||
if (settings.AddNewLine && !input.endsWith(settings.EndOfLine)) {
|
||||
input += settings.EndOfLine;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
exports.beautify = beautify;
|
||||
function replaceEscapedWords(input, arr, prefix) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var text = arr[i];
|
||||
var regex = new RegExp("(" + prefix + "){" + text.length + "}");
|
||||
input = input.replace(regex, text);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
function replaceEscapedComments(input, arr, prefix) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
input = input.replace(prefix + i, arr[i]);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
function RemoveLeadingWhitespaces(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = arr[i].replace(/^\s+/, "");
|
||||
}
|
||||
}
|
||||
class FormattedLine {
|
||||
constructor(line, indent) {
|
||||
this.Line = line;
|
||||
this.Indent = indent;
|
||||
}
|
||||
}
|
||||
exports.FormattedLine = FormattedLine;
|
||||
function FormattedLineToString(arr, indentation) {
|
||||
let result = [];
|
||||
if (arr == null) {
|
||||
return result;
|
||||
}
|
||||
if (indentation == null) {
|
||||
indentation = "";
|
||||
}
|
||||
arr.forEach(i => {
|
||||
if (i instanceof FormattedLine) {
|
||||
if (i.Line.length > 0) {
|
||||
result.push((Array(i.Indent + 1).join(indentation)) + i.Line);
|
||||
}
|
||||
else {
|
||||
result.push("");
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = result.concat(FormattedLineToString(i, indentation));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
exports.FormattedLineToString = FormattedLineToString;
|
||||
function GetCloseparentheseEndIndex(inputs, startIndex) {
|
||||
let openParentheseCount = 0;
|
||||
let closeParentheseCount = 0;
|
||||
for (let i = startIndex; i < inputs.length; i++) {
|
||||
let input = inputs[i];
|
||||
openParentheseCount += input.count("(");
|
||||
closeParentheseCount += input.count(")");
|
||||
if (openParentheseCount > 0
|
||||
&& openParentheseCount <= closeParentheseCount) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return startIndex;
|
||||
}
|
||||
function beautifyPortGenericBlock(inputs, result, settings, startIndex, parentEndIndex, indent, mode) {
|
||||
let firstLine = inputs[startIndex];
|
||||
let regex = new RegExp("[\\w\\s:]*(" + mode + ")([\\s]|$)");
|
||||
if (!firstLine.regexStartsWith(regex)) {
|
||||
return [startIndex, parentEndIndex];
|
||||
}
|
||||
let firstLineHasParenthese = firstLine.indexOf("(") >= 0;
|
||||
let hasParenthese = firstLineHasParenthese;
|
||||
let blockBodyStartIndex = startIndex;
|
||||
let secondLineHasParenthese = startIndex + 1 < inputs.length && inputs[startIndex + 1].startsWith("(");
|
||||
if (secondLineHasParenthese) {
|
||||
hasParenthese = true;
|
||||
blockBodyStartIndex++;
|
||||
}
|
||||
let endIndex = hasParenthese ? GetCloseparentheseEndIndex(inputs, startIndex) : startIndex;
|
||||
if (endIndex != startIndex && firstLineHasParenthese) {
|
||||
inputs[startIndex] = inputs[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3');
|
||||
let newInputs = inputs[startIndex].split("\r\n");
|
||||
if (newInputs.length == 2) {
|
||||
inputs[startIndex] = newInputs[0];
|
||||
inputs.splice(startIndex + 1, 0, newInputs[1]);
|
||||
endIndex++;
|
||||
parentEndIndex++;
|
||||
}
|
||||
}
|
||||
else if (endIndex > startIndex + 1 && secondLineHasParenthese) {
|
||||
inputs[startIndex + 1] = inputs[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1');
|
||||
let newInputs = inputs[startIndex + 1].split("\r\n");
|
||||
if (newInputs.length == 2) {
|
||||
inputs[startIndex + 1] = newInputs[0];
|
||||
inputs.splice(startIndex + 2, 0, newInputs[1]);
|
||||
endIndex++;
|
||||
parentEndIndex++;
|
||||
}
|
||||
}
|
||||
if (firstLineHasParenthese && inputs[startIndex].indexOf("MAP") > 0) {
|
||||
inputs[startIndex] = inputs[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2(');
|
||||
}
|
||||
result.push(new FormattedLine(inputs[startIndex], indent));
|
||||
if (secondLineHasParenthese) {
|
||||
let secondLineIndent = indent;
|
||||
if (endIndex == startIndex + 1) {
|
||||
secondLineIndent++;
|
||||
}
|
||||
result.push(new FormattedLine(inputs[startIndex + 1], secondLineIndent));
|
||||
}
|
||||
let blockBodyEndIndex = endIndex;
|
||||
let i = beautify3(inputs, result, settings, blockBodyStartIndex + 1, indent + 1, endIndex);
|
||||
if (inputs[i].startsWith(")")) {
|
||||
result[i].Indent--;
|
||||
blockBodyEndIndex--;
|
||||
}
|
||||
var alignSettings = settings.SignAlignSettings;
|
||||
if (alignSettings != null) {
|
||||
if (alignSettings.isRegional && !alignSettings.isAll
|
||||
&& alignSettings.keyWords != null
|
||||
&& alignSettings.keyWords.indexOf(mode) >= 0) {
|
||||
blockBodyStartIndex++;
|
||||
AlignSigns(result, blockBodyStartIndex, blockBodyEndIndex, alignSettings.mode, settings.AlignComments);
|
||||
}
|
||||
}
|
||||
return [i, parentEndIndex];
|
||||
}
|
||||
exports.beautifyPortGenericBlock = beautifyPortGenericBlock;
|
||||
function AlignSigns(result, startIndex, endIndex, mode, alignComments) {
|
||||
AlignSign_(result, startIndex, endIndex, ":", mode);
|
||||
AlignSign_(result, startIndex, endIndex, ":=", mode);
|
||||
AlignSign_(result, startIndex, endIndex, "<=", mode);
|
||||
AlignSign_(result, startIndex, endIndex, "=>", mode);
|
||||
if (alignComments) {
|
||||
AlignSign_(result, startIndex, endIndex, "@@comments", mode);
|
||||
}
|
||||
}
|
||||
exports.AlignSigns = AlignSigns;
|
||||
function AlignSign_(result, startIndex, endIndex, symbol, mode) {
|
||||
let maxSymbolIndex = -1;
|
||||
let symbolIndices = {};
|
||||
let startLine = startIndex;
|
||||
let labelAndKeywords = [
|
||||
"([\\w\\s]*:(\\s)*PROCESS)",
|
||||
"([\\w\\s]*:(\\s)*POSTPONED PROCESS)",
|
||||
"([\\w\\s]*:\\s*$)",
|
||||
"([\\w\\s]*:.*\\s+GENERATE)"
|
||||
];
|
||||
let labelAndKeywordsStr = labelAndKeywords.join("|");
|
||||
let labelAndKeywordsRegex = new RegExp("(" + labelAndKeywordsStr + ")([^\\w]|$)");
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
let line = result[i].Line;
|
||||
if (symbol == ":" && line.regexStartsWith(labelAndKeywordsRegex)) {
|
||||
continue;
|
||||
}
|
||||
let regex = new RegExp("([\\s\\w\\\\]|^)" + symbol + "([\\s\\w\\\\]|$)");
|
||||
if (line.regexCount(regex) > 1) {
|
||||
continue;
|
||||
}
|
||||
let colonIndex = line.regexIndexOf(regex);
|
||||
if (colonIndex > 0) {
|
||||
maxSymbolIndex = Math.max(maxSymbolIndex, colonIndex);
|
||||
symbolIndices[i] = colonIndex;
|
||||
}
|
||||
else if ((mode != "local" && !line.startsWith(ILCommentPrefix) && line.length != 0)
|
||||
|| (mode == "local")) {
|
||||
if (startLine < i - 1) // if cannot find the symbol, a block of symbols ends
|
||||
{
|
||||
AlignSign(result, startLine, i - 1, symbol, maxSymbolIndex, symbolIndices);
|
||||
}
|
||||
maxSymbolIndex = -1;
|
||||
symbolIndices = {};
|
||||
startLine = i;
|
||||
}
|
||||
}
|
||||
if (startLine < endIndex) // if cannot find the symbol, a block of symbols ends
|
||||
{
|
||||
AlignSign(result, startLine, endIndex, symbol, maxSymbolIndex, symbolIndices);
|
||||
}
|
||||
}
|
||||
function AlignSign(result, startIndex, endIndex, symbol, maxSymbolIndex = -1, symbolIndices = {}) {
|
||||
if (maxSymbolIndex < 0) {
|
||||
return;
|
||||
}
|
||||
for (let lineIndex in symbolIndices) {
|
||||
let symbolIndex = symbolIndices[lineIndex];
|
||||
if (symbolIndex == maxSymbolIndex) {
|
||||
continue;
|
||||
}
|
||||
let line = result[lineIndex].Line;
|
||||
result[lineIndex].Line = line.substring(0, symbolIndex)
|
||||
+ (Array(maxSymbolIndex - symbolIndex + 1).join(" "))
|
||||
+ line.substring(symbolIndex);
|
||||
}
|
||||
}
|
||||
exports.AlignSign = AlignSign;
|
||||
function beautifyCaseBlock(inputs, result, settings, startIndex, indent) {
|
||||
if (!inputs[startIndex].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) {
|
||||
return startIndex;
|
||||
}
|
||||
result.push(new FormattedLine(inputs[startIndex], indent));
|
||||
let i = beautify3(inputs, result, settings, startIndex + 1, indent + 2);
|
||||
result[i].Indent = indent;
|
||||
return i;
|
||||
}
|
||||
exports.beautifyCaseBlock = beautifyCaseBlock;
|
||||
function getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex) {
|
||||
let endIndex = 0;
|
||||
let openBracketsCount = 0;
|
||||
let closeBracketsCount = 0;
|
||||
for (let i = startIndex; i < inputs.length; i++) {
|
||||
let input = inputs[i];
|
||||
let indexOfSemicolon = input.indexOf(";");
|
||||
let splitIndex = indexOfSemicolon < 0 ? input.length : indexOfSemicolon + 1;
|
||||
let stringBeforeSemicolon = input.substring(0, splitIndex);
|
||||
let stringAfterSemicolon = input.substring(splitIndex);
|
||||
stringAfterSemicolon = stringAfterSemicolon.replace(new RegExp(ILCommentPrefix + "[0-9]+"), "");
|
||||
openBracketsCount += stringBeforeSemicolon.count("(");
|
||||
closeBracketsCount += stringBeforeSemicolon.count(")");
|
||||
if (indexOfSemicolon < 0) {
|
||||
continue;
|
||||
}
|
||||
if (openBracketsCount == closeBracketsCount) {
|
||||
endIndex = i;
|
||||
if (stringAfterSemicolon.trim().length > 0 && settings.NewLineSettings.newLineAfter.indexOf(";") >= 0) {
|
||||
inputs[i] = stringBeforeSemicolon;
|
||||
inputs.splice(i, 0, stringAfterSemicolon);
|
||||
parentEndIndex++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [endIndex, parentEndIndex];
|
||||
}
|
||||
function beautifyComponentBlock(inputs, result, settings, startIndex, parentEndIndex, indent) {
|
||||
let endIndex = startIndex;
|
||||
for (let i = startIndex; i < inputs.length; i++) {
|
||||
if (inputs[i].regexStartsWith(/END(\s|$)/)) {
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.push(new FormattedLine(inputs[startIndex], indent));
|
||||
if (endIndex != startIndex) {
|
||||
let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex);
|
||||
let incremental = actualEndIndex - endIndex;
|
||||
endIndex += incremental;
|
||||
parentEndIndex += incremental;
|
||||
}
|
||||
return [endIndex, parentEndIndex];
|
||||
}
|
||||
exports.beautifyComponentBlock = beautifyComponentBlock;
|
||||
function beautifyPackageIsNewBlock(inputs, result, settings, startIndex, parentEndIndex, indent) {
|
||||
let endIndex = startIndex;
|
||||
for (let i = startIndex; i < inputs.length; i++) {
|
||||
if (inputs[i].regexIndexOf(/;(\s|$)/) >= 0) {
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.push(new FormattedLine(inputs[startIndex], indent));
|
||||
if (endIndex != startIndex) {
|
||||
let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex);
|
||||
let incremental = actualEndIndex - endIndex;
|
||||
endIndex += incremental;
|
||||
parentEndIndex += incremental;
|
||||
}
|
||||
return [endIndex, parentEndIndex];
|
||||
}
|
||||
exports.beautifyPackageIsNewBlock = beautifyPackageIsNewBlock;
|
||||
function beautifySemicolonBlock(inputs, result, settings, startIndex, parentEndIndex, indent) {
|
||||
let endIndex = startIndex;
|
||||
[endIndex, parentEndIndex] = getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex);
|
||||
result.push(new FormattedLine(inputs[startIndex], indent));
|
||||
if (endIndex != startIndex) {
|
||||
let i = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex);
|
||||
}
|
||||
return [endIndex, parentEndIndex];
|
||||
}
|
||||
exports.beautifySemicolonBlock = beautifySemicolonBlock;
|
||||
function beautify3(inputs, result, settings, startIndex, indent, endIndex) {
|
||||
let i;
|
||||
let regexOneLineBlockKeyWords = new RegExp(/(PROCEDURE)[^\w](?!.+[^\w]IS([^\w]|$))/); //match PROCEDURE..; but not PROCEDURE .. IS;
|
||||
let regexFunctionMultiLineBlockKeyWords = new RegExp(/(FUNCTION|IMPURE FUNCTION)[^\w](?=.+[^\w]IS([^\w]|$))/); //match FUNCTION .. IS; but not FUNCTION
|
||||
let blockMidKeyWords = ["BEGIN"];
|
||||
let blockStartsKeyWords = [
|
||||
"IF",
|
||||
"CASE",
|
||||
"ARCHITECTURE",
|
||||
"PROCEDURE",
|
||||
"PACKAGE",
|
||||
"(([\\w\\s]*:)?(\\s)*PROCESS)",
|
||||
"(([\\w\\s]*:)?(\\s)*POSTPONED PROCESS)",
|
||||
"(.*\\s*PROTECTED)",
|
||||
"(COMPONENT)",
|
||||
"(ENTITY(?!.+;))",
|
||||
"FOR",
|
||||
"WHILE",
|
||||
"LOOP",
|
||||
"(.*\\s*GENERATE)",
|
||||
"(CONTEXT[\\w\\s\\\\]+IS)",
|
||||
"(CONFIGURATION(?!.+;))",
|
||||
"BLOCK",
|
||||
"UNITS",
|
||||
"\\w+\\s+\\w+\\s+IS\\s+RECORD"
|
||||
];
|
||||
let blockEndsKeyWords = ["END", ".*\\)\\s*RETURN\\s+[\\w]+;"];
|
||||
let indentedEndsKeyWords = [ILIndentedReturnPrefix + "RETURN\\s+\\w+;"];
|
||||
let blockEndsWithSemicolon = [
|
||||
"(WITH\\s+[\\w\\s\\\\]+SELECT)",
|
||||
"([\\w\\\\]+[\\s]*<=)",
|
||||
"([\\w\\\\]+[\\s]*:=)",
|
||||
"FOR\\s+[\\w\\s,]+:\\s*\\w+\\s+USE",
|
||||
"REPORT"
|
||||
];
|
||||
let newLineAfterKeyWordsStr = blockStartsKeyWords.join("|");
|
||||
let regexBlockMidKeyWords = blockMidKeyWords.convertToRegexBlockWords();
|
||||
let regexBlockStartsKeywords = new RegExp("([\\w]+\\s*:\\s*)?(" + newLineAfterKeyWordsStr + ")([^\\w]|$)");
|
||||
let regexBlockEndsKeyWords = blockEndsKeyWords.convertToRegexBlockWords();
|
||||
let regexBlockIndentedEndsKeyWords = indentedEndsKeyWords.convertToRegexBlockWords();
|
||||
let regexblockEndsWithSemicolon = blockEndsWithSemicolon.convertToRegexBlockWords();
|
||||
let regexMidKeyWhen = "WHEN".convertToRegexBlockWords();
|
||||
let regexMidKeyElse = "ELSE|ELSIF".convertToRegexBlockWords();
|
||||
if (endIndex == null) {
|
||||
endIndex = inputs.length - 1;
|
||||
}
|
||||
for (i = startIndex; i <= endIndex; i++) {
|
||||
if (indent < 0) {
|
||||
indent = 0;
|
||||
}
|
||||
let input = inputs[i].trim();
|
||||
if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) {
|
||||
result.push(new FormattedLine(input, indent));
|
||||
return i;
|
||||
}
|
||||
if (input.regexStartsWith(/COMPONENT\s/)) {
|
||||
let modeCache = Mode;
|
||||
Mode = FormatMode.EndsWithSemicolon;
|
||||
[i, endIndex] = beautifyComponentBlock(inputs, result, settings, i, endIndex, indent);
|
||||
Mode = modeCache;
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/PACKAGE[\s\w]+IS\s+NEW/)) {
|
||||
let modeCache = Mode;
|
||||
Mode = FormatMode.EndsWithSemicolon;
|
||||
[i, endIndex] = beautifyPackageIsNewBlock(inputs, result, settings, i, endIndex, indent);
|
||||
Mode = modeCache;
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/\w+\s*:\s*ENTITY/)) {
|
||||
let modeCache = Mode;
|
||||
Mode = FormatMode.EndsWithSemicolon;
|
||||
[i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent);
|
||||
Mode = modeCache;
|
||||
continue;
|
||||
}
|
||||
if (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexblockEndsWithSemicolon)) {
|
||||
let modeCache = Mode;
|
||||
Mode = FormatMode.EndsWithSemicolon;
|
||||
[i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent);
|
||||
Mode = modeCache;
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) {
|
||||
let modeCache = Mode;
|
||||
Mode = FormatMode.CaseWhen;
|
||||
i = beautifyCaseBlock(inputs, result, settings, i, indent);
|
||||
Mode = modeCache;
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/.*?\:\=\s*\($/)) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, ":=");
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PORT");
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/TYPE\s+\w+\s+IS\s+\(/)) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IS");
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/[\w\s:]*GENERIC([\s]|$)/)) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "GENERIC");
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/[\w\s:]*PROCEDURE[\s\w]+\($/)) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PROCEDURE");
|
||||
if (inputs[i].regexStartsWith(/.*\)[\s]*IS/)) {
|
||||
i = beautify3(inputs, result, settings, i + 1, indent + 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/FUNCTION[^\w]/)
|
||||
&& input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "FUNCTION");
|
||||
if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) {
|
||||
i = beautify3(inputs, result, settings, i + 1, indent + 1);
|
||||
}
|
||||
else {
|
||||
result[i].Indent++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(/IMPURE FUNCTION[^\w]/)
|
||||
&& input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) {
|
||||
[i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IMPURE FUNCTION");
|
||||
if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) {
|
||||
if (inputs[i].regexStartsWith(regexBlockIndentedEndsKeyWords)) {
|
||||
result[i].Indent++;
|
||||
}
|
||||
else {
|
||||
i = beautify3(inputs, result, settings, i + 1, indent + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result[i].Indent++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
result.push(new FormattedLine(input, indent));
|
||||
if (startIndex != 0
|
||||
&& (input.regexStartsWith(regexBlockMidKeyWords)
|
||||
|| (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexMidKeyElse))
|
||||
|| (Mode == FormatMode.CaseWhen && input.regexStartsWith(regexMidKeyWhen)))) {
|
||||
result[i].Indent--;
|
||||
}
|
||||
else if (startIndex != 0
|
||||
&& (input.regexStartsWith(regexBlockEndsKeyWords))) {
|
||||
result[i].Indent--;
|
||||
return i;
|
||||
}
|
||||
if (input.regexStartsWith(regexOneLineBlockKeyWords)) {
|
||||
continue;
|
||||
}
|
||||
if (input.regexStartsWith(regexFunctionMultiLineBlockKeyWords)
|
||||
|| input.regexStartsWith(regexBlockStartsKeywords)) {
|
||||
i = beautify3(inputs, result, settings, i + 1, indent + 1);
|
||||
}
|
||||
}
|
||||
i--;
|
||||
return i;
|
||||
}
|
||||
exports.beautify3 = beautify3;
|
||||
function ReserveSemicolonInKeywords(arr) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].match(/FUNCTION|PROCEDURE/) != null) {
|
||||
arr[i] = arr[i].replace(/;/g, ILSemicolon);
|
||||
}
|
||||
}
|
||||
}
|
||||
function ApplyNoNewLineAfter(arr, noNewLineAfter) {
|
||||
if (noNewLineAfter == null) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
noNewLineAfter.forEach(n => {
|
||||
let regex = new RegExp("(" + n.toUpperCase + ")[ a-z0-9]+[a-z0-9]+");
|
||||
if (arr[i].regexIndexOf(regex) >= 0) {
|
||||
arr[i] += "@@singleline";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.ApplyNoNewLineAfter = ApplyNoNewLineAfter;
|
||||
function RemoveAsserts(arr) {
|
||||
let need_semi = false;
|
||||
let inAssert = false;
|
||||
let n = 0;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let has_semi = arr[i].indexOf(";") >= 0;
|
||||
if (need_semi) {
|
||||
arr[i] = '';
|
||||
}
|
||||
n = arr[i].indexOf("ASSERT ");
|
||||
if (n >= 0) {
|
||||
inAssert = true;
|
||||
arr[i] = '';
|
||||
}
|
||||
if (!has_semi) {
|
||||
if (inAssert) {
|
||||
need_semi = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
need_semi = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.RemoveAsserts = RemoveAsserts;
|
||||
function escapeText(arr, regex, escapedChar) {
|
||||
let quotes = [];
|
||||
let regexEpr = new RegExp(regex, "g");
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let matches = arr[i].match(regexEpr);
|
||||
if (matches != null) {
|
||||
for (var j = 0; j < matches.length; j++) {
|
||||
var match = matches[j];
|
||||
arr[i] = arr[i].replace(match, escapedChar.repeat(match.length));
|
||||
quotes.push(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
return quotes;
|
||||
}
|
||||
function RemoveExtraNewLines(input) {
|
||||
input = input.replace(/(?:\r\n|\r|\n)/g, '\r\n');
|
||||
input = input.replace(/ \r\n/g, '\r\n');
|
||||
input = input.replace(/\r\n\r\n\r\n/g, '\r\n');
|
||||
return input;
|
||||
}
|
35
resources/formatter/vlogFormatter.js
Normal file
35
resources/formatter/vlogFormatter.js
Normal file
File diff suppressed because one or more lines are too long
5
resources/hdlParser/index.d.ts
vendored
5
resources/hdlParser/index.d.ts
vendored
@ -12,10 +12,7 @@ interface Fast {
|
||||
}
|
||||
|
||||
interface All {
|
||||
content: {
|
||||
error: string[]
|
||||
symbols: RawSymbol[]
|
||||
}
|
||||
content: RawSymbol[]
|
||||
languageId: HdlLangID
|
||||
macro: Macro
|
||||
}
|
||||
|
@ -546,7 +546,7 @@
|
||||
},
|
||||
|
||||
"include": {
|
||||
"prefix": "inc",
|
||||
"prefix": "include",
|
||||
"body": [
|
||||
"`include \"$1\""
|
||||
],
|
||||
@ -554,9 +554,9 @@
|
||||
},
|
||||
|
||||
"define": {
|
||||
"prefix": "def",
|
||||
"prefix": "define",
|
||||
"body": [
|
||||
"`def $1 = $2"
|
||||
"`define $1 $2"
|
||||
],
|
||||
"description": "`define var = val"
|
||||
},
|
||||
@ -902,5 +902,23 @@
|
||||
"}",
|
||||
"*/"
|
||||
]
|
||||
},
|
||||
"dumpfile": {
|
||||
"prefix": "$dumpfile",
|
||||
"body": [
|
||||
"\\$dumpfile(\"$1\");"
|
||||
]
|
||||
},
|
||||
"dumpvars": {
|
||||
"prefix": "$dumpvars",
|
||||
"body": [
|
||||
"\\$dumpvars;"
|
||||
]
|
||||
},
|
||||
"finish": {
|
||||
"prefix": "$finish",
|
||||
"body": [
|
||||
"\\$finish;"
|
||||
]
|
||||
}
|
||||
}
|
@ -3,17 +3,19 @@ import * as vscode from 'vscode';
|
||||
import { opeParam, MainOutput, ReportType } from './global';
|
||||
import { hdlParam } from './hdlParser';
|
||||
import { prjManage, registerManagerCommands } from './manager';
|
||||
import { registerFunctionCommands } from './function';
|
||||
import { registerFunctionCommands, registerLsp } from './function';
|
||||
|
||||
async function registerCommand(context: vscode.ExtensionContext) {
|
||||
registerFunctionCommands(context);
|
||||
registerManagerCommands(context);
|
||||
registerLsp(context);
|
||||
}
|
||||
|
||||
async function launch(context: vscode.ExtensionContext) {
|
||||
await prjManage.initialise(context);
|
||||
await registerCommand(context);
|
||||
|
||||
|
||||
MainOutput.report('Digital-IDE has launched, Version: 0.3.0');
|
||||
MainOutput.report('OS: ' + opeParam.os);
|
||||
}
|
||||
|
@ -4,13 +4,18 @@ import * as hdlDoc from './hdlDoc';
|
||||
import * as sim from './sim';
|
||||
import * as treeView from './treeView';
|
||||
|
||||
import * as lspCompletion from './lsp/completion';
|
||||
import * as lspDocSymbol from './lsp/docSymbol';
|
||||
import * as lspDefinition from './lsp/definition';
|
||||
import * as lspHover from './lsp/hover';
|
||||
import * as lspFormatter from '../../resources/formatter';
|
||||
|
||||
function registerDocumentation(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview);
|
||||
hdlDoc.registerFileDocExport(context);
|
||||
hdlDoc.registerProjectDocExport(context);
|
||||
}
|
||||
|
||||
|
||||
function registerSimulation(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('digital-ide.tool.instance', sim.instantiation);
|
||||
vscode.commands.registerCommand('digital-ide.tool.testbench', sim.testbench);
|
||||
@ -42,6 +47,29 @@ function registerTreeView(context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
|
||||
function registerLsp(context: vscode.ExtensionContext) {
|
||||
const vlogSelector: vscode.DocumentSelector = {scheme: 'file', language: 'verilog'};
|
||||
const svlogSelector: vscode.DocumentSelector = {scheme: 'file', language: 'systemverilog'};
|
||||
const vhdlSelector: vscode.DocumentSelector = {scheme: 'file', language: 'vhdl'};
|
||||
|
||||
// formatter
|
||||
vscode.languages.registerDocumentFormattingEditProvider(vlogSelector, lspFormatter.hdlFormatterProvider);
|
||||
vscode.languages.registerDocumentFormattingEditProvider(vhdlSelector, lspFormatter.hdlFormatterProvider);
|
||||
vscode.languages.registerDocumentFormattingEditProvider(svlogSelector, lspFormatter.hdlFormatterProvider);
|
||||
|
||||
// verilog lsp
|
||||
vscode.languages.registerDocumentSymbolProvider(vlogSelector, lspDocSymbol.vlogDocSymbolProvider);
|
||||
vscode.languages.registerDefinitionProvider(vlogSelector, lspDefinition.vlogDefinitionProvider);
|
||||
vscode.languages.registerHoverProvider(vlogSelector, lspHover.vlogHoverProvider);
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogIncludeCompletionProvider, '/', '"');
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`');
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
|
||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider);
|
||||
// vhdl lsp
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
registerFunctionCommands
|
||||
registerFunctionCommands,
|
||||
registerLsp
|
||||
};
|
8
src/function/lsp/completion/index.ts
Normal file
8
src/function/lsp/completion/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { vlogCompletionProvider, vlogIncludeCompletionProvider, vlogMacroCompletionProvider, vlogPositionPortProvider } from './vlog';
|
||||
|
||||
export {
|
||||
vlogCompletionProvider,
|
||||
vlogIncludeCompletionProvider,
|
||||
vlogMacroCompletionProvider,
|
||||
vlogPositionPortProvider
|
||||
};
|
303
src/function/lsp/completion/vlog.ts
Normal file
303
src/function/lsp/completion/vlog.ts
Normal file
@ -0,0 +1,303 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as util from '../util';
|
||||
import { hdlFile, hdlPath } from '../../../hdlFs';
|
||||
import { hdlParam, HdlSymbol } from '../../../hdlParser';
|
||||
import { AbsPath, MainOutput, ReportType } from '../../../global';
|
||||
import { Define, Include, RawSymbol } from '../../../hdlParser/common';
|
||||
import { HdlInstance, HdlModule } from '../../../hdlParser/core';
|
||||
import { vlogKeyword } from '../util/keyword';
|
||||
|
||||
class VlogIncludeCompletionProvider implements vscode.CompletionItemProvider {
|
||||
public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> {
|
||||
try {
|
||||
const items = this.provideIncludeFiles(document, position);
|
||||
return items;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
private provideIncludeFiles(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
|
||||
if (position.character === 0) {
|
||||
return [];
|
||||
}
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
const lineText = document.lineAt(position).text;
|
||||
|
||||
let firstQIndex = lineText.lastIndexOf('"', position.character - 1);
|
||||
let lastQIndex = lineText.indexOf('"', position.character);
|
||||
|
||||
if (firstQIndex !== -1 && lastQIndex !== -1) {
|
||||
const currentPath = lineText.substring(firstQIndex + 1, lastQIndex);
|
||||
const folderName = currentPath.length === 0 ? '.' : currentPath;
|
||||
const folderAbsPath = hdlPath.rel2abs(filePath, folderName);
|
||||
return this.filterIncludeFiles(folderAbsPath, filePath);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private filterIncludeFiles(folderPath: AbsPath, currentPath: AbsPath) {
|
||||
if (fs.existsSync(folderPath)) {
|
||||
const suggestFiles = [];
|
||||
for (const fileName of fs.readdirSync(folderPath)) {
|
||||
const filePath = hdlPath.join(folderPath, fileName);
|
||||
if (filePath === currentPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const stat = fs.statSync(filePath);
|
||||
const clItem = new vscode.CompletionItem(fileName);
|
||||
if (stat.isDirectory()) {
|
||||
clItem.kind = vscode.CompletionItemKind.Folder;
|
||||
} else if (stat.isFile()) {
|
||||
clItem.kind = vscode.CompletionItemKind.File;
|
||||
}
|
||||
suggestFiles.push(clItem);
|
||||
}
|
||||
return suggestFiles;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class VlogMacroCompletionProvider 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> {
|
||||
try {
|
||||
const targetWordRange = document.getWordRangeAtPosition(position, /[`_0-9a-zA-Z]+/);
|
||||
const targetWord = document.getText(targetWordRange);
|
||||
const filePath = document.fileName;
|
||||
const symbolResult = await HdlSymbol.all(filePath);
|
||||
if (!symbolResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = this.provideMacros(targetWord, symbolResult.macro.defines);
|
||||
return items;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
private provideMacros(targetWord: string, defines: Define[]): vscode.CompletionItem[] {
|
||||
const suggestMacros: vscode.CompletionItem[] = [];
|
||||
if (!defines || defines.length === 0) {
|
||||
return suggestMacros;
|
||||
}
|
||||
for (const define of defines) {
|
||||
const name = '`' + define.name;
|
||||
const clItem = new vscode.CompletionItem(name, vscode.CompletionItemKind.Constant);
|
||||
clItem.detail = 'macro ' + define.replacement;
|
||||
clItem.insertText = targetWord.startsWith('`') ? define.name : name;
|
||||
suggestMacros.push(clItem);
|
||||
}
|
||||
return suggestMacros;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VlogPositionPortProvider 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> {
|
||||
try {
|
||||
const suggestPositionPorts: vscode.CompletionItem[] = [];
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
const symbolResult = await HdlSymbol.all(filePath);
|
||||
if (!symbolResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scopeSymbols = util.filterSymbolScope(position, symbolResult.content);
|
||||
if (!scopeSymbols ||
|
||||
!scopeSymbols.module ||
|
||||
!scopeSymbols.symbols ||
|
||||
!hdlParam.hasHdlModule(filePath, scopeSymbols.module.name)) {
|
||||
return suggestPositionPorts;
|
||||
}
|
||||
|
||||
const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name);
|
||||
if (!currentModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentInst = util.filterInstanceByPosition(position, scopeSymbols.symbols, currentModule);
|
||||
// find instance and instMod is not null (solve the dependence already)
|
||||
|
||||
if (currentInst && currentInst.module && currentInst.instModPath) {
|
||||
const portsparams = this.providePositionPorts(position, currentInst);
|
||||
suggestPositionPorts.push(...portsparams);
|
||||
}
|
||||
|
||||
return suggestPositionPorts;
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
private providePositionPorts(position: vscode.Position, currentInst: HdlInstance): vscode.CompletionItem[] {
|
||||
if (!currentInst.module) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const params = currentInst.instparams;
|
||||
const ports = currentInst.instports;
|
||||
|
||||
if (params &&
|
||||
util.positionAfterEqual(position, params.start) &&
|
||||
util.positionAfterEqual(params.end, position)) {
|
||||
|
||||
return currentInst.module.params.map(param => {
|
||||
const clItem = new vscode.CompletionItem(param.name, vscode.CompletionItemKind.Constant);
|
||||
clItem.detail = 'param';
|
||||
return clItem;
|
||||
});
|
||||
}
|
||||
if (ports &&
|
||||
util.positionAfterEqual(position, ports.start) &&
|
||||
util.positionAfterEqual(ports.end, position)) {
|
||||
|
||||
return currentInst.module.ports.map(port => {
|
||||
const clItem = new vscode.CompletionItem(port.name, vscode.CompletionItemKind.Interface);
|
||||
clItem.detail = 'port';
|
||||
return clItem;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class VlogCompletionProvider 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> {
|
||||
try {
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
|
||||
// 1. provide keyword
|
||||
const completions = this.getKeyWordItem();
|
||||
|
||||
const symbolResult = await HdlSymbol.all(filePath);
|
||||
if (!symbolResult) {
|
||||
return completions;
|
||||
}
|
||||
|
||||
// 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(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 getKeyWordItem(): vscode.CompletionItem[] {
|
||||
const vlogKeywordItem = [];
|
||||
for (const keyword of vlogKeyword.keys()) {
|
||||
const clItem = this.makekeywordCompletionItem(keyword);
|
||||
vlogKeywordItem.push(clItem);
|
||||
}
|
||||
|
||||
return vlogKeywordItem;
|
||||
}
|
||||
|
||||
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$1end"); break;
|
||||
default: break;
|
||||
}
|
||||
return clItem;
|
||||
}
|
||||
|
||||
private async provideModules(filePath: AbsPath, includes: Include[]): Promise<vscode.CompletionItem[]> {
|
||||
const suggestModules: vscode.CompletionItem[] = [];
|
||||
|
||||
// TODO : add `include xxx automatically
|
||||
for (const module of hdlParam.getAllHdlModules()) {
|
||||
const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class);
|
||||
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.detail = symbol.type;
|
||||
suggestNets.push(clItem);
|
||||
}
|
||||
}
|
||||
return suggestNets;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const vlogCompletionProvider = new VlogCompletionProvider();
|
||||
const vlogIncludeCompletionProvider = new VlogIncludeCompletionProvider();
|
||||
const vlogMacroCompletionProvider = new VlogMacroCompletionProvider();
|
||||
const vlogPositionPortProvider = new VlogPositionPortProvider();
|
||||
|
||||
export {
|
||||
vlogCompletionProvider,
|
||||
vlogIncludeCompletionProvider,
|
||||
vlogMacroCompletionProvider,
|
||||
vlogPositionPortProvider
|
||||
};
|
5
src/function/lsp/definition/index.ts
Normal file
5
src/function/lsp/definition/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { vlogDefinitionProvider } from './vlog';
|
||||
|
||||
export {
|
||||
vlogDefinitionProvider
|
||||
};
|
161
src/function/lsp/definition/vlog.ts
Normal file
161
src/function/lsp/definition/vlog.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as vsctm from 'vscode-textmate';
|
||||
|
||||
import { hdlPath } from '../../../hdlFs';
|
||||
import { hdlParam, HdlSymbol } from '../../../hdlParser';
|
||||
import { All } from '../../../../resources/hdlParser';
|
||||
import { vlogKeyword } from '../util/keyword';
|
||||
import * as util from '../util';
|
||||
import { MainOutput, ReportType } from '../../../global';
|
||||
|
||||
|
||||
class VlogDefinitionProvider implements vscode.DefinitionProvider {
|
||||
|
||||
public async provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Location | vscode.LocationLink[] | null> {
|
||||
// get current words
|
||||
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9A-Za-z]+/);
|
||||
if (!wordRange) {
|
||||
return null;
|
||||
}
|
||||
const targetWord = document.getText(wordRange);
|
||||
|
||||
// check if need skip
|
||||
if (this.needSkip(document, position, targetWord)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filePath = document.fileName;
|
||||
const vlogAll = await HdlSymbol.all(filePath);
|
||||
if (!vlogAll) {
|
||||
return null;
|
||||
} else {
|
||||
const location = await this.makeDefinition(document, position, vlogAll, targetWord, wordRange);
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
private needSkip(document: vscode.TextDocument, position: vscode.Position, targetWord: string): boolean {
|
||||
// check keyword
|
||||
if (vlogKeyword.isKeyword(targetWord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: check comment
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async makeDefinition(document: vscode.TextDocument, position: vscode.Position, all: All, targetWord: string, targetWordRange: vscode.Range): Promise<vscode.Location | vscode.LocationLink[] | null> {
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
const lineText = document.lineAt(position).text;
|
||||
|
||||
// match `include
|
||||
const includeResult = util.matchInclude(document, position, all.macro.includes);
|
||||
if (includeResult) {
|
||||
const absPath = hdlPath.rel2abs(filePath, includeResult.name);
|
||||
const targetFile = vscode.Uri.file(absPath);
|
||||
const targetPosition = new vscode.Position(0, 0);
|
||||
const targetRange = new vscode.Range(targetPosition, targetPosition);
|
||||
const originSelectionRange = document.getWordRangeAtPosition(position, /[\."_0-9a-zA-Z]+/);
|
||||
const link: vscode.LocationLink = { targetUri: targetFile, targetRange, originSelectionRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
|
||||
// match macro
|
||||
const macroResult = util.matchDefineMacro(position, targetWord, all.macro.defines);
|
||||
if (macroResult) {
|
||||
const targetRange = util.transformRange(macroResult.range, -1, -1);
|
||||
const link: vscode.LocationLink = { targetUri: document.uri, targetRange: targetRange, originSelectionRange: targetWordRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
// locate at one module
|
||||
const scopeSymbols = util.filterSymbolScope(position, all.content);
|
||||
if (!scopeSymbols || !scopeSymbols.module) {
|
||||
return null;
|
||||
}
|
||||
const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name);
|
||||
if (!currentModule) {
|
||||
MainOutput.report('Fail to get HdlModule ' + filePath + ' ' + scopeSymbols.module.name, ReportType.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
// match instance
|
||||
const instResult = util.matchInstance(targetWord, currentModule);
|
||||
if (instResult) {
|
||||
const instModule = instResult.module;
|
||||
if (!instModule || !instResult.instModPath) {
|
||||
return null;
|
||||
}
|
||||
const targetFile = vscode.Uri.file(instResult.instModPath);
|
||||
const targetRange = util.transformRange(instModule.range, -1, 0, 1);
|
||||
const link: vscode.LocationLink = { targetUri: targetFile, targetRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
// match port or param definition (position input)
|
||||
if (util.isPositionInput(lineText, position.character)) {
|
||||
const currentInstResult = util.filterInstanceByPosition(position, scopeSymbols.symbols, currentModule);
|
||||
if (!currentInstResult || !currentInstResult.instModPath) {
|
||||
return null;
|
||||
}
|
||||
const instParamPromise = util.getInstParamByPosition(currentInstResult, position, targetWord);
|
||||
const instPortPromise = util.getInstPortByPosition(currentInstResult, position, targetWord);
|
||||
|
||||
const instParam = await instParamPromise;
|
||||
const instPort = await instPortPromise;
|
||||
const instModPathUri = vscode.Uri.file(currentInstResult.instModPath);
|
||||
|
||||
if (instParam) {
|
||||
const targetRange = util.transformRange(instParam.range, -1, 0);
|
||||
const link: vscode.LocationLink = { targetUri: instModPathUri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
if (instPort) {
|
||||
const targetRange = util.transformRange(instPort.range, -1, 0);
|
||||
const link: vscode.LocationLink = { targetUri: instModPathUri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// match params
|
||||
const paramResult = util.matchParams(targetWord, currentModule);
|
||||
|
||||
if (paramResult) {
|
||||
const targetRange = util.transformRange(paramResult.range, -1, 0);
|
||||
const link: vscode.LocationLink = { targetUri: document.uri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
// match ports
|
||||
const portResult = util.matchPorts(targetWord, currentModule);
|
||||
|
||||
if (portResult) {
|
||||
const targetRange = util.transformRange(portResult.range, -1, 0);
|
||||
const link: vscode.LocationLink = { targetUri: document.uri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
// match others
|
||||
const normalResult = util.matchNormalSymbol(targetWord, scopeSymbols.symbols);
|
||||
if (normalResult) {
|
||||
const targetRange = util.transformRange(normalResult.range, -1, 0);
|
||||
|
||||
console.log(targetRange, normalResult);
|
||||
|
||||
const link: vscode.LocationLink = { targetUri: document.uri, targetRange };
|
||||
return [link];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const vlogDefinitionProvider = new VlogDefinitionProvider();
|
||||
|
||||
export {
|
||||
vlogDefinitionProvider
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
import { vlogDocSymbolProvider } from './vlog';
|
||||
|
||||
|
||||
export {
|
||||
vlogDocSymbolProvider
|
||||
};
|
@ -1,37 +1,36 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { AllowNull } from '../../../global';
|
||||
import { HdlSymbol } from '../../../hdlParser';
|
||||
import { RawSymbol, makeVscodePosition, Range } from '../../../hdlParser/common';
|
||||
|
||||
import { positionAfterEqual } from '../util';
|
||||
|
||||
interface DocSymbolContainer {
|
||||
docSymbol: AllowNull<vscode.DocumentSymbol>,
|
||||
range: AllowNull<Range>
|
||||
};
|
||||
|
||||
class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
public async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentSymbol[]> {
|
||||
const path = document.fileName;
|
||||
const vlogAll = await HdlSymbol.all(path);
|
||||
|
||||
public provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.SymbolInformation[] | vscode.DocumentSymbol[]> {
|
||||
const code = document.getText();
|
||||
const symbolResult = HdlSymbol.all(code);
|
||||
const symbols = symbolResult.symbols;
|
||||
|
||||
if (!symbols) {
|
||||
if (!vlogAll || !vlogAll.content) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const symbolInfos = this.makeSymbolInfos(document, symbols);
|
||||
} else {
|
||||
const symbols = vlogAll.content;
|
||||
const symbolInfos = this.makeDocumentSymbols(document, symbols);
|
||||
return symbolInfos;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {vscode.TextDocument} document
|
||||
* @param {Array<SymbolResult>} symbols
|
||||
* @returns {Array<vscode.DocumentSymbol>}
|
||||
*/
|
||||
makeSymbolInfos(document: vscode.TextDocument, symbols: SymbolResult[]) {
|
||||
let docSymbols = [];
|
||||
|
||||
private makeDocumentSymbols(document: vscode.TextDocument, symbols: RawSymbol[]): vscode.DocumentSymbol[] {
|
||||
const docSymbols = [];
|
||||
const visitedSymbols = new Set();
|
||||
const moduleSymbols = symbols.filter(symbol => {
|
||||
if (symbol.type == 'module') {
|
||||
if (symbol.type === 'module') {
|
||||
visitedSymbols.add(symbol);
|
||||
return true;
|
||||
}
|
||||
@ -41,38 +40,43 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
for (const moduleSymbol of moduleSymbols) {
|
||||
const moduleName = moduleSymbol.name;
|
||||
const moduleKind = this.getSymbolKind(moduleSymbol.type);
|
||||
const moduleRange = new vscode.Range(moduleSymbol.start, moduleSymbol.end);
|
||||
const moduleStart = new vscode.Position(moduleSymbol.range.start.line - 1, moduleSymbol.range.start.character);
|
||||
const moduleEnd = new vscode.Position(moduleSymbol.range.end.line - 1, moduleSymbol.range.start.character);
|
||||
const moduleRange = new vscode.Range(moduleStart, moduleEnd);
|
||||
const moduleDocSymbol = new vscode.DocumentSymbol(moduleName,
|
||||
moduleName,
|
||||
moduleKind,
|
||||
moduleRange,
|
||||
moduleRange);
|
||||
docSymbols.push(moduleDocSymbol);
|
||||
let paramContainer = {
|
||||
const paramContainer: DocSymbolContainer = {
|
||||
docSymbol: null,
|
||||
range: null
|
||||
};
|
||||
let portContainer = {
|
||||
const portContainer: DocSymbolContainer = {
|
||||
docSymbol: null,
|
||||
range: null
|
||||
};
|
||||
const portTypes = ['input', 'inout', 'output'];
|
||||
|
||||
// make others in module inner
|
||||
for (const symbol of symbols) {
|
||||
if (visitedSymbols.has(symbol)) {
|
||||
continue;
|
||||
}
|
||||
if (!(positionAfterEqual(symbol.start, moduleSymbol.start) &&
|
||||
positionAfterEqual(moduleSymbol.end, symbol.end))) {
|
||||
if (!(positionAfterEqual(symbol.range.start, moduleSymbol.range.start) &&
|
||||
positionAfterEqual(moduleSymbol.range.end, symbol.range.end))) {
|
||||
continue;
|
||||
}
|
||||
if (!symbol.name) {
|
||||
symbol.name = '???';
|
||||
}
|
||||
visitedSymbols.add(symbol);
|
||||
const symbolRange = new vscode.Range(symbol.start, symbol.end);
|
||||
const symbolStart = new vscode.Position(symbol.range.start.line - 1, symbol.range.start.character);
|
||||
const symbolEnd = new vscode.Position(symbol.range.end.line - 1, symbol.range.end.character);
|
||||
const symbolRange = new vscode.Range(symbolStart, symbolEnd);
|
||||
|
||||
if (symbol.type == 'parameter') {
|
||||
if (symbol.type === 'parameter') {
|
||||
if (!paramContainer.range) {
|
||||
paramContainer.range = symbolRange;
|
||||
paramContainer.docSymbol = new vscode.DocumentSymbol('param',
|
||||
@ -87,9 +91,9 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
vscode.SymbolKind.Constant,
|
||||
symbolRange,
|
||||
symbolRange);
|
||||
paramContainer.docSymbol.children.push(paramDocSymbol);
|
||||
paramContainer.docSymbol?.children.push(paramDocSymbol);
|
||||
|
||||
} else if (['input', 'inout', 'output'].includes(symbol.type)) {
|
||||
} else if (portTypes.includes(symbol.type)) {
|
||||
if (!portContainer.range) {
|
||||
portContainer.range = symbolRange;
|
||||
portContainer.docSymbol = new vscode.DocumentSymbol('port',
|
||||
@ -105,7 +109,7 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
vscode.SymbolKind.Interface,
|
||||
symbolRange,
|
||||
symbolRange);
|
||||
portContainer.docSymbol.children.push(portDocSymbol);
|
||||
portContainer.docSymbol?.children.push(portDocSymbol);
|
||||
} else {
|
||||
const symbolKind = this.getSymbolKind(symbol.type);
|
||||
const symbolDocSymbol = new vscode.DocumentSymbol(symbol.name,
|
||||
@ -122,8 +126,8 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
}
|
||||
|
||||
|
||||
getSymbolKind(name) {
|
||||
if (name.indexOf('[') != -1) {
|
||||
getSymbolKind(name: string): vscode.SymbolKind {
|
||||
if (name.indexOf('[') !== -1) {
|
||||
return vscode.SymbolKind.Array;
|
||||
}
|
||||
switch (name) {
|
||||
@ -176,12 +180,11 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
case 'bit': return vscode.SymbolKind.Boolean;
|
||||
default: return vscode.SymbolKind.Event;
|
||||
}
|
||||
/* Unused/Free SymbolKind icons
|
||||
return SymbolKind.Number;
|
||||
return SymbolKind.Enum;
|
||||
return SymbolKind.EnumMember;
|
||||
return SymbolKind.Operator;
|
||||
return SymbolKind.Array;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
const vlogDocSymbolProvider = new VlogDocSymbolProvider();
|
||||
|
||||
export {
|
||||
vlogDocSymbolProvider
|
||||
};
|
5
src/function/lsp/hover/index.ts
Normal file
5
src/function/lsp/hover/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { vlogHoverProvider } from './vlog';
|
||||
|
||||
export {
|
||||
vlogHoverProvider
|
||||
};
|
193
src/function/lsp/hover/vlog.ts
Normal file
193
src/function/lsp/hover/vlog.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { hdlDir, hdlPath } from '../../../hdlFs';
|
||||
import { hdlParam, HdlSymbol } from '../../../hdlParser';
|
||||
import { All } from '../../../../resources/hdlParser';
|
||||
import { vlogKeyword } from '../util/keyword';
|
||||
import * as util from '../util';
|
||||
import { MainOutput, ReportType } from '../../../global';
|
||||
import { HdlLangID } from '../../../global/enum';
|
||||
|
||||
|
||||
class VlogHoverProvider implements vscode.HoverProvider {
|
||||
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | null> {
|
||||
// get current words
|
||||
const wordRange = document.getWordRangeAtPosition(position, /[`_0-9A-Za-z]+/);
|
||||
if (!wordRange) {
|
||||
return null;
|
||||
}
|
||||
const targetWord = document.getText(wordRange);
|
||||
|
||||
// check if need skip
|
||||
if (this.needSkip(document, position, targetWord)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filePath = document.fileName;
|
||||
const vlogAll = await HdlSymbol.all(filePath);
|
||||
if (!vlogAll) {
|
||||
return null;
|
||||
} else {
|
||||
const hover = await this.makeHover(document, position, vlogAll, targetWord, wordRange);
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
|
||||
private needSkip(document: vscode.TextDocument, position: vscode.Position, targetWord: string): boolean {
|
||||
// check keyword
|
||||
if (vlogKeyword.isKeyword(targetWord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: check comment
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async makeHover(document: vscode.TextDocument, position: vscode.Position, all: All, targetWord: string, targetWordRange: vscode.Range): Promise<vscode.Hover | null> {
|
||||
const lineText = document.lineAt(position).text;
|
||||
const filePath = hdlPath.toSlash(document.fileName);
|
||||
|
||||
// total content rendered on the hover box
|
||||
const content = new vscode.MarkdownString('', true);
|
||||
|
||||
// match `include
|
||||
const includeResult = util.matchInclude(document, position, all.macro.includes);
|
||||
if (includeResult) {
|
||||
const absPath = hdlPath.rel2abs(filePath, includeResult.name);
|
||||
content.appendCodeblock(`"${absPath}"`, HdlLangID.Verilog);
|
||||
const targetRange = document.getWordRangeAtPosition(position, /[1-9a-zA-Z_\.]+/);
|
||||
return new vscode.Hover(content, targetRange);
|
||||
} else if (lineText.trim().startsWith('`include')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// match macro
|
||||
const macroResult = util.matchDefineMacro(position, targetWord, all.macro.defines);
|
||||
if (macroResult) {
|
||||
const name = macroResult.name;
|
||||
const value = macroResult.value;
|
||||
content.appendCodeblock(`\`define ${name} ${value}`, HdlLangID.Verilog);
|
||||
return new vscode.Hover(content, targetWordRange);
|
||||
}
|
||||
|
||||
// locate at one module
|
||||
const scopeSymbols = util.filterSymbolScope(position, all.content);
|
||||
if (!scopeSymbols || !scopeSymbols.module || !hdlParam.hasHdlModule(filePath, scopeSymbols.module.name)) {
|
||||
return null;
|
||||
}
|
||||
const currentModule = hdlParam.getHdlModule(filePath, scopeSymbols.module.name);
|
||||
if (!currentModule) {
|
||||
MainOutput.report('Fail to get HdlModule ' + filePath + ' ' + scopeSymbols.module.name, ReportType.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
// match instance
|
||||
const instResult = util.matchInstance(targetWord, currentModule);
|
||||
if (instResult) {
|
||||
const instModule = instResult.module;
|
||||
if (!instModule || !instResult.instModPath) {
|
||||
content.appendMarkdown('cannot find the definition of the module');
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
await util.makeVlogHoverContent(content, instModule);
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
|
||||
|
||||
// match port or param definition (position input)
|
||||
if (util.isPositionInput(lineText, position.character)) {
|
||||
const currentInstResult = util.filterInstanceByPosition(position, scopeSymbols.symbols, currentModule);
|
||||
if (!currentInstResult || !currentInstResult.instModPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const instParamPromise = util.getInstParamByPosition(currentInstResult, position, targetWord);
|
||||
const instPortPromise = util.getInstPortByPosition(currentInstResult, position, targetWord);
|
||||
|
||||
const instParam = await instParamPromise;
|
||||
const instPort = await instPortPromise;
|
||||
|
||||
if (instParam) {
|
||||
const paramComment = await util.searchCommentAround(currentInstResult.instModPath, instParam.range);
|
||||
const paramDesc = util.makeParamDesc(instParam);
|
||||
content.appendCodeblock(paramDesc, HdlLangID.Verilog);
|
||||
if (paramComment) {
|
||||
content.appendCodeblock(paramComment, HdlLangID.Verilog);
|
||||
}
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
if (instPort) {
|
||||
const portComment = await util.searchCommentAround(currentInstResult.instModPath, instPort.range);
|
||||
const portDesc = util.makePortDesc(instPort);
|
||||
content.appendCodeblock(portDesc, HdlLangID.Verilog);
|
||||
if (portComment) {
|
||||
content.appendCodeblock(portComment, HdlLangID.Verilog);
|
||||
}
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// match params
|
||||
const paramResult = util.matchParams(targetWord, currentModule);
|
||||
if (paramResult) {
|
||||
const paramComment = await util.searchCommentAround(filePath, paramResult.range);
|
||||
const paramDesc = util.makeParamDesc(paramResult);
|
||||
content.appendCodeblock(paramDesc, HdlLangID.Verilog);
|
||||
if (paramComment) {
|
||||
content.appendCodeblock(paramComment, HdlLangID.Verilog);
|
||||
}
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
|
||||
// match ports
|
||||
const portResult = util.matchPorts(targetWord, currentModule);
|
||||
if (portResult) {
|
||||
const portComment = await util.searchCommentAround(filePath, portResult.range);
|
||||
const portDesc = util.makePortDesc(portResult);
|
||||
content.appendCodeblock(portDesc, HdlLangID.Verilog);
|
||||
if (portComment) {
|
||||
content.appendCodeblock(portComment, HdlLangID.Verilog);
|
||||
}
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
|
||||
// match others
|
||||
const normalResult = util.matchNormalSymbol(targetWord, scopeSymbols.symbols);
|
||||
if (normalResult) {
|
||||
const normalComment = await util.searchCommentAround(filePath, normalResult.range);
|
||||
const normalDesc = util.makeNormalDesc(normalResult);
|
||||
|
||||
console.log(normalResult);
|
||||
|
||||
|
||||
content.appendCodeblock(normalDesc, HdlLangID.Verilog);
|
||||
if (normalComment) {
|
||||
content.appendCodeblock(normalComment, HdlLangID.Verilog);
|
||||
}
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
|
||||
|
||||
// feature 1. number signed and unsigned number display
|
||||
const numberResult = util.transferVlogNumber(lineText, position.character);
|
||||
if (numberResult) {
|
||||
const bits = targetWord.length - 1;
|
||||
content.appendCodeblock(bits + "'" + targetWord, HdlLangID.Verilog);
|
||||
content.appendMarkdown("`unsigned` " + numberResult.unsigned);
|
||||
content.appendText('\n');
|
||||
content.appendMarkdown("`signed` " + numberResult.signed);
|
||||
}
|
||||
|
||||
return new vscode.Hover(content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const vlogHoverProvider = new VlogHoverProvider();
|
||||
|
||||
export {
|
||||
vlogHoverProvider
|
||||
};
|
@ -1,203 +0,0 @@
|
||||
const vscode = require('vscode');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const HDLPath = require('../../../HDLfilesys/operation/path');
|
||||
const { HDLParam, Module, SymbolResult, Instance } = require('../../../HDLparser');
|
||||
const { positionAfterEqual } = require('./index');
|
||||
|
||||
/**
|
||||
* @param {string} folderPath
|
||||
* @param {string} currentPath
|
||||
* @returns {Array<vscode.CompletionItem>}
|
||||
*/
|
||||
function filterIncludeFiles(folderPath, currentPath) {
|
||||
if (fs.existsSync(folderPath)) {
|
||||
const suggestFiles = [];
|
||||
for (const fileName of fs.readdirSync(folderPath)) {
|
||||
const filePath = HDLPath.join(folderPath, fileName);
|
||||
if (filePath === currentPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const stat = fs.statSync(filePath);
|
||||
const clItem = new vscode.CompletionItem(fileName);
|
||||
if (stat.isDirectory()) {
|
||||
clItem.kind = vscode.CompletionItemKind.Folder;
|
||||
} else if (stat.isFile()) {
|
||||
clItem.kind = vscode.CompletionItemKind.File;
|
||||
}
|
||||
suggestFiles.push(clItem);
|
||||
}
|
||||
return suggestFiles;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {vscode.TextDocument} document
|
||||
* @param {vscode.Position} position
|
||||
* @returns {Array<vscode.CompletionItem>}
|
||||
*/
|
||||
function provideIncludeFiles(document, position) {
|
||||
if (position.character == 0) {
|
||||
return [];
|
||||
}
|
||||
const filePath = HDLPath.toSlash(document.fileName);
|
||||
const lineText = document.lineAt(position).text;
|
||||
|
||||
let firstQIndex = lineText.lastIndexOf('"', position.character - 1);
|
||||
let lastQIndex = lineText.indexOf('"', position.character);
|
||||
|
||||
if (firstQIndex != -1 && lastQIndex != -1) {
|
||||
const currentPath = lineText.substring(firstQIndex + 1, lastQIndex);
|
||||
const folderName = currentPath.length == 0 ? '.' : currentPath;
|
||||
const folderAbsPath = HDLPath.rel2abs(filePath, folderName);
|
||||
return filterIncludeFiles(folderAbsPath, filePath);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} singleWord
|
||||
* @param {object} defines
|
||||
* @returns {Promise<Array<vscode.CompletionItem>>}
|
||||
*/
|
||||
function provideMacros(singleWord, defines) {
|
||||
const suggestMacros = [];
|
||||
if (!defines) {
|
||||
return suggestMacros;
|
||||
}
|
||||
for (const macro of Object.keys(defines)) {
|
||||
const value = defines[macro].value;
|
||||
const name = '`' + macro;
|
||||
const clItem = new vscode.CompletionItem('`' + macro, vscode.CompletionItemKind.Constant)
|
||||
clItem.detail = 'macro ' + value;
|
||||
if (singleWord[0] == '`') {
|
||||
clItem.insertText = macro;
|
||||
} else {
|
||||
clItem.insertText = name;
|
||||
}
|
||||
|
||||
suggestMacros.push(clItem);
|
||||
}
|
||||
return suggestMacros;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {vscode.Position} position cursor position
|
||||
* @param {Instance} currentInst
|
||||
* @returns {Promise<Array<vscode.CompletionItem>>}
|
||||
*/
|
||||
function providePositionPorts(position, currentInst) {
|
||||
const params = currentInst.instparams;
|
||||
const ports = currentInst.instports;
|
||||
console.log(position);
|
||||
console.log(params);
|
||||
console.log(ports);
|
||||
|
||||
if (params &&
|
||||
positionAfterEqual(position, params.start) &&
|
||||
positionAfterEqual(params.end, position)) {
|
||||
|
||||
return currentInst.module.params.map(param => {
|
||||
const clItem = new vscode.CompletionItem(param.name, vscode.CompletionItemKind.Constant);
|
||||
clItem.detail = 'param';
|
||||
return clItem;
|
||||
})
|
||||
}
|
||||
if (ports &&
|
||||
positionAfterEqual(position, ports.start) &&
|
||||
positionAfterEqual(ports.end, position)) {
|
||||
|
||||
return currentInst.module.ports.map(port => {
|
||||
const clItem = new vscode.CompletionItem(port.name, vscode.CompletionItemKind.Interface);
|
||||
clItem.detail = 'port';
|
||||
return clItem;
|
||||
})
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description provide module of the current module and include module
|
||||
* @param {string} filePath
|
||||
* @param {object} includes {path: range}
|
||||
* @returns {Promise<Array<vscode.CompletionItem>>}
|
||||
*/
|
||||
async function provideModules(filePath, includes) {
|
||||
// support include of all the module
|
||||
// use command property to auto add include path
|
||||
const suggestModules = [];
|
||||
|
||||
if (!includes) {
|
||||
return suggestModules;
|
||||
}
|
||||
|
||||
for (const module of HDLParam.getAllModules()) {
|
||||
const clItem = new vscode.CompletionItem(module.name, vscode.CompletionItemKind.Class);
|
||||
clItem.detail = 'module';
|
||||
suggestModules.push(clItem);
|
||||
}
|
||||
|
||||
return suggestModules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Module} module
|
||||
* @returns {Promise<Array<vscode.CompletionItem>>}
|
||||
*/
|
||||
async function provideParamsPorts(module) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array<SymbolResult>} symbols
|
||||
* @returns {Promise<Array<vscode.CompletionItem>>}
|
||||
*/
|
||||
async function provideNets(symbols) {
|
||||
if (!symbols) {
|
||||
return [];
|
||||
}
|
||||
const suggestNets = [];
|
||||
for (const symbol of symbols) {
|
||||
if (symbol.type == 'net') {
|
||||
const clItem = new vscode.CompletionItem(symbol.name, vscode.CompletionItemKind.Variable);
|
||||
clItem.detail = 'net';
|
||||
suggestNets.push(clItem);
|
||||
}
|
||||
}
|
||||
return suggestNets;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
provideIncludeFiles,
|
||||
provideMacros,
|
||||
providePositionPorts,
|
||||
provideModules,
|
||||
provideParamsPorts,
|
||||
provideNets
|
||||
};
|
@ -126,14 +126,8 @@ function bin2float(bin: string, exp: number, fra: number): number | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
async function getFullSymbolInfo(document: vscode.TextDocument, range: Range, nonblank: RegExp, l_comment_symbol: string, l_comment_regExp: RegExp, needDefinition=true) {
|
||||
async function getFullSymbolInfo(document: vscode.TextDocument, range: Range, nonblank: RegExp, l_comment_symbol: string, l_comment_regExp: RegExp) {
|
||||
const comments = [];
|
||||
if (needDefinition) {
|
||||
const startPosition = new vscode.Position(range.start.line, range.start.character);
|
||||
const endPosition = new vscode.Position(range.end.line, range.end.character);
|
||||
const definitionString = document.getText(new vscode.Range(startPosition, endPosition));
|
||||
comments.push(definitionString);
|
||||
}
|
||||
|
||||
let content = '';
|
||||
let is_b_comment = false;
|
||||
@ -212,7 +206,13 @@ async function getFullSymbolInfo(document: vscode.TextDocument, range: Range, no
|
||||
}
|
||||
}
|
||||
|
||||
return comments.reverse().join('');
|
||||
// 清除空前行
|
||||
let resultComment = '';
|
||||
for (const c of comments.reverse()) {
|
||||
resultComment += c.trim() + '\n';
|
||||
}
|
||||
|
||||
return resultComment;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,8 +220,8 @@ async function getFullSymbolInfo(document: vscode.TextDocument, range: Range, no
|
||||
* @param path
|
||||
* @param range
|
||||
*/
|
||||
async function getSymbolComment(path: AbsPath, range: Range) {
|
||||
let languageId = hdlFile.getLanguageId(path);
|
||||
async function getSymbolComment(path: AbsPath, range: Range): Promise<string | null> {
|
||||
const languageId = hdlFile.getLanguageId(path);
|
||||
const uri = vscode.Uri.file(path);
|
||||
const documentPromise = vscode.workspace.openTextDocument(uri);
|
||||
|
||||
@ -229,14 +229,15 @@ async function getSymbolComment(path: AbsPath, range: Range) {
|
||||
const nonblank = /\S+/g;
|
||||
const l_comment = getCommentUtilByLanguageId(languageId);
|
||||
if (l_comment) {
|
||||
let l_comment_symbol = l_comment.l_comment_symbol;
|
||||
let l_comment_regExp = l_comment.l_comment_regExp;
|
||||
const l_comment_symbol = l_comment.l_comment_symbol;
|
||||
const l_comment_regExp = l_comment.l_comment_regExp;
|
||||
|
||||
// add definition first
|
||||
const document = await documentPromise;
|
||||
return await getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp);
|
||||
const symbolInfo = await getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp);
|
||||
return symbolInfo;
|
||||
}
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,7 +264,7 @@ async function getSymbolComments(path: string, ranges: Range[]): Promise<string[
|
||||
const commentPromises = [];
|
||||
const comments = [];
|
||||
for (const range of ranges) {
|
||||
const commentP = getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp, false);
|
||||
const commentP = getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp);
|
||||
commentPromises.push(commentP);
|
||||
}
|
||||
|
||||
|
@ -1,570 +0,0 @@
|
||||
const vscode = require('vscode');
|
||||
|
||||
const { transferVlogNumber, getSymbolComment, getSymbolComments } = require('./feature');
|
||||
|
||||
const { SymbolResult, Position, CommentResult, Range, Module, Instance,
|
||||
HDLParam, ModPort, ModParam } = require('../../../HDLparser');
|
||||
|
||||
const vlogKeyword = new Set([
|
||||
'`include', '`define', 'input', 'output', 'inout', 'module', 'endmodule',
|
||||
'wire', 'reg', 'parameter', 'always', 'assign', 'if', 'else', 'begin', 'end',
|
||||
'case', 'endcase', 'posedge', 'negedge', 'or', 'default', 'while', 'and', '`timescale',
|
||||
'or', 'xor', 'initial', 'function', 'endfunction', 'force', 'pulldown'
|
||||
]);
|
||||
|
||||
|
||||
/**
|
||||
* @returns {Array<vscode.CompletionItem>}
|
||||
*/
|
||||
function getVlogKeywordItem() {
|
||||
const vlogKeywordItem = [];
|
||||
for (const keyword of vlogKeyword) {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
clItem.detail = "keyword";
|
||||
vlogKeywordItem.push(clItem);
|
||||
}
|
||||
return vlogKeywordItem;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} singleWord
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isVlogKeyword(singleWord) {
|
||||
return vlogKeyword.has(singleWord);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description get the last single word in current line
|
||||
* @param {string} prefixString
|
||||
* @returns {string}
|
||||
*/
|
||||
function getLastSingleWord(prefixString) {
|
||||
prefixString = prefixString.trim();
|
||||
const length = prefixString.length;
|
||||
if (length == 0) {
|
||||
return '';
|
||||
}
|
||||
const wordCharacters = [];
|
||||
let alphaReg = /[`_0-9A-Za-z]/;
|
||||
for (let i = length - 1; i >= 0; -- i) {
|
||||
const ch = prefixString[i];
|
||||
if (alphaReg.test(ch)) {
|
||||
wordCharacters.push(ch);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wordCharacters.reverse().join('');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description get the single word at hover
|
||||
* @param {string} lineText
|
||||
* @param {number} character
|
||||
*/
|
||||
function getSingleWordAtCurrentPosition(lineText, character) {
|
||||
let alphaReg = /[`_0-9A-Za-z]/;
|
||||
if (alphaReg.test(lineText[character])) {
|
||||
const leftPart = [];
|
||||
const rightPart = [];
|
||||
const length = lineText.length;
|
||||
for (let i = character - 1; i >= 0; -- i) {
|
||||
const ch = lineText[i];
|
||||
if (alphaReg.test(ch)) {
|
||||
leftPart.push(ch);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = character + 1; i < length; ++ i) {
|
||||
const ch = lineText[i];
|
||||
if (alphaReg.test(ch)) {
|
||||
rightPart.push(ch);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const leftWord = leftPart.reverse().join('');
|
||||
const rightWord = rightPart.join('');
|
||||
return leftWord + lineText[character] + rightWord;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Position} position_a
|
||||
* @param {Position} position_b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function positionAfter(position_a, position_b) {
|
||||
return position_a.line > position_b.line || (
|
||||
position_a.line == position_b.line &&
|
||||
position_a.character > position_b.character);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Position} position_a
|
||||
* @param {Position} position_b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function positionEqual(position_a, position_b) {
|
||||
return position_a.line == position_b.line &&
|
||||
position_a.character == position_b.character;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description position_a behind or equal to position_b
|
||||
* @param {Position} position_a
|
||||
* @param {Position} position_b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function positionAfterEqual(position_a, position_b) {
|
||||
return positionAfter(position_a, position_b) ||
|
||||
positionEqual(position_a, position_b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description filter the symbol result item that exceed the scope
|
||||
* @param {vscode.Position} position
|
||||
* @param {Array<SymbolResult>} symbolResults
|
||||
* @returns {{module : SymbolResult, symbols : Array<SymbolResult>}}
|
||||
*/
|
||||
function filterSymbolScope(position, symbolResults) {
|
||||
if (!symbolResults) {
|
||||
return null;
|
||||
}
|
||||
const parentModules = symbolResults.filter(item =>
|
||||
item.type == 'module' &&
|
||||
positionAfterEqual(position, item.start) &&
|
||||
positionAfterEqual(item.end, position)
|
||||
);
|
||||
|
||||
if (parentModules.length == 0) {
|
||||
// TODO : macro
|
||||
return null;
|
||||
}
|
||||
|
||||
const parentModule = parentModules[0];
|
||||
const symbols = symbolResults.filter(item =>
|
||||
item != parentModule &&
|
||||
positionAfterEqual(item.start, parentModule.start) &&
|
||||
positionAfterEqual(parentModule.end, item.end));
|
||||
|
||||
return {
|
||||
module : parentModule,
|
||||
symbols : symbols
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {vscode.TextDocument} document
|
||||
* @param {Position} position
|
||||
* @param {Array<CommentResult>} comments
|
||||
*/
|
||||
function isInComment(document, position, comments) {
|
||||
if (!comments) {
|
||||
return false;
|
||||
}
|
||||
// remove the situation that <cursor> // comment
|
||||
const lineText = document.lineAt(position).text;
|
||||
const singleCommentIndex = lineText.indexOf('//');
|
||||
if (singleCommentIndex != -1) {
|
||||
return position.character >= singleCommentIndex;
|
||||
}
|
||||
|
||||
const currentLine = position.line + 1;
|
||||
for (const comment of comments) {
|
||||
const commentLine = comment.start.line;
|
||||
if (commentLine > currentLine) {
|
||||
continue;
|
||||
}
|
||||
const startPosition = new vscode.Position(commentLine, 0);
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endPosition = document.positionAt(startOffset + comment.length);
|
||||
|
||||
const originalPosition = new Position(currentLine, position.character);
|
||||
|
||||
if (positionAfterEqual(originalPosition, startPosition) &&
|
||||
positionAfterEqual(endPosition, originalPosition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {vscode.Position} position
|
||||
* @param {object} includes
|
||||
* @returns {{name: string, start: Position, end: Position}}
|
||||
*/
|
||||
function matchInclude(position, includes) {
|
||||
if (!includes) {
|
||||
return null;
|
||||
}
|
||||
for (const includeString of Object.keys(includes)) {
|
||||
const range = includes[includeString];
|
||||
// TODO : remove - 1 if bug is fixed
|
||||
range.start.line -= 1;
|
||||
range.end.line -= 1;
|
||||
if (positionAfterEqual(position, range.start) &&
|
||||
positionAfterEqual(range.end, position)) {
|
||||
|
||||
return {
|
||||
name : includeString,
|
||||
start: range.start,
|
||||
end: range.end
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {vscode.Position} position
|
||||
* @param {string} singleWord
|
||||
* @param {object} defines
|
||||
* @returns {{name: string, value: any, range: Range}}
|
||||
*/
|
||||
function matchDefine(position, defines) {
|
||||
if (!defines) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const macro of Object.keys(defines)) {
|
||||
const range = defines[macro].range;
|
||||
range.start.line -= 1;
|
||||
range.end.line -= 1;
|
||||
if (positionAfterEqual(position, range.start) &&
|
||||
positionAfterEqual(range.end, position)) {
|
||||
return {
|
||||
name : macro,
|
||||
value: defines[macro].value,
|
||||
range: range
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {vscode.Position} position
|
||||
* @param {string} singleWord
|
||||
* @param {object} defines
|
||||
* @returns {{name: string, value: any, range: Range}}
|
||||
*/
|
||||
function matchDefineMacro(position, singleWord, defines) {
|
||||
if (!defines) {
|
||||
return null;
|
||||
}
|
||||
if (singleWord[0] != '`' || singleWord.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
const targetMacro = singleWord.substring(1);
|
||||
for (const macro of Object.keys(defines)) {
|
||||
if (macro == targetMacro) {
|
||||
const range = defines[macro].range;
|
||||
const value = defines[macro].value;
|
||||
// TODO : remove - 1 if bug is fixed
|
||||
range.start.line -= 1;
|
||||
range.end.line -= 1;
|
||||
return {
|
||||
name : macro,
|
||||
value : value,
|
||||
range : range
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} singleWord single word to be matched
|
||||
* @param {Module} module
|
||||
* @returns {Instance}
|
||||
*/
|
||||
function matchInstance(singleWord, module) {
|
||||
if (!module) {
|
||||
console.log('warning, cannot locate module', singleWord);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const inst of module.getInstances()) {
|
||||
if (singleWord == inst.type) {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {vscode.Position} position current cursor position
|
||||
* @param {Array<SymbolResult>} symbols all the symbols in the wrapper module
|
||||
* @param {Module} module wrapper module
|
||||
* @param {Instance}
|
||||
*/
|
||||
function filterInstanceByPosition(position, symbols, module) {
|
||||
if (!symbols) {
|
||||
return null;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
const inst = module.findInstance(symbol.name);
|
||||
if (positionAfterEqual(position, symbol.start) &&
|
||||
positionAfterEqual(symbol.end, position) &&
|
||||
inst) {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Instance} inst
|
||||
* @param {vscode.Position} position
|
||||
* @param {string} singleWord
|
||||
* @returns {Promise<ModPort>}
|
||||
*/
|
||||
async function getInstPortByPosition(inst, position, singleWord) {
|
||||
if (!inst.module || !inst.instports) {
|
||||
return null;
|
||||
}
|
||||
if (positionAfterEqual(position, inst.instports.start) &&
|
||||
positionAfterEqual(inst.instports.end, position)) {
|
||||
for (const port of inst.module.ports) {
|
||||
if (port.name == singleWord) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {Instance} inst
|
||||
* @param {vscode.Position} position
|
||||
* @param {string} singleWord
|
||||
* @returns {Promise<ModParam>}
|
||||
*/
|
||||
async function getInstParamByPosition(inst, position, singleWord) {
|
||||
if (!inst.module || !inst.instparams) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (positionAfterEqual(position, inst.instparams.start) &&
|
||||
positionAfterEqual(inst.instparams.end, position)) {
|
||||
for (const param of inst.module.params) {
|
||||
if (param.name == singleWord) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} lineText
|
||||
* @param {number} character
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isPositionInput(lineText, character) {
|
||||
let alphaReg = /[_0-9A-Za-z]/;
|
||||
for (let i = character; i >= 0; -- i) {
|
||||
const ch = lineText[i];
|
||||
if (alphaReg.test(ch)) {
|
||||
continue;
|
||||
} else if (ch == '.') {
|
||||
if (i == 0) {
|
||||
return true;
|
||||
} else if (lineText[i - 1] == ' ') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} singleWord
|
||||
* @param {Module} module
|
||||
* @returns {ModPort}
|
||||
*/
|
||||
function matchPorts(singleWord, module) {
|
||||
if (!module || module.ports.length == 0) {
|
||||
return null;
|
||||
}
|
||||
const targetPorts = module.ports.filter(port => port.name == singleWord);
|
||||
if (targetPorts.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return targetPorts[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} singleWord
|
||||
* @param {Module} module
|
||||
* @returns {ModParam}
|
||||
*/
|
||||
function matchParams(singleWord, module) {
|
||||
if (!module || module.params.length == 0) {
|
||||
return null;
|
||||
}
|
||||
const targetParams = module.params.filter(param => param.name == singleWord);
|
||||
if (targetParams.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return targetParams[0];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ModPort} port
|
||||
* @returns {string}
|
||||
*/
|
||||
function makePortDesc(port) {
|
||||
let portDesc = port.type;
|
||||
if (port.width) {
|
||||
portDesc += ' ' + port.width;
|
||||
}
|
||||
portDesc += ' ' + port.name;
|
||||
return portDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ModParam} param
|
||||
* @returns {string}
|
||||
*/
|
||||
function makeParamDesc(param) {
|
||||
let paramDesc = 'parameter ' + param.name;
|
||||
if (param.init) {
|
||||
paramDesc += ' = ' + param.init;
|
||||
}
|
||||
return paramDesc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} singleWord
|
||||
* @param {Array<SymbolResult>} symbols
|
||||
* @returns {SymbolResult}
|
||||
*/
|
||||
function matchNormalSymbol(singleWord, symbols) {
|
||||
if (!symbols || Object.keys(symbols).length == 0) {
|
||||
return null;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
if (singleWord == symbol.name) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {vscode.MarkdownString} content
|
||||
* @param {Module} module
|
||||
*/
|
||||
async function makeVlogHoverContent(content, module) {
|
||||
const portNum = module.ports.length;
|
||||
const paramNum = module.params.length;
|
||||
const instNum = module.getInstanceNum();
|
||||
|
||||
const moduleUri = vscode.Uri.file(module.path);
|
||||
const thenableFileDocument = vscode.workspace.openTextDocument(moduleUri);
|
||||
|
||||
const portDesc = paramNum + ' $(instance-param) ' +
|
||||
portNum + ' $(instance-port) ' +
|
||||
instNum + ' $(instance-module)';
|
||||
|
||||
|
||||
content.appendCodeblock('module ' + module.name, 'verilog');
|
||||
content.appendText('\n');
|
||||
content.appendMarkdown(portDesc);
|
||||
content.appendText(' | ');
|
||||
|
||||
const count = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
inout: 0
|
||||
};
|
||||
for (const port of module.ports) {
|
||||
count[port.type] += 1;
|
||||
}
|
||||
const ioDesc = count.input + ' $(instance-input) ' +
|
||||
count.output + ' $(instance-output) ' +
|
||||
count.inout + ' $(instance-inout)';
|
||||
content.appendMarkdown(ioDesc);
|
||||
content.appendText('\n');
|
||||
|
||||
content.appendMarkdown('---');
|
||||
|
||||
// make document
|
||||
const fileDocument = await thenableFileDocument;
|
||||
const range = new vscode.Range(module.range.start, module.range.end);
|
||||
const moduleDefinitionCode = fileDocument.getText(range);
|
||||
content.appendCodeblock(moduleDefinitionCode, 'verilog');
|
||||
}
|
||||
|
||||
|
||||
async function searchCommentAround(uri, range) {
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getVlogKeywordItem,
|
||||
getLastSingleWord,
|
||||
getSingleWordAtCurrentPosition,
|
||||
filterSymbolScope,
|
||||
filterInstanceByPosition,
|
||||
isPositionInput,
|
||||
isInComment,
|
||||
matchInclude,
|
||||
matchDefine,
|
||||
matchDefineMacro,
|
||||
matchInstance,
|
||||
matchPorts,
|
||||
matchParams,
|
||||
matchNormalSymbol,
|
||||
isVlogKeyword,
|
||||
makeVlogHoverContent,
|
||||
positionAfterEqual,
|
||||
getInstPortByPosition,
|
||||
getInstParamByPosition,
|
||||
makePortDesc,
|
||||
makeParamDesc,
|
||||
transferVlogNumber,
|
||||
getSymbolComment,
|
||||
getSymbolComments
|
||||
};
|
408
src/function/lsp/util/index.ts
Normal file
408
src/function/lsp/util/index.ts
Normal file
@ -0,0 +1,408 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { transferVlogNumber, getSymbolComment, getSymbolComments } from './feature';
|
||||
|
||||
import { AbsPath, AllowNull } from '../../../global';
|
||||
import { Position, Range, HdlModulePort, HdlModuleParam, CommentResult, RawSymbol, Define, Include, makeVscodePosition } from '../../../hdlParser/common';
|
||||
import { HdlModule, HdlInstance, hdlParam } from '../../../hdlParser/core';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Unknown = 'Unknown';
|
||||
|
||||
interface MatchedSymbol {
|
||||
name: string,
|
||||
value: any,
|
||||
range: Range
|
||||
};
|
||||
|
||||
|
||||
interface ModuleScope {
|
||||
module: RawSymbol,
|
||||
symbols: RawSymbol[]
|
||||
};
|
||||
|
||||
|
||||
function transformRange(range: Range | vscode.Range, lineOffset: number = 0, characterOffset: number = 0,
|
||||
endLineOffset: number | undefined = undefined, endCharacterOffset: number | undefined = undefined): vscode.Range {
|
||||
const start = range.start;
|
||||
const end = range.end;
|
||||
const startPosition = new vscode.Position(start.line + lineOffset, start.character + characterOffset);
|
||||
endLineOffset = endLineOffset ? endLineOffset : lineOffset;
|
||||
endCharacterOffset = endCharacterOffset ? endLineOffset : characterOffset;
|
||||
const endPosition = new vscode.Position(end.line + endLineOffset, end.character + endCharacterOffset);
|
||||
return new vscode.Range(startPosition, endPosition);
|
||||
}
|
||||
|
||||
|
||||
function positionAfter(positionA: Position, positionB: Position): boolean {
|
||||
return positionA.line > positionB.line || (
|
||||
positionA.line === positionB.line &&
|
||||
positionA.character > positionB.character);
|
||||
}
|
||||
|
||||
|
||||
function positionEqual(positionA: Position, positionB: Position): boolean {
|
||||
return positionA.line === positionB.line &&
|
||||
positionA.character === positionB.character;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description positionA behind or equal to positionB
|
||||
*/
|
||||
function positionAfterEqual(positionA: Position, positionB: Position): boolean {
|
||||
return positionAfter(positionA, positionB) ||
|
||||
positionEqual(positionA, positionB);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description filter the symbol result item that exceed the scope
|
||||
*/
|
||||
function filterSymbolScope(position: vscode.Position, rawSymbols: RawSymbol[]): AllowNull<ModuleScope> {
|
||||
if (!rawSymbols) {
|
||||
return null;
|
||||
}
|
||||
const parentModules = rawSymbols.filter(item =>
|
||||
item.type === 'module' &&
|
||||
positionAfterEqual(position, item.range.start) &&
|
||||
positionAfterEqual(item.range.end, position)
|
||||
);
|
||||
|
||||
if (parentModules.length === 0) {
|
||||
// TODO : macro
|
||||
return null;
|
||||
}
|
||||
|
||||
const parentModule = parentModules[0];
|
||||
const symbols = rawSymbols.filter(item =>
|
||||
item !== parentModule &&
|
||||
positionAfterEqual(item.range.start, parentModule.range.start) &&
|
||||
positionAfterEqual(parentModule.range.end, item.range.end));
|
||||
|
||||
return {
|
||||
module : parentModule,
|
||||
symbols : symbols
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function isInComment(document: vscode.TextDocument, position: Position, comments: CommentResult[]): boolean {
|
||||
if (!comments) {
|
||||
return false;
|
||||
}
|
||||
// remove the situation that <cursor> // comment
|
||||
const lineText = document.lineAt(makeVscodePosition(position)).text;
|
||||
const singleCommentIndex = lineText.indexOf('//');
|
||||
if (singleCommentIndex !== -1) {
|
||||
return position.character >= singleCommentIndex;
|
||||
}
|
||||
|
||||
const currentLine = position.line + 1;
|
||||
for (const comment of comments) {
|
||||
const commentLine = comment.start.line;
|
||||
if (commentLine > currentLine) {
|
||||
continue;
|
||||
}
|
||||
const startPosition = new vscode.Position(commentLine, 0);
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endPosition = document.positionAt(startOffset + comment.length);
|
||||
|
||||
const originalPosition: Position = {line: currentLine, character: position.character};
|
||||
|
||||
if (positionAfterEqual(originalPosition, startPosition) &&
|
||||
positionAfterEqual(endPosition, originalPosition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function matchInclude(document: vscode.TextDocument, position: vscode.Position, includes: Include[]) : AllowNull<MatchedSymbol> {
|
||||
const selectFileRange = document.getWordRangeAtPosition(position, /[\._0-9A-Za-z]+/);
|
||||
const selectFileName = document.getText(selectFileRange);
|
||||
|
||||
if (!includes) {
|
||||
return null;
|
||||
}
|
||||
for (const include of includes) {
|
||||
const range = include.range;
|
||||
if (position.line + 1 === range.start.line && selectFileName === include.path) {
|
||||
return {
|
||||
name : include.path,
|
||||
value: include.path,
|
||||
range: range
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function matchDefine(position: vscode.Position, defines: Define[]) : AllowNull<MatchedSymbol> {
|
||||
if (!defines) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const define of defines) {
|
||||
const range = define.range;
|
||||
if (positionAfterEqual(position, range.start) &&
|
||||
positionAfterEqual(range.end, position)) {
|
||||
return {
|
||||
name : define.name,
|
||||
value: define.replacement,
|
||||
range: range
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function matchDefineMacro(position: vscode.Position, singleWord: string, defines: Define[]) : AllowNull<MatchedSymbol> {
|
||||
if (!defines) {
|
||||
return null;
|
||||
}
|
||||
if (singleWord[0] !== '`' || singleWord.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
const targetMacro = singleWord.substring(1);
|
||||
for (const define of defines) {
|
||||
if (define.name === targetMacro) {
|
||||
const range = define.range;
|
||||
return {
|
||||
name : define.name,
|
||||
value : define.replacement,
|
||||
range : range
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function matchInstance(singleWord: string, module: HdlModule): AllowNull<HdlInstance> {
|
||||
for (const inst of module.getAllInstances()) {
|
||||
if (singleWord === inst.type) {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function filterInstanceByPosition(position: vscode.Position, symbols: RawSymbol[], module: HdlModule): AllowNull<HdlInstance> {
|
||||
if (!symbols) {
|
||||
return null;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
const inst = module.getInstance(symbol.name);
|
||||
if (positionAfterEqual(position, symbol.range.start) &&
|
||||
positionAfterEqual(symbol.range.end, position) &&
|
||||
inst) {
|
||||
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
async function getInstPortByPosition(inst: HdlInstance, position: vscode.Position, singleWord: string): Promise<AllowNull<HdlModulePort>> {
|
||||
if (!inst.module || !inst.instports) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const instportRange = transformRange(inst.instports, -1, 0);
|
||||
|
||||
if (positionAfterEqual(position, instportRange.start) &&
|
||||
positionAfterEqual(instportRange.end, position)) {
|
||||
for (const port of inst.module.ports) {
|
||||
if (port.name === singleWord) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
async function getInstParamByPosition(inst: AllowNull<HdlInstance>, position: vscode.Position, singleWord: string): Promise<AllowNull<HdlModuleParam>> {
|
||||
if (!inst || !inst.module || !inst.instparams) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const instParamRange = transformRange(inst.instparams, -1, 0);
|
||||
|
||||
if (positionAfterEqual(position, instParamRange.start) &&
|
||||
positionAfterEqual(instParamRange.end, position)) {
|
||||
for (const param of inst.module.params) {
|
||||
if (param.name === singleWord) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function isPositionInput(lineText: string, character: number): boolean {
|
||||
const alphaReg = /[_0-9A-Za-z]/;
|
||||
for (let i = character; i >= 0; -- i) {
|
||||
const ch = lineText[i];
|
||||
if (alphaReg.test(ch)) {
|
||||
continue;
|
||||
} else if (ch === '.') {
|
||||
if (i === 0) {
|
||||
return true;
|
||||
} else if (lineText[i - 1] === ' ') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function matchPorts(singleWord: string, module: HdlModule): AllowNull<HdlModulePort> {
|
||||
if (!module || module.ports.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const targetPorts = module.ports.filter(port => port.name === singleWord);
|
||||
if (targetPorts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return targetPorts[0];
|
||||
}
|
||||
|
||||
|
||||
function matchParams(singleWord: string, module: HdlModule): AllowNull<HdlModuleParam> {
|
||||
if (module.params.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetParams = module.params.filter(param => param.name === singleWord);
|
||||
if (targetParams.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return targetParams[0];
|
||||
}
|
||||
|
||||
|
||||
function makePortDesc(port: HdlModulePort): string {
|
||||
let portDesc: string = port.type;
|
||||
if (port.width && port.width !== Unknown) {
|
||||
portDesc += ' ' + port.width;
|
||||
}
|
||||
portDesc += ' ' + port.name;
|
||||
return portDesc;
|
||||
}
|
||||
|
||||
|
||||
function makeParamDesc(param: HdlModuleParam): string {
|
||||
let paramDesc = 'parameter ' + param.name;
|
||||
if (param.init && param.init !== Unknown) {
|
||||
paramDesc += ' = ' + param.init;
|
||||
}
|
||||
return paramDesc;
|
||||
}
|
||||
|
||||
function makeNormalDesc(normal: RawSymbol): string {
|
||||
const desc = normal.type + ' ' + normal.width + ' ' + normal.name;
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
||||
function matchNormalSymbol(singleWord: string, symbols: RawSymbol[]): AllowNull<RawSymbol> {
|
||||
if (!symbols || Object.keys(symbols).length === 0) {
|
||||
return null;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
if (singleWord === symbol.name) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
async function makeVlogHoverContent(content: vscode.MarkdownString, module: HdlModule) {
|
||||
const portNum = module.ports.length;
|
||||
const paramNum = module.params.length;
|
||||
const instNum = module.getInstanceNum();
|
||||
|
||||
const moduleUri = vscode.Uri.file(module.path);
|
||||
const thenableFileDocument = vscode.workspace.openTextDocument(moduleUri);
|
||||
|
||||
const portDesc = ' $(instance-param) ' + paramNum +
|
||||
' $(instance-port) ' + portNum +
|
||||
' $(instance-module)' + instNum;
|
||||
|
||||
|
||||
content.appendMarkdown(portDesc);
|
||||
content.appendText(' | ');
|
||||
|
||||
const count = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
inout: 0
|
||||
};
|
||||
for (const port of module.ports) {
|
||||
count[port.type as keyof typeof count] += 1;
|
||||
}
|
||||
const ioDesc = ' $(instance-input) ' + count.input +
|
||||
' $(instance-output) ' + count.output +
|
||||
' $(instance-inout)' + count.inout;
|
||||
content.appendMarkdown(ioDesc);
|
||||
content.appendText('\n');
|
||||
|
||||
content.appendMarkdown('---');
|
||||
|
||||
// make document
|
||||
const fileDocument = await thenableFileDocument;
|
||||
const range = transformRange(module.range, -1, 0, 1);
|
||||
const moduleDefinitionCode = fileDocument.getText(range);
|
||||
content.appendCodeblock(moduleDefinitionCode, 'verilog');
|
||||
}
|
||||
|
||||
|
||||
async function searchCommentAround(path: AbsPath, range: Range): Promise<string | null> {
|
||||
const targetRange = transformRange(range, -1, 0);
|
||||
const comment = await getSymbolComment(path, targetRange);
|
||||
return comment;
|
||||
}
|
||||
|
||||
export {
|
||||
transformRange,
|
||||
filterSymbolScope,
|
||||
filterInstanceByPosition,
|
||||
isPositionInput,
|
||||
isInComment,
|
||||
matchInclude,
|
||||
matchDefine,
|
||||
matchDefineMacro,
|
||||
matchInstance,
|
||||
matchPorts,
|
||||
matchParams,
|
||||
matchNormalSymbol,
|
||||
makeVlogHoverContent,
|
||||
positionAfterEqual,
|
||||
getInstPortByPosition,
|
||||
getInstParamByPosition,
|
||||
makePortDesc,
|
||||
makeParamDesc,
|
||||
makeNormalDesc,
|
||||
transferVlogNumber,
|
||||
searchCommentAround,
|
||||
};
|
130
src/function/lsp/util/keyword.ts
Normal file
130
src/function/lsp/util/keyword.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
class Keywords {
|
||||
keywords: Set<string>;
|
||||
keywordItems: vscode.CompletionItem[];
|
||||
compilerKeywords: string[]; // start with `
|
||||
systemKeywords: string[]; // start with $
|
||||
constructor(keywords: string[], compilerKeywords: string[], systemKeywords: string[]) {
|
||||
this.keywords = new Set(keywords);
|
||||
const keywordItems = [];
|
||||
for (const keyword of keywords) {
|
||||
const clItem = new vscode.CompletionItem(keyword, vscode.CompletionItemKind.Keyword);
|
||||
clItem.detail = "keyword";
|
||||
keywordItems.push(clItem);
|
||||
}
|
||||
this.keywordItems = keywordItems;
|
||||
this.compilerKeywords = compilerKeywords;
|
||||
this.systemKeywords = systemKeywords;
|
||||
}
|
||||
|
||||
public keys(): Set<string> {
|
||||
return this.keywords;
|
||||
}
|
||||
|
||||
public isKeyword(word: string): boolean {
|
||||
return this.keywords.has(word);
|
||||
}
|
||||
}
|
||||
|
||||
const vlogKeyword = new Keywords([
|
||||
"above", "disable", "idt", "notif1", "supply0", "abs", "discipline", "idtmod",
|
||||
"or", "supply1", "absdelay", "driver_update", "if", "output", "table", "ac_stim",
|
||||
"edge", "ifnone", "parameter", "tan", "acos", "else", "inf", "pmos", "tanh",
|
||||
"acosh", "end", "initial", "posedge", "task", "always", "endcase", "initial_step",
|
||||
"potential", "time", "analog", "endconnectrules", "inout", "pow", "timer", "analysis",
|
||||
"enddiscipline", "input", "primitive", "tran", "and", "endfunction", "integer",
|
||||
"pull0", "tranif0", "asin", "endmodule", "join", "pull1", "tranif1", "asinh",
|
||||
"endnature", "laplace_nd", "pulldown", "transition", "assign", "endprimitive",
|
||||
"laplace_np", "pullup", "tri", "atan", "endspecify", "laplace_zd", "rcmos", "tri0",
|
||||
"atan2", "endtable", "laplace_zp", "real", "tri1", "atanh", "endtask", "large",
|
||||
"realtime", "triand", "begin", "event", "last_crossing", "reg", "trior", "branch",
|
||||
"exclude", "limexp", "release", "trireg", "buf", "exp", "ln", "repeat", "vectored",
|
||||
"bufif0", "final_step", "log", "rnmos", "wait", "bufif1", "flicker_noise", "macromodule",
|
||||
"rpmos", "wand", "case", "flow", "max", "rtran", "weak0", "casex", "for", "medium",
|
||||
"rtranif0", "weak1", "casez", "force", "min", "rtranif1", "while", "ceil", "forever",
|
||||
"module", "scalared", "white_noise", "cmos", "fork", "nand", "sin", "wire",
|
||||
"connectrules", "from", "nature", "sinh", "wor", "cos", "function", "negedge",
|
||||
"slew", "wreal", "cosh", "generate", "net_resolution", "small", "xnor", "cross",
|
||||
"genvar", "nmos", "specify", "xor", "ddt", "ground", "noise_table", "specparam",
|
||||
"zi_nd", "deassign", "highz0", "nor", "sqrt", "zi_np", "default", "highz1",
|
||||
"not", "strong0", "zi_zd", "defparam", "hypot", "notif0", "strong1", "zi_zp",
|
||||
],
|
||||
[
|
||||
"begin_keywords", "celldefine", "default_nettype", "define", "else", "elsif",
|
||||
"end_keywords", "endcelldefine", "endif", "ifdef", "ifndef", "include", "line",
|
||||
"nounconnected_drive", "pragma", "resetall", "timescale", "unconnected_drive", "undef"
|
||||
],
|
||||
[
|
||||
'acos', 'sqrt', 'ftell', 'fdisplay', 'fclose', 'timeformat', 'fseek', 'asinh', 'fdisplayh',
|
||||
'fmonitorb', 'dist_erlang', 'writeb', 'fwrite', 'swriteb', 'printtimescale', 'displayb', 'atan',
|
||||
'sscanf', 'exp', 'nand', 'fdisplayo', 'sdf_annotate', 'stime', 'sin', 'dist_chi_square', 'strobeh',
|
||||
'array', 'swriteo', 'and', 'dist_normal', 'readmemb', 'strobeb', 'realtobits', 'or', 'dist_exponential',
|
||||
'plusargs', 'hypot', 'swrite', 'writeh', 'plane', 'fstrobeb', 'time', 'fread', 'fstrobe', 'rewind', 'async',
|
||||
'sync', 'bitstoreal', 'readmemh', 'fwriteo', 'value', 'tanh', 'rtoi', 'fgetc', 'realtime', 'atan2', 'acosh',
|
||||
'displayh', 'log10', 'clog2', 'fstrobeo', 'pow', 'fwriteb', 'fwriteh', 'nor', 'fdisplayb', 'monitoroff', 'fgets',
|
||||
'q_add', 'atanh', 'monitor', 'monitorh', 'cos', 'ln', 'q_initialize', 'unsigned', 'swriteh', 'asin', 'monitoron',
|
||||
'monitoro', 'display', 'tan', 'fscanf', 'dist_uniform', 'test', 'q_exam', 'itor', 'random', 'fstrobeh', 'strobe',
|
||||
'displayo', 'monitorb', 'ungetc', 'feof', 'stop', 'ferror', 'finish', 'fmonitor', 'fmonitorh', 'cosh', 'writeo', 'sinh',
|
||||
'dist_poisson', 'write', 'fopen', 'fmonitoro', 'fflush', 'ceil', 'strobeo', 'dist_t', 'q_full', 'signed', 'sformat',
|
||||
'q_remove', 'floor',
|
||||
'dumpfile', 'dumpvars', 'dumpoff', 'dumpon', 'dumpall', 'dumplimit', 'dumpflush',
|
||||
'dumpports', 'dumpportsoff', 'dumpportson', 'dumpportsall', 'dumpportslimit', 'dumpportsflush'
|
||||
]
|
||||
);
|
||||
|
||||
// TODO : do vhdl and sv version
|
||||
const vhdlKeyword = new Keywords([], [], []);
|
||||
|
||||
const systemverilogKeyword = new Keywords([
|
||||
"above", "disable", "idt", "notif1", "supply0", "abs", "discipline", "idtmod",
|
||||
"or", "supply1", "absdelay", "driver_update", "if", "output", "table", "ac_stim",
|
||||
"edge", "ifnone", "parameter", "tan", "acos", "else", "inf", "pmos", "tanh",
|
||||
"acosh", "end", "initial", "posedge", "task", "always", "endcase", "initial_step",
|
||||
"potential", "time", "analog", "endconnectrules", "inout", "pow", "timer", "analysis",
|
||||
"enddiscipline", "input", "primitive", "tran", "and", "endfunction", "integer",
|
||||
"pull0", "tranif0", "asin", "endmodule", "join", "pull1", "tranif1", "asinh",
|
||||
"endnature", "laplace_nd", "pulldown", "transition", "assign", "endprimitive",
|
||||
"laplace_np", "pullup", "tri", "atan", "endspecify", "laplace_zd", "rcmos", "tri0",
|
||||
"atan2", "endtable", "laplace_zp", "real", "tri1", "atanh", "endtask", "large",
|
||||
"realtime", "triand", "begin", "event", "last_crossing", "reg", "trior", "branch",
|
||||
"exclude", "limexp", "release", "trireg", "buf", "exp", "ln", "repeat", "vectored",
|
||||
"bufif0", "final_step", "log", "rnmos", "wait", "bufif1", "flicker_noise", "macromodule",
|
||||
"rpmos", "wand", "case", "flow", "max", "rtran", "weak0", "casex", "for", "medium",
|
||||
"rtranif0", "weak1", "casez", "force", "min", "rtranif1", "while", "ceil", "forever",
|
||||
"module", "scalared", "white_noise", "cmos", "fork", "nand", "sin", "wire",
|
||||
"connectrules", "from", "nature", "sinh", "wor", "cos", "function", "negedge",
|
||||
"slew", "wreal", "cosh", "generate", "net_resolution", "small", "xnor", "cross",
|
||||
"genvar", "nmos", "specify", "xor", "ddt", "ground", "noise_table", "specparam",
|
||||
"zi_nd", "deassign", "highz0", "nor", "sqrt", "zi_np", "default", "highz1",
|
||||
"not", "strong0", "zi_zd", "defparam", "hypot", "notif0", "strong1", "zi_zp"
|
||||
],
|
||||
[
|
||||
"begin_keywords", "celldefine", "default_nettype", "define", "else", "elsif",
|
||||
"end_keywords", "endcelldefine", "endif", "ifdef", "ifndef", "include", "line",
|
||||
"nounconnected_drive", "pragma", "resetall", "timescale", "unconnected_drive", "undef"
|
||||
],
|
||||
[
|
||||
'acos', 'sqrt', 'ftell', 'fdisplay', 'fclose', 'timeformat', 'fseek', 'asinh', 'fdisplayh',
|
||||
'fmonitorb', 'dist_erlang', 'writeb', 'fwrite', 'swriteb', 'printtimescale', 'displayb', 'atan',
|
||||
'sscanf', 'exp', 'nand', 'fdisplayo', 'sdf_annotate', 'stime', 'sin', 'dist_chi_square', 'strobeh',
|
||||
'array', 'swriteo', 'and', 'dist_normal', 'readmemb', 'strobeb', 'realtobits', 'or', 'dist_exponential',
|
||||
'plusargs', 'hypot', 'swrite', 'writeh', 'plane', 'fstrobeb', 'time', 'fread', 'fstrobe', 'rewind', 'async',
|
||||
'sync', 'bitstoreal', 'readmemh', 'fwriteo', 'value', 'tanh', 'rtoi', 'fgetc', 'realtime', 'atan2', 'acosh',
|
||||
'displayh', 'log10', 'clog2', 'fstrobeo', 'pow', 'fwriteb', 'fwriteh', 'nor', 'fdisplayb', 'monitoroff', 'fgets',
|
||||
'q_add', 'atanh', 'monitor', 'monitorh', 'cos', 'ln', 'q_initialize', 'unsigned', 'swriteh', 'asin', 'monitoron',
|
||||
'monitoro', 'display', 'tan', 'fscanf', 'dist_uniform', 'test', 'q_exam', 'itor', 'random', 'fstrobeh', 'strobe',
|
||||
'displayo', 'monitorb', 'ungetc', 'feof', 'stop', 'ferror', 'finish', 'fmonitor', 'fmonitorh', 'cosh', 'writeo', 'sinh',
|
||||
'dist_poisson', 'write', 'fopen', 'fmonitoro', 'fflush', 'ceil', 'strobeo', 'dist_t', 'q_full', 'signed', 'sformat',
|
||||
'q_remove', 'floor'
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
|
||||
export {
|
||||
vlogKeyword,
|
||||
vhdlKeyword,
|
||||
systemverilogKeyword
|
||||
};
|
@ -8,6 +8,8 @@ import * as Lang from './lang';
|
||||
type AbsPath = string;
|
||||
type RelPath = string;
|
||||
|
||||
type AllowNull<T> = T | null;
|
||||
|
||||
export {
|
||||
opeParam,
|
||||
OpeParamDefaults,
|
||||
@ -19,5 +21,6 @@ export {
|
||||
RelPath,
|
||||
MainOutput,
|
||||
YosysOutput,
|
||||
ReportType
|
||||
ReportType,
|
||||
AllowNull
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { AbsPath, RelPath } from '../global';
|
||||
|
||||
@ -10,6 +11,10 @@ interface Position {
|
||||
character: number
|
||||
};
|
||||
|
||||
function makeVscodePosition(position: Position): vscode.Position {
|
||||
return new vscode.Position(position.line, position.character);
|
||||
}
|
||||
|
||||
interface Range {
|
||||
start: Position
|
||||
end: Position
|
||||
@ -32,11 +37,45 @@ enum HdlFileType {
|
||||
RemoteLib = 'remote_lib'
|
||||
};
|
||||
enum InstModPathStatus {Current, Include, Others, Unknown};
|
||||
enum SymbolType {
|
||||
Module = 'module',
|
||||
Input = 'input',
|
||||
Output = 'output'
|
||||
};
|
||||
// enum SymbolType {
|
||||
// Module = 'module',
|
||||
// Input = 'input',
|
||||
// Output = 'output',
|
||||
// Inout = 'inout',
|
||||
// Program = 'program',
|
||||
// Package = 'package',
|
||||
// Import = 'import',
|
||||
// Always = 'always',
|
||||
// Processe = 'processe',
|
||||
// Task = 'task',
|
||||
// Function = 'function',
|
||||
// Assert = 'assert',
|
||||
// Event = 'event',
|
||||
// Instance = 'instance',
|
||||
// Time = 'time',
|
||||
// Define = 'define',
|
||||
// Typedef = 'typedef',
|
||||
// Generate = 'generate',
|
||||
// Enum = 'enum',
|
||||
// Modport = 'modport',
|
||||
// Property = 'property',
|
||||
// Interface = 'interface',
|
||||
// Buffer = 'buffer',
|
||||
// Localparam = 'localparam',
|
||||
// Parameter = 'parameter',
|
||||
// Integer = 'integer',
|
||||
// Char = 'char',
|
||||
// Float = 'float',
|
||||
// Int = 'int',
|
||||
// String = 'string',
|
||||
// Struct = 'struct',
|
||||
// Class = 'class',
|
||||
// Logic = 'logic',
|
||||
// Wire = 'wire',
|
||||
// Reg = 'reg',
|
||||
// Net = 'net',
|
||||
// Bit = 'bit'
|
||||
// };
|
||||
|
||||
interface Error {
|
||||
severity: number
|
||||
@ -45,12 +84,30 @@ interface Error {
|
||||
range: Range
|
||||
};
|
||||
|
||||
interface Define {
|
||||
// `define A out
|
||||
// name is "A", value is "out"
|
||||
name: string
|
||||
interface DefineParam {
|
||||
name: string,
|
||||
value: string
|
||||
range: Position
|
||||
};
|
||||
|
||||
/**
|
||||
* `define A out
|
||||
* name is "A", replacement is "out"
|
||||
* `define max(a, b=1) a*b
|
||||
* name is "max", replacement is "a*b", params is
|
||||
* {
|
||||
"name": "a",
|
||||
"value": "Unknown"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"value": "1"
|
||||
}
|
||||
*/
|
||||
interface Define {
|
||||
name: string
|
||||
replacement: string
|
||||
range: Range
|
||||
params: DefineParam[],
|
||||
};
|
||||
|
||||
interface Include {
|
||||
@ -105,8 +162,9 @@ interface RawHdlModule {
|
||||
|
||||
interface RawSymbol {
|
||||
name: string
|
||||
type: SymbolType
|
||||
type: string
|
||||
range: Range
|
||||
width?: string
|
||||
};
|
||||
|
||||
interface InstModPathSearchResult {
|
||||
@ -120,6 +178,12 @@ interface HdlDependence {
|
||||
others: AbsPath[]
|
||||
};
|
||||
|
||||
interface CommentResult {
|
||||
start: { line : number }
|
||||
length: number
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
Position,
|
||||
Range,
|
||||
@ -138,5 +202,7 @@ export {
|
||||
RawHdlModule,
|
||||
InstModPathSearchResult,
|
||||
HdlDependence,
|
||||
RawSymbol
|
||||
RawSymbol,
|
||||
CommentResult,
|
||||
makeVscodePosition
|
||||
};
|
@ -72,10 +72,6 @@ class HdlParam {
|
||||
this.modules.add(hdlModule);
|
||||
}
|
||||
|
||||
/**
|
||||
* add module to global top modules
|
||||
* @param hdlModule
|
||||
*/
|
||||
public addTopModule(hdlModule: HdlModule) {
|
||||
this.topModules.add(hdlModule);
|
||||
}
|
||||
@ -204,13 +200,19 @@ class HdlParam {
|
||||
}
|
||||
|
||||
public async initHdlFiles(hdlFiles: AbsPath[] | Generator<AbsPath>) {
|
||||
const pools: Promise<void>[] = [];
|
||||
for (const path of hdlFiles) {
|
||||
this.doHdlFast(path);
|
||||
const p = this.doHdlFast(path);
|
||||
pools.push(p);
|
||||
}
|
||||
for (const p of pools) {
|
||||
await p;
|
||||
}
|
||||
}
|
||||
|
||||
public async initialize(hdlFiles: AbsPath[] | Generator<AbsPath>) {
|
||||
await this.initHdlFiles(hdlFiles);
|
||||
|
||||
for (const hdlFile of this.getAllHdlFiles()) {
|
||||
hdlFile.makeInstance();
|
||||
}
|
||||
@ -296,6 +298,7 @@ class HdlInstance {
|
||||
|
||||
if (instModPath) {
|
||||
this.module = hdlParam.getHdlModule(instModPath, instModName);
|
||||
|
||||
// add refer for module
|
||||
this.module?.addGlobalReferedInstance(this);
|
||||
// if module and parent module share the same source (e.g both in src folder)
|
||||
@ -348,8 +351,19 @@ class HdlModule {
|
||||
this.params = params;
|
||||
this.ports = ports;
|
||||
|
||||
if (!this.params) {
|
||||
this.params = [];
|
||||
}
|
||||
|
||||
if (!this.ports) {
|
||||
this.ports = [];
|
||||
}
|
||||
|
||||
// make instance
|
||||
this.rawInstances = instances;
|
||||
if (!this.rawInstances) {
|
||||
this.rawInstances = [];
|
||||
}
|
||||
this.nameToInstances = new Map<string, HdlInstance>();
|
||||
|
||||
// add in hdlParam data structure
|
||||
@ -397,6 +411,7 @@ class HdlModule {
|
||||
|
||||
public createHdlInstance(rawHdlInstance: common.RawHdlInstance): HdlInstance {
|
||||
const instModName = rawHdlInstance.type;
|
||||
|
||||
const searchResult = this.searchInstModPath(instModName);
|
||||
const hdlInstance = new HdlInstance(rawHdlInstance.name,
|
||||
rawHdlInstance.type,
|
||||
@ -417,6 +432,7 @@ class HdlModule {
|
||||
}
|
||||
|
||||
public makeNameToInstances() {
|
||||
|
||||
if (this.rawInstances) {
|
||||
this.nameToInstances.clear();
|
||||
for (const inst of this.rawInstances) {
|
||||
@ -508,6 +524,7 @@ class HdlModule {
|
||||
public addGlobalReferedInstance(inst: HdlInstance) {
|
||||
const globalRefers = this.globalRefers;
|
||||
globalRefers.add(inst);
|
||||
// it is refered in global scope, so delete this from top module
|
||||
if (globalRefers.size > 0) {
|
||||
hdlParam.deleteTopModule(this);
|
||||
}
|
||||
@ -524,6 +541,7 @@ class HdlModule {
|
||||
public addLocalReferedInstance(inst: HdlInstance) {
|
||||
const localRefers = this.localRefers;
|
||||
localRefers.add(inst);
|
||||
// it is refered in local scope, so delete this from top module
|
||||
if (localRefers.size > 0) {
|
||||
hdlParam.deleteTopModuleToSource(this);
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ namespace HdlSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {
|
||||
HdlSymbol
|
||||
HdlSymbol,
|
||||
};
|
@ -49,7 +49,7 @@ class PrjManage {
|
||||
* init opeParam
|
||||
* @param context
|
||||
*/
|
||||
public initOpeParam(context: vscode.ExtensionContext) {
|
||||
public async initOpeParam(context: vscode.ExtensionContext) {
|
||||
const os = process.platform;
|
||||
const extensionPath = hdlPath.toSlash(context.extensionPath);
|
||||
const workspacePath = this.getWorkspacePath();
|
||||
@ -71,6 +71,15 @@ class PrjManage {
|
||||
if (fs.existsSync(propertyJsonPath)) {
|
||||
const rawPrjInfo = hdlFile.readJSON(propertyJsonPath) as RawPrjInfo;
|
||||
opeParam.mergePrjInfo(rawPrjInfo);
|
||||
} else {
|
||||
const createProperty = await vscode.window.showInformationMessage(
|
||||
"property.json is not detected, do you want to create one ?",
|
||||
{ title: 'Yes', value: true },
|
||||
{ title: 'No', value: false }
|
||||
);
|
||||
if (createProperty?.value) {
|
||||
vscode.commands.executeCommand('digital-ide.property-json.generate');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +115,7 @@ class PrjManage {
|
||||
console.time('launch');
|
||||
}
|
||||
|
||||
this.initOpeParam(context);
|
||||
await this.initOpeParam(context);
|
||||
MainOutput.report('finish initialise opeParam', ReportType.Info);
|
||||
|
||||
const hdlFiles = this.getPrjHardwareFiles();
|
||||
@ -121,7 +130,6 @@ class PrjManage {
|
||||
MainOutput.report('create pl and ps', ReportType.Info);
|
||||
|
||||
|
||||
|
||||
if (countTimeCost) {
|
||||
console.timeLog('launch');
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
module dependence_1 (
|
||||
// this is a test
|
||||
input a, b, c,
|
||||
output Q
|
||||
// a test
|
||||
output Result // balabalabala for result
|
||||
);
|
||||
|
||||
// a & b | ((b & c) & (b | c))
|
||||
@ -10,6 +12,6 @@ module dependence_1 (
|
||||
// Simplify A + A = A AB + BC
|
||||
// Factor B(A+C)
|
||||
|
||||
assign Q = a & (b | c);
|
||||
assign Result = a & (b | c);
|
||||
|
||||
endmodule
|
@ -3,14 +3,17 @@
|
||||
* current file -> macro include -> whole project
|
||||
* expect dependence_1 from child_1.v (macro include)
|
||||
* expect dependence_2 from child_2.v (whole project)
|
||||
* cannot find dependence_3
|
||||
* cannot find dependence_3 `main
|
||||
*/
|
||||
|
||||
`include "child_1.v"
|
||||
`include "child_2.v"
|
||||
`define main out
|
||||
|
||||
module Main (
|
||||
// Main input
|
||||
input a, b, c,
|
||||
// Main output
|
||||
output Qus, Qs, `main
|
||||
);
|
||||
|
||||
@ -18,7 +21,7 @@ dependence_1 u_dependence_1(
|
||||
.a(a),
|
||||
.b(b),
|
||||
.c(c),
|
||||
.Q(Qus)
|
||||
.Result(Qus)
|
||||
);
|
||||
|
||||
dependence_2 u_dependence_2(
|
||||
|
@ -1,29 +0,0 @@
|
||||
|
||||
/*
|
||||
* CN: 如果使用`include "head_1.v" 则模块 dependence_1 使用的应该是 head_1.v 文件中的,
|
||||
* 而不会调用child_1.v中的 dependence_1 同名模块。
|
||||
* EN:
|
||||
*/
|
||||
|
||||
`include "child_1.v"
|
||||
`define main_o out
|
||||
module Main(
|
||||
input a, b, c,
|
||||
output Qus, Qs, `main_o
|
||||
);
|
||||
|
||||
dependence_1 dependence_1(
|
||||
.a(a),
|
||||
.b(b),
|
||||
.c(c),
|
||||
.Q(Qus)
|
||||
);
|
||||
|
||||
dependence_2 dependence_2(
|
||||
.a(a),
|
||||
.b(b),
|
||||
.c(c),
|
||||
.Q(Qs)
|
||||
);
|
||||
|
||||
endmodule
|
@ -6,6 +6,49 @@
|
||||
"keyEquivalent": "^~V",
|
||||
"name": "Verilog",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\s*(wire|reg)\\s+\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.control.verilog"
|
||||
},
|
||||
"2": {
|
||||
"name": "variable.other.constant.declaration.verilog"
|
||||
}
|
||||
},
|
||||
"end": "(;)",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "source.verilog"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"begin": "\\s*(wire|reg)\\s+\\[(.*?)(:)(.*?)\\]\\s+\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.control.verilog"
|
||||
},
|
||||
"2": {
|
||||
"name": "constant.numeric.width.verilog"
|
||||
},
|
||||
"3": {
|
||||
"name": "entity.name.function.width.spliter.verilog"
|
||||
},
|
||||
"4": {
|
||||
"name": "constant.numeric.width.verilog"
|
||||
},
|
||||
"5": {
|
||||
"name": "variable.other.constant.declaration.verilog"
|
||||
}
|
||||
},
|
||||
"end": "(;)",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "source.verilog"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"begin": "\\s*\\b(function|task)\\b(\\s+automatic)?",
|
||||
"beginCaptures": {
|
||||
@ -145,10 +188,10 @@
|
||||
"name": "support.class"
|
||||
}
|
||||
},
|
||||
"end": ";",
|
||||
"end": "(;)",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "entity.name.function.verilog"
|
||||
"name": "source.verilog"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
@ -166,7 +209,7 @@
|
||||
"include": "#ifmodport"
|
||||
}
|
||||
],
|
||||
"name": "variable.module.verilog"
|
||||
"name": "variable.other.constant.module.verilog"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
@ -637,10 +680,10 @@
|
||||
},
|
||||
{
|
||||
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b(?=\\s*(\\(|$))",
|
||||
"name": "support.class"
|
||||
"name": "entity.name.function.module.verilog"
|
||||
}
|
||||
],
|
||||
"name": "meta.module.inst.param.verilog"
|
||||
"name": "variable.other.constant.module.inst.param.verilog"
|
||||
},
|
||||
{
|
||||
"begin": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s+(?!intersect|and|or|throughout|within)([a-zA-Z_][a-zA-Z0-9_]*)\\s*(\\[(\\d+)(\\:(\\d+))?\\])?\\s*(\\(|$)",
|
||||
@ -649,7 +692,7 @@
|
||||
"name": "support.class"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.module.verilog"
|
||||
"name": "entity.name.function.module.verilog"
|
||||
},
|
||||
"4": {
|
||||
"name": "constant.numeric.verilog"
|
||||
@ -676,7 +719,7 @@
|
||||
"include": "#constants"
|
||||
}
|
||||
],
|
||||
"name": "variable.module.inst.verilog"
|
||||
"name": "variable.other.constant.module.inst.verilog"
|
||||
},
|
||||
{
|
||||
"name": "meta.struct.assign.verilog",
|
||||
@ -1047,7 +1090,7 @@
|
||||
"match": "\\.([a-zA-Z_][a-zA-Z0-9_]*)\\s*",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.inst.port.verilog"
|
||||
"name": "variable.inst.port.verilog"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user