finish vlog completion | definition | hover | docSymbol | formatter

This commit is contained in:
锦恢 2023-06-29 19:38:42 +08:00
parent b16f9bf104
commit 4fd79de4e4
37 changed files with 2667 additions and 1108 deletions

View File

@ -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
View File

@ -13,6 +13,7 @@
"showdown": "^2.1.0", "showdown": "^2.1.0",
"state-machine-cat": "^9.2.5", "state-machine-cat": "^9.2.5",
"temp": "^0.9.4", "temp": "^0.9.4",
"vscode-textmate": "^9.0.0",
"wavedrom": "^2.9.1" "wavedrom": "^2.9.1"
}, },
"devDependencies": { "devDependencies": {
@ -2829,6 +2830,11 @@
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==", "integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==",
"deprecated": "no longer supported" "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": { "node_modules/wavedrom": {
"version": "2.9.1", "version": "2.9.1",
"resolved": "https://registry.npmmirror.com/wavedrom/-/wavedrom-2.9.1.tgz", "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", "resolved": "https://registry.npmmirror.com/viz.js/-/viz.js-1.8.2.tgz",
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==" "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": { "wavedrom": {
"version": "2.9.1", "version": "2.9.1",
"resolved": "https://registry.npmmirror.com/wavedrom/-/wavedrom-2.9.1.tgz", "resolved": "https://registry.npmmirror.com/wavedrom/-/wavedrom-2.9.1.tgz",

View File

@ -129,7 +129,52 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "run the simulation command in terminal instead of output" "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
}
} }
}, },
"commands": [ "commands": [
@ -749,6 +794,7 @@
"showdown": "^2.1.0", "showdown": "^2.1.0",
"state-machine-cat": "^9.2.5", "state-machine-cat": "^9.2.5",
"temp": "^0.9.4", "temp": "^0.9.4",
"vscode-textmate": "^9.0.0",
"wavedrom": "^2.9.1" "wavedrom": "^2.9.1"
} }
} }

8
resources/formatter/index.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
import * as vscode from 'vscode';
declare module formatterProvider {
export const hdlFormatterProvider: vscode.DocumentFormattingEditProvider;
}
export = formatterProvider;

View 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 };

View 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;
}

File diff suppressed because one or more lines are too long

View File

@ -12,10 +12,7 @@ interface Fast {
} }
interface All { interface All {
content: { content: RawSymbol[]
error: string[]
symbols: RawSymbol[]
}
languageId: HdlLangID languageId: HdlLangID
macro: Macro macro: Macro
} }

View File

@ -546,7 +546,7 @@
}, },
"include": { "include": {
"prefix": "inc", "prefix": "include",
"body": [ "body": [
"`include \"$1\"" "`include \"$1\""
], ],
@ -554,9 +554,9 @@
}, },
"define": { "define": {
"prefix": "def", "prefix": "define",
"body": [ "body": [
"`def $1 = $2" "`define $1 $2"
], ],
"description": "`define var = val" "description": "`define var = val"
}, },
@ -902,5 +902,23 @@
"}", "}",
"*/" "*/"
] ]
},
"dumpfile": {
"prefix": "$dumpfile",
"body": [
"\\$dumpfile(\"$1\");"
]
},
"dumpvars": {
"prefix": "$dumpvars",
"body": [
"\\$dumpvars;"
]
},
"finish": {
"prefix": "$finish",
"body": [
"\\$finish;"
]
} }
} }

View File

@ -3,17 +3,19 @@ import * as vscode from 'vscode';
import { opeParam, MainOutput, ReportType } from './global'; import { opeParam, MainOutput, ReportType } from './global';
import { hdlParam } from './hdlParser'; import { hdlParam } from './hdlParser';
import { prjManage, registerManagerCommands } from './manager'; import { prjManage, registerManagerCommands } from './manager';
import { registerFunctionCommands } from './function'; import { registerFunctionCommands, registerLsp } from './function';
async function registerCommand(context: vscode.ExtensionContext) { async function registerCommand(context: vscode.ExtensionContext) {
registerFunctionCommands(context); registerFunctionCommands(context);
registerManagerCommands(context); registerManagerCommands(context);
registerLsp(context);
} }
async function launch(context: vscode.ExtensionContext) { async function launch(context: vscode.ExtensionContext) {
await prjManage.initialise(context); await prjManage.initialise(context);
await registerCommand(context); await registerCommand(context);
MainOutput.report('Digital-IDE has launched, Version: 0.3.0'); MainOutput.report('Digital-IDE has launched, Version: 0.3.0');
MainOutput.report('OS: ' + opeParam.os); MainOutput.report('OS: ' + opeParam.os);
} }

View File

@ -4,13 +4,18 @@ import * as hdlDoc from './hdlDoc';
import * as sim from './sim'; import * as sim from './sim';
import * as treeView from './treeView'; 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) { function registerDocumentation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview); vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview);
hdlDoc.registerFileDocExport(context); hdlDoc.registerFileDocExport(context);
hdlDoc.registerProjectDocExport(context); hdlDoc.registerProjectDocExport(context);
} }
function registerSimulation(context: vscode.ExtensionContext) { function registerSimulation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.tool.instance', sim.instantiation); vscode.commands.registerCommand('digital-ide.tool.instance', sim.instantiation);
vscode.commands.registerCommand('digital-ide.tool.testbench', sim.testbench); 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 { export {
registerFunctionCommands registerFunctionCommands,
registerLsp
}; };

View File

@ -0,0 +1,8 @@
import { vlogCompletionProvider, vlogIncludeCompletionProvider, vlogMacroCompletionProvider, vlogPositionPortProvider } from './vlog';
export {
vlogCompletionProvider,
vlogIncludeCompletionProvider,
vlogMacroCompletionProvider,
vlogPositionPortProvider
};

View 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
};

View File

@ -0,0 +1,5 @@
import { vlogDefinitionProvider } from './vlog';
export {
vlogDefinitionProvider
};

View 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
};

View File

@ -0,0 +1,6 @@
import { vlogDocSymbolProvider } from './vlog';
export {
vlogDocSymbolProvider
};

View File

@ -1,37 +1,36 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { AllowNull } from '../../../global';
import { HdlSymbol } from '../../../hdlParser'; 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 { 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[]> { if (!vlogAll || !vlogAll.content) {
const code = document.getText();
const symbolResult = HdlSymbol.all(code);
const symbols = symbolResult.symbols;
if (!symbols) {
return []; return [];
} } else {
try { const symbols = vlogAll.content;
const symbolInfos = this.makeSymbolInfos(document, symbols); const symbolInfos = this.makeDocumentSymbols(document, symbols);
return symbolInfos; return symbolInfos;
} catch (err) {
console.log(err);
return [];
} }
} }
/**
* private makeDocumentSymbols(document: vscode.TextDocument, symbols: RawSymbol[]): vscode.DocumentSymbol[] {
* @param {vscode.TextDocument} document const docSymbols = [];
* @param {Array<SymbolResult>} symbols
* @returns {Array<vscode.DocumentSymbol>}
*/
makeSymbolInfos(document: vscode.TextDocument, symbols: SymbolResult[]) {
let docSymbols = [];
const visitedSymbols = new Set(); const visitedSymbols = new Set();
const moduleSymbols = symbols.filter(symbol => { const moduleSymbols = symbols.filter(symbol => {
if (symbol.type == 'module') { if (symbol.type === 'module') {
visitedSymbols.add(symbol); visitedSymbols.add(symbol);
return true; return true;
} }
@ -41,38 +40,43 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
for (const moduleSymbol of moduleSymbols) { for (const moduleSymbol of moduleSymbols) {
const moduleName = moduleSymbol.name; const moduleName = moduleSymbol.name;
const moduleKind = this.getSymbolKind(moduleSymbol.type); 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, const moduleDocSymbol = new vscode.DocumentSymbol(moduleName,
moduleName, moduleName,
moduleKind, moduleKind,
moduleRange, moduleRange,
moduleRange); moduleRange);
docSymbols.push(moduleDocSymbol); docSymbols.push(moduleDocSymbol);
let paramContainer = { const paramContainer: DocSymbolContainer = {
docSymbol: null, docSymbol: null,
range: null range: null
}; };
let portContainer = { const portContainer: DocSymbolContainer = {
docSymbol: null, docSymbol: null,
range: null range: null
}; };
const portTypes = ['input', 'inout', 'output'];
// make others in module inner // make others in module inner
for (const symbol of symbols) { for (const symbol of symbols) {
if (visitedSymbols.has(symbol)) { if (visitedSymbols.has(symbol)) {
continue; continue;
} }
if (!(positionAfterEqual(symbol.start, moduleSymbol.start) && if (!(positionAfterEqual(symbol.range.start, moduleSymbol.range.start) &&
positionAfterEqual(moduleSymbol.end, symbol.end))) { positionAfterEqual(moduleSymbol.range.end, symbol.range.end))) {
continue; continue;
} }
if (!symbol.name) { if (!symbol.name) {
symbol.name = '???'; symbol.name = '???';
} }
visitedSymbols.add(symbol); 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) { if (!paramContainer.range) {
paramContainer.range = symbolRange; paramContainer.range = symbolRange;
paramContainer.docSymbol = new vscode.DocumentSymbol('param', paramContainer.docSymbol = new vscode.DocumentSymbol('param',
@ -87,9 +91,9 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
vscode.SymbolKind.Constant, vscode.SymbolKind.Constant,
symbolRange, symbolRange,
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) { if (!portContainer.range) {
portContainer.range = symbolRange; portContainer.range = symbolRange;
portContainer.docSymbol = new vscode.DocumentSymbol('port', portContainer.docSymbol = new vscode.DocumentSymbol('port',
@ -105,7 +109,7 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
vscode.SymbolKind.Interface, vscode.SymbolKind.Interface,
symbolRange, symbolRange,
symbolRange); symbolRange);
portContainer.docSymbol.children.push(portDocSymbol); portContainer.docSymbol?.children.push(portDocSymbol);
} else { } else {
const symbolKind = this.getSymbolKind(symbol.type); const symbolKind = this.getSymbolKind(symbol.type);
const symbolDocSymbol = new vscode.DocumentSymbol(symbol.name, const symbolDocSymbol = new vscode.DocumentSymbol(symbol.name,
@ -122,8 +126,8 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
} }
getSymbolKind(name) { getSymbolKind(name: string): vscode.SymbolKind {
if (name.indexOf('[') != -1) { if (name.indexOf('[') !== -1) {
return vscode.SymbolKind.Array; return vscode.SymbolKind.Array;
} }
switch (name) { switch (name) {
@ -176,12 +180,11 @@ class VlogDocSymbolProvider implements vscode.DocumentSymbolProvider {
case 'bit': return vscode.SymbolKind.Boolean; case 'bit': return vscode.SymbolKind.Boolean;
default: return vscode.SymbolKind.Event; 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
};

View File

@ -0,0 +1,5 @@
import { vlogHoverProvider } from './vlog';
export {
vlogHoverProvider
};

View 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
};

View File

@ -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
};

View File

@ -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 = []; 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 content = '';
let is_b_comment = false; 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 path
* @param range * @param range
*/ */
async function getSymbolComment(path: AbsPath, range: Range) { async function getSymbolComment(path: AbsPath, range: Range): Promise<string | null> {
let languageId = hdlFile.getLanguageId(path); const languageId = hdlFile.getLanguageId(path);
const uri = vscode.Uri.file(path); const uri = vscode.Uri.file(path);
const documentPromise = vscode.workspace.openTextDocument(uri); const documentPromise = vscode.workspace.openTextDocument(uri);
@ -229,14 +229,15 @@ async function getSymbolComment(path: AbsPath, range: Range) {
const nonblank = /\S+/g; const nonblank = /\S+/g;
const l_comment = getCommentUtilByLanguageId(languageId); const l_comment = getCommentUtilByLanguageId(languageId);
if (l_comment) { if (l_comment) {
let l_comment_symbol = l_comment.l_comment_symbol; const l_comment_symbol = l_comment.l_comment_symbol;
let l_comment_regExp = l_comment.l_comment_regExp; const l_comment_regExp = l_comment.l_comment_regExp;
// add definition first // add definition first
const document = await documentPromise; 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 commentPromises = [];
const comments = []; const comments = [];
for (const range of ranges) { 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); commentPromises.push(commentP);
} }

View File

@ -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
};

View 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,
};

View 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
};

View File

@ -8,6 +8,8 @@ import * as Lang from './lang';
type AbsPath = string; type AbsPath = string;
type RelPath = string; type RelPath = string;
type AllowNull<T> = T | null;
export { export {
opeParam, opeParam,
OpeParamDefaults, OpeParamDefaults,
@ -19,5 +21,6 @@ export {
RelPath, RelPath,
MainOutput, MainOutput,
YosysOutput, YosysOutput,
ReportType ReportType,
AllowNull
}; };

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import * as vscode from 'vscode';
import { AbsPath, RelPath } from '../global'; import { AbsPath, RelPath } from '../global';
@ -10,6 +11,10 @@ interface Position {
character: number character: number
}; };
function makeVscodePosition(position: Position): vscode.Position {
return new vscode.Position(position.line, position.character);
}
interface Range { interface Range {
start: Position start: Position
end: Position end: Position
@ -32,11 +37,45 @@ enum HdlFileType {
RemoteLib = 'remote_lib' RemoteLib = 'remote_lib'
}; };
enum InstModPathStatus {Current, Include, Others, Unknown}; enum InstModPathStatus {Current, Include, Others, Unknown};
enum SymbolType { // enum SymbolType {
Module = 'module', // Module = 'module',
Input = 'input', // Input = 'input',
Output = 'output' // 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 { interface Error {
severity: number severity: number
@ -45,12 +84,30 @@ interface Error {
range: Range range: Range
}; };
interface Define { interface DefineParam {
// `define A out name: string,
// name is "A", value is "out"
name: string
value: 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 { interface Include {
@ -105,8 +162,9 @@ interface RawHdlModule {
interface RawSymbol { interface RawSymbol {
name: string name: string
type: SymbolType type: string
range: Range range: Range
width?: string
}; };
interface InstModPathSearchResult { interface InstModPathSearchResult {
@ -120,6 +178,12 @@ interface HdlDependence {
others: AbsPath[] others: AbsPath[]
}; };
interface CommentResult {
start: { line : number }
length: number
}
export { export {
Position, Position,
Range, Range,
@ -138,5 +202,7 @@ export {
RawHdlModule, RawHdlModule,
InstModPathSearchResult, InstModPathSearchResult,
HdlDependence, HdlDependence,
RawSymbol RawSymbol,
CommentResult,
makeVscodePosition
}; };

View File

@ -72,10 +72,6 @@ class HdlParam {
this.modules.add(hdlModule); this.modules.add(hdlModule);
} }
/**
* add module to global top modules
* @param hdlModule
*/
public addTopModule(hdlModule: HdlModule) { public addTopModule(hdlModule: HdlModule) {
this.topModules.add(hdlModule); this.topModules.add(hdlModule);
} }
@ -204,13 +200,19 @@ class HdlParam {
} }
public async initHdlFiles(hdlFiles: AbsPath[] | Generator<AbsPath>) { public async initHdlFiles(hdlFiles: AbsPath[] | Generator<AbsPath>) {
const pools: Promise<void>[] = [];
for (const path of hdlFiles) { 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>) { public async initialize(hdlFiles: AbsPath[] | Generator<AbsPath>) {
await this.initHdlFiles(hdlFiles); await this.initHdlFiles(hdlFiles);
for (const hdlFile of this.getAllHdlFiles()) { for (const hdlFile of this.getAllHdlFiles()) {
hdlFile.makeInstance(); hdlFile.makeInstance();
} }
@ -296,6 +298,7 @@ class HdlInstance {
if (instModPath) { if (instModPath) {
this.module = hdlParam.getHdlModule(instModPath, instModName); this.module = hdlParam.getHdlModule(instModPath, instModName);
// add refer for module // add refer for module
this.module?.addGlobalReferedInstance(this); this.module?.addGlobalReferedInstance(this);
// if module and parent module share the same source (e.g both in src folder) // 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.params = params;
this.ports = ports; this.ports = ports;
if (!this.params) {
this.params = [];
}
if (!this.ports) {
this.ports = [];
}
// make instance // make instance
this.rawInstances = instances; this.rawInstances = instances;
if (!this.rawInstances) {
this.rawInstances = [];
}
this.nameToInstances = new Map<string, HdlInstance>(); this.nameToInstances = new Map<string, HdlInstance>();
// add in hdlParam data structure // add in hdlParam data structure
@ -397,6 +411,7 @@ class HdlModule {
public createHdlInstance(rawHdlInstance: common.RawHdlInstance): HdlInstance { public createHdlInstance(rawHdlInstance: common.RawHdlInstance): HdlInstance {
const instModName = rawHdlInstance.type; const instModName = rawHdlInstance.type;
const searchResult = this.searchInstModPath(instModName); const searchResult = this.searchInstModPath(instModName);
const hdlInstance = new HdlInstance(rawHdlInstance.name, const hdlInstance = new HdlInstance(rawHdlInstance.name,
rawHdlInstance.type, rawHdlInstance.type,
@ -417,6 +432,7 @@ class HdlModule {
} }
public makeNameToInstances() { public makeNameToInstances() {
if (this.rawInstances) { if (this.rawInstances) {
this.nameToInstances.clear(); this.nameToInstances.clear();
for (const inst of this.rawInstances) { for (const inst of this.rawInstances) {
@ -508,6 +524,7 @@ class HdlModule {
public addGlobalReferedInstance(inst: HdlInstance) { public addGlobalReferedInstance(inst: HdlInstance) {
const globalRefers = this.globalRefers; const globalRefers = this.globalRefers;
globalRefers.add(inst); globalRefers.add(inst);
// it is refered in global scope, so delete this from top module
if (globalRefers.size > 0) { if (globalRefers.size > 0) {
hdlParam.deleteTopModule(this); hdlParam.deleteTopModule(this);
} }
@ -524,6 +541,7 @@ class HdlModule {
public addLocalReferedInstance(inst: HdlInstance) { public addLocalReferedInstance(inst: HdlInstance) {
const localRefers = this.localRefers; const localRefers = this.localRefers;
localRefers.add(inst); localRefers.add(inst);
// it is refered in local scope, so delete this from top module
if (localRefers.size > 0) { if (localRefers.size > 0) {
hdlParam.deleteTopModuleToSource(this); hdlParam.deleteTopModuleToSource(this);
} }

View File

@ -25,6 +25,8 @@ namespace HdlSymbol {
} }
} }
export { export {
HdlSymbol HdlSymbol,
}; };

View File

@ -49,7 +49,7 @@ class PrjManage {
* init opeParam * init opeParam
* @param context * @param context
*/ */
public initOpeParam(context: vscode.ExtensionContext) { public async initOpeParam(context: vscode.ExtensionContext) {
const os = process.platform; const os = process.platform;
const extensionPath = hdlPath.toSlash(context.extensionPath); const extensionPath = hdlPath.toSlash(context.extensionPath);
const workspacePath = this.getWorkspacePath(); const workspacePath = this.getWorkspacePath();
@ -71,6 +71,15 @@ class PrjManage {
if (fs.existsSync(propertyJsonPath)) { if (fs.existsSync(propertyJsonPath)) {
const rawPrjInfo = hdlFile.readJSON(propertyJsonPath) as RawPrjInfo; const rawPrjInfo = hdlFile.readJSON(propertyJsonPath) as RawPrjInfo;
opeParam.mergePrjInfo(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'); console.time('launch');
} }
this.initOpeParam(context); await this.initOpeParam(context);
MainOutput.report('finish initialise opeParam', ReportType.Info); MainOutput.report('finish initialise opeParam', ReportType.Info);
const hdlFiles = this.getPrjHardwareFiles(); const hdlFiles = this.getPrjHardwareFiles();
@ -121,7 +130,6 @@ class PrjManage {
MainOutput.report('create pl and ps', ReportType.Info); MainOutput.report('create pl and ps', ReportType.Info);
if (countTimeCost) { if (countTimeCost) {
console.timeLog('launch'); console.timeLog('launch');
} }

View File

@ -1,6 +1,8 @@
module dependence_1 ( module dependence_1 (
// this is a test
input a, b, c, input a, b, c,
output Q // a test
output Result // balabalabala for result
); );
// a & b | ((b & c) & (b | c)) // a & b | ((b & c) & (b | c))
@ -10,6 +12,6 @@ module dependence_1 (
// Simplify A + A = A AB + BC // Simplify A + A = A AB + BC
// Factor B(A+C) // Factor B(A+C)
assign Q = a & (b | c); assign Result = a & (b | c);
endmodule endmodule

View File

@ -3,14 +3,17 @@
* current file -> macro include -> whole project * current file -> macro include -> whole project
* expect dependence_1 from child_1.v (macro include) * expect dependence_1 from child_1.v (macro include)
* expect dependence_2 from child_2.v (whole project) * expect dependence_2 from child_2.v (whole project)
* cannot find dependence_3 * cannot find dependence_3 `main
*/ */
`include "child_1.v" `include "child_1.v"
`include "child_2.v"
`define main out `define main out
module Main ( module Main (
// Main input
input a, b, c, input a, b, c,
// Main output
output Qus, Qs, `main output Qus, Qs, `main
); );
@ -18,7 +21,7 @@ dependence_1 u_dependence_1(
.a(a), .a(a),
.b(b), .b(b),
.c(c), .c(c),
.Q(Qus) .Result(Qus)
); );
dependence_2 u_dependence_2( dependence_2 u_dependence_2(

View File

@ -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

View File

@ -6,6 +6,49 @@
"keyEquivalent": "^~V", "keyEquivalent": "^~V",
"name": "Verilog", "name": "Verilog",
"patterns": [ "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)?", "begin": "\\s*\\b(function|task)\\b(\\s+automatic)?",
"beginCaptures": { "beginCaptures": {
@ -145,10 +188,10 @@
"name": "support.class" "name": "support.class"
} }
}, },
"end": ";", "end": "(;)",
"endCaptures": { "endCaptures": {
"1": { "1": {
"name": "entity.name.function.verilog" "name": "source.verilog"
} }
}, },
"patterns": [ "patterns": [
@ -166,7 +209,7 @@
"include": "#ifmodport" "include": "#ifmodport"
} }
], ],
"name": "variable.module.verilog" "name": "variable.other.constant.module.verilog"
}, },
{ {
"captures": { "captures": {
@ -637,10 +680,10 @@
}, },
{ {
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b(?=\\s*(\\(|$))", "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*(\\(|$)", "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" "name": "support.class"
}, },
"2": { "2": {
"name": "entity.name.module.verilog" "name": "entity.name.function.module.verilog"
}, },
"4": { "4": {
"name": "constant.numeric.verilog" "name": "constant.numeric.verilog"
@ -676,7 +719,7 @@
"include": "#constants" "include": "#constants"
} }
], ],
"name": "variable.module.inst.verilog" "name": "variable.other.constant.module.inst.verilog"
}, },
{ {
"name": "meta.struct.assign.verilog", "name": "meta.struct.assign.verilog",
@ -1047,7 +1090,7 @@
"match": "\\.([a-zA-Z_][a-zA-Z0-9_]*)\\s*", "match": "\\.([a-zA-Z_][a-zA-Z0-9_]*)\\s*",
"captures": { "captures": {
"1": { "1": {
"name": "keyword.inst.port.verilog" "name": "variable.inst.port.verilog"
} }
} }
}, },