finish testbench, instance | finish doc | move all the none-ts file to resources

This commit is contained in:
锦恢 2023-02-11 01:10:45 +08:00
parent a02620cf53
commit 01f44548ea
42 changed files with 9249 additions and 4246 deletions

View File

@ -1,71 +1,4 @@
# digital-ide README
# Digital-IDE
This is the README for your extension "digital-ide". After writing up a brief description, we recommend including the following sections.
![](https://img.shields.io/badge/version-0.3.0-blue)
## Features
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
For example if there is an image subfolder under your extension project workspace:
\!\[feature X\]\(images/feature-x.png\)
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
## Requirements
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
## Extension Settings
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
For example:
This extension contributes the following settings:
* `myExtension.enable`: Enable/disable this extension.
* `myExtension.thing`: Set to `blah` to do something.
## Known Issues
Calling out known issues can help limit users opening duplicate issues against your extension.
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of ...
### 1.0.1
Fixed issue #.
### 1.1.0
Added features X, Y, and Z.
---
## Following extension guidelines
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
## Working with Markdown
You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux).
* Toggle preview (`Shift+Cmd+V` on macOS or `Shift+Ctrl+V` on Windows and Linux).
* Press `Ctrl+Space` (Windows, Linux, macOS) to see a list of Markdown snippets.
## For more information
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
**Enjoy!**

View File

@ -0,0 +1,178 @@
:root {
--title-font : "Cascadia code", "Microsoft YaHei", "Times", serif;
--base-font : "Roboto Slab", "Microsoft YaHei", serif;
--monospace: "Cascadia code", "Microsoft YaHei", "Courier New", monospace;
--main-font-size : 15px;
}
.error-out {
display: flex;
justify-content: center;
align-items: center;
}
.error {
color: red;
}
@font-face {
font-family: "Roboto Slab";
src: url("../font/RobotoSlab-Regular-2.ttf");
}
@font-face {
font-family: "Cascadia code";
src: url("../font/Cascadia-Code-Regular-2.ttf");
}
@font-face {
font-family: "Open Sans";
src: url("../font/Open-Sans-2.ttf");
}
@font-face {
font-family: "Glow Sans";
src: url("../font/GlowSansSC-Normal-Book.woff2") format('woff2');
}
body {
font-family: "Roboto Slab", "SimHei", serif;
/* background-color: rgb(255, 255, 255); */
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
html {
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
text-rendering: optimizelegibility;
-webkit-font-smoothing: initial;
}
.octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
img {
border-style: none;
}
#write h1, h2, h3, h4, h5, h6 {
font-family: var(--title-font);
font-weight: 800;
line-height: 1.5;
margin: 0 0 1em 0;
}
#write h1 {
font-size: 2em;
margin: 0.67em 0;
}
#write {
position: static;
/* width: 90%; */
max-width: 1000px;
min-height: calc(100vh - 6rem);
min-width: calc(100vw - 45rem);
line-height: 1.6;
transform: none;
height: auto;
caret-color: var(--main-color);
}
#write h2 {
font-size: 28px;
border-radius: .5em;
width: fit-content;
}
#write h3 {
font-size: 24px;
border-radius: .3em;
width: fit-content;
}
#write h4 {
font-size: 20px;
}
#write h5 {
font-size: 16px;
}
#write hr {
width: 60%;
text-align:center;
margin: 4.5em auto;
}
/* table */
#write table
{
width: 95%;
border-collapse: collapse;
text-align: center;
font-family: var(--monospace);
margin: 20px;
}
#write table td, table th
{
border: 1px solid;
padding: 5px;
border-radius: .5em;
}
#write table td {
font-family: var(--title-font);
}
#write table thead th
{
font-size: 16px;
font-weight: bolder;
text-align: center;
vertical-align: middle;
padding: 10px;
}
/* table tr:nth-child(odd)
{
background: white;
}
table tr:nth-child(even)
{
background: var(--light-color);
} */
/* main element consisting of your text */
p {
margin: 0 0 2em 0;
font-weight: 500;
line-height: 1.6;
font-size: var(--main-font-size);
font-family: var(--base-font);
}
a {
font-weight: 500;
text-decoration: none;
text-decoration-style: none;
cursor: pointer;
padding: 0 3px 0 3px;
}
/* list */
ol, ul {
font-size: var(--main-font-size);
padding-left: 2em;
line-height: 2;
font-family: var(--base-font);
}

520
css/documentation.css Normal file
View File

@ -0,0 +1,520 @@
:root {
--dark-main-color : #df733d;
--light-main-color : #cc6633;
}
body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
background-attachment: fixed;
background-repeat: no-repeat;
background-size: cover;
-webkit-background-size: cover;
-o-background-size: cover;
background-position: center 0;
background-image: url(--backgroundImage);
}
.octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
#wrapper {
justify-content: center;
display: flex;
}
#write {
padding: 15px 30px;
width: 1000px;
}
.ImgCaption {
padding-top: 0;
margin-top: 7px;
width: fit-content;
}
.vscode-dark .ImgCaption {
border-bottom: 2px solid var(--dark-main-color);
color: white;
}
.vscode-light .ImgCaption {
border-bottom: 2px solid var(--light-main-color);
color: black;
}
a:active,
a:hover {
outline-width: 0;
}
strong {
font-weight: inherit;
}
strong {
font-weight: bolder;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
.error-out {
display: flex;
justify-content: center;
align-items: center;
}
.error {
color: rgb(227, 60, 60);
border-radius: 1em;
border: 1.5px solid rgb(227, 60, 60);
padding: 10px 20px;
}
code,
kbd,
pre {
font-family: monospace, monospace;
font-size: 1em;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
input {
font: inherit;
margin: 0;
}
input {
overflow: visible;
}
[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
* {
box-sizing: border-box;
}
input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
a {
color: #4078c0;
text-decoration: none;
}
a:hover,
a:active {
text-decoration: underline;
}
strong {
font-weight: 600;
}
hr {
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid;
}
hr::before {
display: table;
content: "";
}
hr::after {
display: table;
clear: both;
content: "";
}
table {
border-spacing: 0;
border-collapse: collapse;
}
td,
th {
padding: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0;
}
h1 {
font-size: 32px;
font-weight: 600;
}
h2 {
font-size: 24px;
font-weight: 600;
}
h3 {
font-size: 20px;
font-weight: 600;
}
h4 {
font-size: 16px;
font-weight: 600;
}
h5 {
font-size: 14px;
font-weight: 600;
}
h6 {
font-size: 12px;
font-weight: 600;
}
p {
margin-top: 0;
margin-bottom: 10px;
}
blockquote {
margin: 0;
}
ul,
ol {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
ol ol,
ul ol {
list-style-type: lower-roman;
}
ul ul ol,
ul ol ol,
ol ul ol,
ol ol ol {
list-style-type: lower-alpha;
}
dd {
margin-left: 0;
}
code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
pre {
margin-top: 0;
margin-bottom: 0;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
.octicon {
vertical-align: text-bottom;
}
input {
-webkit-font-feature-settings: "liga" 0;
font-feature-settings: "liga" 0;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
a:not([href]) {
color: inherit;
text-decoration: none;
}
.anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.anchor:focus {
outline: none;
}
p,
blockquote,
ul,
ol,
dl,
table,
pre {
margin-top: 0;
margin-bottom: 16px;
}
hr {
height: 0.25em;
padding: 0;
margin: 24px 0;
border: 0;
}
blockquote {
padding: 0 1em;
}
blockquote>:first-child {
margin-top: 0;
}
blockquote>:last-child {
margin-bottom: 0;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
vertical-align: middle;
border-radius: 3px;
}
#write h1,
#write h2,
#write h3,
#write h4,
#write h5,
#write h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.vscode-dark h1 {
color: #eee;
border-bottom: 3px solid #df733d;
width: fit-content;
margin: 0 0 1.0em 0;
line-height: 1.3;
}
.vscode-dark h2 {
color: #eee;
}
.vscode-dark h2::before {
content: 'H2';
border-radius: .3em;
font-size: .8em;
padding: 3px 7px;
margin-right: 15px;
color: #eee;
background-color: #df733d;
}
.vscode-light h1 {
color: #000;
border-bottom: 3px solid #cc6633;
width: fit-content;
margin: 0 0 1.0em 0;
line-height: 1.3;
}
.vscode-light h2 {
color: #000;
}
.vscode-light h2::before {
content: 'H2';
border-radius: .3em;
font-size: .8em;
padding: 3px 7px;
margin-right: 15px;
color: #eee;
background-color: #cc6633;
}
h1 {
padding-bottom: 0.3em;
font-size: 2em;
}
h2 {
padding-bottom: 0.3em;
font-size: 1.5em;
}
h3 {
font-size: 1.25em;
}
h4 {
font-size: 1em;
}
h5 {
font-size: 0.875em;
}
h6 {
font-size: 0.85em;
}
ul,
ol {
padding-left: 2em;
}
ul ul,
ul ol,
ol ol,
ol ul {
margin-top: 0;
margin-bottom: 0;
}
li>p {
margin-top: 16px;
}
li+li {
margin-top: 0.25em;
}
/*
table {
display: block;
width: 100%;
overflow: auto;
}
table th {
font-weight: bold;
}
table th,
table td {
padding: 6px 13px;
border: .7px solid;
}
table tr {
border-top: .7px solid;
} */
img {
max-width: 100%;
box-sizing: content-box;
}
#write table
{
width: 95%;
border-collapse: collapse;
text-align: center;
margin: 20px;
max-width: 750px;
}
#write table td, table th
{
border: 1px solid transparent;
padding: 12px 10px;
border-radius: .5em;
word-wrap: break-word;
}
#write table thead th
{
background-color: var(--dark-main-color);
font-size: 20px;
font-weight: bolder;
width: 100px;
text-align: center;
vertical-align: middle;
padding: 10px;
}
.vscode-dark table thead th {
color: white;
}
.vscode-dark table td, table th {
color: rgb(234, 231, 231);
}
.vscode-light table thead th {
color: white;
}
.vscode-light table td, table th {
color: rgb(16, 16, 16);
}
.vscode-dark table tr:nth-child(even)
{
background: #20242b;
}
.vscode-light table tr:nth-child(even)
{
background: #e7e1e1;
}

128
images/icons/iconfont.json Normal file
View File

@ -0,0 +1,128 @@
{
"id": "3826523",
"name": "draft",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "7569441",
"name": "天气-未知",
"font_class": "help-unknown",
"unicode": "e62a",
"unicode_decimal": 58922
},
{
"icon_id": "6989868",
"name": "module-fill",
"font_class": "module-fill",
"unicode": "e622",
"unicode_decimal": 58914
},
{
"icon_id": "6989870",
"name": "module",
"font_class": "module",
"unicode": "e623",
"unicode_decimal": 58915
},
{
"icon_id": "1126",
"name": "文件夹",
"font_class": "wenjianjia",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "1478582",
"name": "ic_input",
"font_class": "ic_input",
"unicode": "e908",
"unicode_decimal": 59656
},
{
"icon_id": "17397810",
"name": "output",
"font_class": "output",
"unicode": "e7b0",
"unicode_decimal": 59312
},
{
"icon_id": "18171148",
"name": "交换,双箭头,平行",
"font_class": "exchange-full",
"unicode": "ea19",
"unicode_decimal": 59929
},
{
"icon_id": "13553191",
"name": "markdown-line",
"font_class": "markdown-line",
"unicode": "ee68",
"unicode_decimal": 61032
},
{
"icon_id": "10168298",
"name": "html-1",
"font_class": "html-",
"unicode": "e633",
"unicode_decimal": 58931
},
{
"icon_id": "16432310",
"name": "PDF",
"font_class": "PDF",
"unicode": "e684",
"unicode_decimal": 59012
},
{
"icon_id": "15378918",
"name": "v",
"font_class": "v",
"unicode": "ecf7",
"unicode_decimal": 60663
},
{
"icon_id": "23306741",
"name": "chip",
"font_class": "chip1",
"unicode": "e7a8",
"unicode_decimal": 59304
},
{
"icon_id": "31357424",
"name": "模块",
"font_class": "mokuai",
"unicode": "e60b",
"unicode_decimal": 58891
},
{
"icon_id": "3851337",
"name": "参数",
"font_class": "canshu",
"unicode": "e655",
"unicode_decimal": 58965
},
{
"icon_id": "11520228",
"name": "接口",
"font_class": "jiekou",
"unicode": "e638",
"unicode_decimal": 58936
},
{
"icon_id": "24698418",
"name": "chip",
"font_class": "chip",
"unicode": "e749",
"unicode_decimal": 59209
},
{
"icon_id": "13304138",
"name": "verilog",
"font_class": "verilog",
"unicode": "e6b3",
"unicode_decimal": 59059
}
]
}

BIN
images/icons/iconfont.woff2 Normal file

Binary file not shown.

9450
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,71 @@
"type": "string",
"default": "",
"description": "path of the dictionary of \"custom\" in library"
},
"function.doc.webview.backgroundImage": {
"type": "string",
"default": "",
"description": "url of the background image"
},
"function.doc.pdf.scale": {
"type": "number",
"default": 1,
"description": "scale of the exported pdf"
},
"function.doc.pdf.printBackground": {
"type": "boolean",
"default": true,
"description": "whether print background"
},
"function.doc.pdf.landscape": {
"type": "boolean",
"default": false,
"description": "whether export pdf as a landscape style"
},
"function.doc.pdf.format": {
"type": "string",
"default": "A4",
"description": "format of pdf size"
},
"function.doc.pdf.displayHeaderFooter": {
"type": "boolean",
"default": false,
"description": "display header and footer in the exported pdf"
},
"function.doc.pdf.browserPath": {
"type": "string",
"default": "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe",
"description": "the absolute path of edge or chrome, we need browser to render pdf"
},
"function.doc.pdf.margin.top": {
"type": "number",
"default": 0.5,
"description": "top margin of exported pdf, unit cm"
},
"function.doc.pdf.margin.right": {
"type": "number",
"default": 0.5,
"description": "top margin of exported pdf, unit cm"
},
"function.doc.pdf.margin.bottom": {
"type": "number",
"default": 0.5,
"description": "top margin of exported pdf, unit cm"
},
"function.doc.pdf.margin.left": {
"type": "number",
"default": 0.5,
"description": "top margin of exported pdf, unit cm"
},
"function.doc.pdf.headerTemplate": {
"type": "string",
"default": "<div style=\"font-size: 9px; margin-left: 1cm;\"> <span class='title'></span></div> <div style=\"font-size: 9px; margin-left: auto; margin-right: 1cm; \"> <span class='date'></span></div>",
"description": "html template of header, if displayHeaderFooter is set to false, this setting will be ignored"
},
"function.doc.pdf.footerTemplate": {
"type": "string",
"default": "<div style=\"font-size: 9px; margin-left: 1cm;\"> <span class='title'></span></div> <div style=\"font-size: 9px; margin-left: auto; margin-right: 1cm; \"> <span class='date'></span></div>",
"description": "<div style=\"font-size: 9px; margin: 0 auto;\"> <span class='pageNumber'></span> / <span class='totalPages'></span></div>"
}
}
},
@ -42,8 +107,60 @@
"command": "digital-ide.property-json.overwrite",
"title": "%digital-ide.property-json.overwrite.title%",
"category": "Digital-IDE"
},
{
"command": "digital-ide.hdlDoc.exportFile",
"title": "%digital-ide.hdlDoc.exportFile.title%",
"category": "Digital-IDE"
},
{
"command": "digital-ide.hdlDoc.exportProject",
"title": "%digital-ide.hdlDoc.exportProject.title%",
"category": "Digital-IDE"
},
{
"command": "digital-ide.hdlDoc.showWebview",
"title": "%digital-ide.hdlDoc.showWebview.title%",
"category": "Digital-IDE",
"icon": {
"light": "images/svg/light/documentation.svg",
"dark": "images/svg/dark/documentation.svg"
}
},
{
"command": "digital-ide.tool.instance",
"title": "%digital-ide.tool.instance.title%",
"category": "Digital-IDE"
},
{
"command": "digital-ide.tool.testbench",
"title": "%digital-ide.tool.testbench.title%",
"category": "Digital-IDE"
}
],
"menus": {
"editor/title": [
{
"when": "editorLangId == verilog || editorLangId == systemverilog || editorLangId == vhdl",
"command": "digital-ide.hdlDoc.showWebview",
"group": "navigation@4"
}
]
},
"keybindings": [
{
"command": "digital-ide.tool.instance",
"key": "alt+i",
"mac": "alt+i",
"when": "editorTextFocus"
},
{
"command": "digital-ide.tool.testbench",
"key": "alt+t",
"mac": "alt+t",
"when": "editorTextFocus"
}
],
"languages": [
{
"id": "tcl",
@ -188,7 +305,128 @@
"language": "systemverilog",
"path": "snippets/svlog.json"
}
]
],
"icons": {
"instance-verilog": {
"description": "icon of verilog in TOOL.instance",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e7a8"
}
},
"instance-vhdl": {
"description": "icon of verilog in TOOL.instance",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e6b3"
}
},
"instance-port": {
"description": "port of verilog in TOOL.instance",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e638"
}
},
"instance-param": {
"description": "param of verilog in TOOL.instance",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e655"
}
},
"instance-module": {
"description": "module of verilog in TOOL.instance",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e60b"
}
},
"instance-input": {
"description": "input",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e908"
}
},
"instance-output": {
"description": "output",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e7b0"
}
},
"instance-inout": {
"description": "inout",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\ea19"
}
},
"export-html": {
"description": "export html",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e633"
}
},
"export-markdown": {
"description": "export markdown",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\ee68"
}
},
"export-pdf": {
"description": "export pdf",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e684"
}
},
"libpick-folder": {
"description": "libpick folder",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e600"
}
},
"libpick-verilog": {
"description": "libpick verilog",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e7a8"
}
},
"libpick-vhdl": {
"description": "libpick vhdl",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e6b3"
}
},
"libpick-common": {
"description": "libpick common",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e622"
}
},
"libpick-custom": {
"description": "libpick custom",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e623"
}
},
"libpick-unknown": {
"description": "libpick unknown",
"default": {
"fontPath": "./images/icons/iconfont.woff2",
"fontCharacter": "\\e62a"
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
@ -199,16 +437,25 @@
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.74.0",
"@types/glob": "^8.0.0",
"@types/mocha": "^10.0.0",
"@types/node": "16.x",
"@types/showdown": "^2.0.0",
"@types/vscode": "^1.74.0",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"@vscode/test-electron": "^2.2.0",
"eslint": "^8.26.0",
"glob": "^8.0.3",
"mocha": "^10.1.0",
"typescript": "^4.8.4",
"@vscode/test-electron": "^2.2.0"
"typescript": "^4.8.4"
},
"dependencies": {
"chokidar": "^3.5.3",
"puppeteer-core": "^19.4.1",
"showdown": "^2.1.0",
"state-machine-cat": "^9.2.5",
"temp": "^0.9.4",
"wavedrom": "^2.9.1"
}
}

View File

@ -1,4 +1,9 @@
{
"digital-ide.property-json.generate.title": "generate property.json",
"digital-ide.property-json.overwrite.title": "overwrite property.json template"
"digital-ide.property-json.overwrite.title": "overwrite property.json template",
"digital-ide.hdlDoc.exportFile.title": "export the document of current file",
"digital-ide.hdlDoc.exportProject.title": "export the document of current project",
"digital-ide.hdlDoc.showWebview.title": "show the document of current file in a webview",
"digital-ide.tool.instance.title": "generate instance template from selected module",
"digital-ide.tool.testbench.title": "generate testbench template from current file"
}

View File

@ -1,4 +1,9 @@
{
"digital-ide.property-json.generate.title": "生成 property.json 配置文件",
"digital-ide.property-json.overwrite.title": "修改默认的 property.json 模板文件"
"digital-ide.property-json.overwrite.title": "修改默认的 property.json 模板文件",
"digital-ide.hdlDoc.exportFile.title": "导出当前文件的文档",
"digital-ide.hdlDoc.exportProject.title": "导出当前项目的文档",
"digital-ide.hdlDoc.showWebview.title": "在webview中展示文档",
"digital-ide.tool.instance.title": "生成选中module的例化模板",
"digital-ide.tool.testbench.title": "从当前文件中选择module生成testbench"
}

View File

@ -1,4 +1,9 @@
{
"digital-ide.property-json.generate.title": "",
"digital-ide.property-json.overwrite.title": ""
"digital-ide.property-json.overwrite.title": "",
"digital-ide.hdlDoc.exportFile.title": "",
"digital-ide.hdlDoc.exportProject.title": "",
"digital-ide.hdlDoc.showWebview.title": "",
"digital-ide.tool.instance.title": "",
"digital-ide.tool.testbench.title": ""
}

View File

@ -23,5 +23,9 @@ interface All {
macro: Macro
}
export function vlogFast(path: AbsPath): Promise<Fast>;
export function vlogAll(path: AbsPath): Promise<All>;
export function vlogFast(path: AbsPath): Promise<Fast | undefined>;
export function vlogAll(path: AbsPath): Promise<All | undefined>;
export function vhdlFast(path: AbsPath): Promise<Fast | undefined>;
export function vhdlAll(path: AbsPath): Promise<All | undefined>;
export function svFast(path: AbsPath): Promise<Fast | undefined>;
export function svAll(path: AbsPath): Promise<All | undefined>;

View File

@ -1,4 +1,5 @@
const hdlParser = require('./parser');
const fs = require('fs');
const _hdlParser = {
module: null,
@ -17,22 +18,60 @@ const _hdlParser = {
};
async function vlogFast(path) {
if (!fs.existsSync(path)) {
return undefined;
}
const wasmModule = await _hdlParser.acquire();
const source = fs.readFileSync(path, 'utf-8');
const source = fs.readFileSync(path, 'utf-8') + '\n';
wasmModule.FS.writeFile(_hdlParser.tempPath, source, { encoding: 'utf8' });
const res = wasmModule.ccall('vlog_fast', 'string', ['string'], [_hdlParser.tempPath]);
return JSON.parse(res);
}
async function vlogAll(path) {
if (!fs.existsSync(path)) {
return undefined;
}
const wasmModule = await _hdlParser.acquire();
const source = fs.readFileSync(path, 'utf-8');
const source = fs.readFileSync(path, 'utf-8') + '\n';
wasmModule.FS.writeFile(_hdlParser.tempPath, source, { encoding: 'utf8' });
const res = wasmModule.ccall('vlog_all', 'string', ['string'], [_hdlParser.tempPath]);
return JSON.parse(res);
}
async function vhdlFast(path) {
if (!fs.existsSync(path)) {
return undefined;
}
return {};
}
async function vhdlAll(path) {
if (!fs.existsSync(path)) {
return undefined;
}
return {};
}
async function svFast(path) {
if (!fs.existsSync(path)) {
return undefined;
}
return {};
}
async function svAll(path) {
if (!fs.existsSync(path)) {
return undefined;
}
return {};
}
module.exports = {
vlogFast,
vlogAll
vlogAll,
vhdlFast,
vhdlAll,
svFast,
svAll
};

7
resources/json5/index.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
// It seems that webpack cannot package the json5 correctly
declare module JSON5 {
export function parse(text: string): any;
}
export = JSON5;

1
resources/json5/index.js Normal file

File diff suppressed because one or more lines are too long

5
resources/wavedrom/index.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module Wavedrom {
export function renderWaveDrom(id: number, json: any, style: any): string;
}
export = Wavedrom;

View File

@ -0,0 +1,34 @@
/* eslint-disable @typescript-eslint/naming-convention */
const renderAny = require('wavedrom/lib/render-any');
const onmlStringify = require('onml/stringify.js');
const darkSkin = require('wavedrom/skins/dark');
const lightSkin = require('wavedrom/skins/default');
function selectSkin(skin) {
if (skin === 'dark') {
return darkSkin;
} else if (skin === 'light') {
return lightSkin;
}
return darkSkin;
}
/**
*
* @param {number} id
* @param {any} json
* @param {any} style 'dark' or 'light'
* @returns {string}
*/
function renderWaveDrom(id, json, style) {
const skin = selectSkin(style);
const renderObj = renderAny(id, json, skin);
const svgString = onmlStringify(renderObj);
return svgString;
}
const Wavedrom = {
renderWaveDrom
};
module.exports = Wavedrom;

View File

@ -1,18 +1,30 @@
import * as vscode from 'vscode';
import { opeParam } from './global';
import { opeParam, MainOutput, ReportType } from './global';
import { hdlParam } from './hdlParser';
import { prjManage } from './manager';
import { hdlPath } from './hdlFs';
function launch(context: vscode.ExtensionContext) {
import { registerAllCommands } from './function';
async function registerCommand(context: vscode.ExtensionContext) {
registerAllCommands(context);
}
async function launch(context: vscode.ExtensionContext) {
console.time('launch');
prjManage.initOpeParam(context);
console.log(opeParam.prjInfo);
const hdlFiles = prjManage.getPrjHardwareFiles();
await hdlParam.initialize(hdlFiles);
console.timeLog('launch');
await registerCommand(context);
MainOutput.report('Digital-IDE has launched, version: 0.3.0');
MainOutput.report('OS: ' + opeParam.os);
}
export function activate(context: vscode.ExtensionContext) {
console.log('Digital-IDE 0.3.0 is launched');
launch(context);
}
export function deactivate() {}

View File

@ -0,0 +1,372 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as readline from 'readline';
import * as JSON5 from '../../../resources/json5';
import * as Wavedrom from '../../../resources/wavedrom';
import * as showdown from 'showdown';
import { ThemeType } from '../../global/enum';
import { MainOutput, ReportType } from '../../global';
const Count = {
svgMakeTimes: 0
};
const converter = new showdown.Converter({
tables : true,
literalMidWordUnderscores : true,
strikethrough : true,
simpleLineBreaks : true
});
enum MarkdownTag {
Title = '#',
Quote = '>',
Bold = '**',
Italic = '*',
InlineCode = '`',
UnorderedList = '-'
};
enum MarkdownAlign { Left, Center, Right };
enum RenderType { Wavedrom, Markdown };
function getAlignSpliter(align: MarkdownAlign): string {
switch (align) {
case MarkdownAlign.Left: return ':---';
case MarkdownAlign.Center: return ':---:';
case MarkdownAlign.Right: return '---:';
default: return '';
}
}
function joinString(...strings: string[]): string {
return strings.join(' ');
}
function catString(...strings: string[]): string {
return strings.join('');
}
function getThemeColorKind(): ThemeType {
const currentColorKind = vscode.window.activeColorTheme.kind;
if (currentColorKind === vscode.ColorThemeKind.Dark ||
currentColorKind === vscode.ColorThemeKind.HighContrast) {
return ThemeType.Dark;
} else {
return ThemeType.Light;
}
}
abstract class BaseDoc {
value: string;
constructor(value: string) {
this.value = value;
}
};
class Text extends BaseDoc {
constructor(value: string) {
super(value);
}
};
class Title extends BaseDoc {
level: number;
constructor(value: string, level: number) {
super(value);
this.level = level;
const prefix = MarkdownTag.Title.repeat(level);
this.value = joinString(prefix, value);
}
};
class UnorderedList {
value: string;
constructor(values: string[]) {
this.value = '';
for (const v of values) {
this.value += joinString(MarkdownTag.UnorderedList, v, '\n');
}
}
};
class OrderedList {
value: string = '';
constructor(values: string[]) {
values.forEach((v, i) => {
const id = i + 1;
this.value += joinString(id + '.', v, '\n');
});
}
};
class Quote extends BaseDoc {
/**
* @description quote, tag > in markdown
* @param {string} value
*/
constructor(value: string) {
super(value);
this.value = joinString(MarkdownTag.Quote, value);
}
};
class Bold extends BaseDoc {
constructor(value: string) {
super(value);
this.value = catString(MarkdownTag.Bold, value, MarkdownTag.Bold);
}
};
class Italic extends BaseDoc {
constructor(value: string) {
super(value);
this.value = catString(MarkdownTag.Italic, value, MarkdownTag.Italic);
}
};
class InlineCode extends BaseDoc {
constructor(value: string) {
super(value);
this.value = catString(MarkdownTag.InlineCode, value, MarkdownTag.InlineCode);
}
}
class Split extends BaseDoc {
constructor() {
super('---');
}
};
class Table extends BaseDoc {
constructor(fieldNames: string[], rows: string[][], align: MarkdownAlign = MarkdownAlign.Left) {
const colNum = fieldNames.length;
const rowNum = rows.length;
const alignString = getAlignSpliter(align);
let value = catString('| ', fieldNames.join(' | '), ' |', '\n');
const alignUnit = catString('| ', alignString, ' ');
value += catString(alignUnit.repeat(colNum), '|', '\n');
for (let row = 0; row < rowNum; ++ row) {
const data = rows[row];
value += catString('| ', data.join(' | '), '|');
if (row < rowNum - 1) {
value += '\n';
}
}
super(value);
}
};
abstract class RenderString {
line: number;
type: RenderType;
constructor(line: number, type: RenderType) {
this.line = line;
this.type = type;
}
abstract render(): string;
}
interface MarkdownStringValue {
tag: BaseDoc
end: string
};
class MarkdownString extends RenderString {
values: MarkdownStringValue[];
constructor(line: number) {
super(line, RenderType.Markdown);
this.values = [];
}
addText(value: string, end: string='\n') {
const tag = new Text(value);
this.values.push({tag, end});
}
addTitle(value: string, level: number, end: string='\n') {
const tag = new Title(value, level);
this.values.push({tag, end});
}
addQuote(value: string, end: string='\n') {
const tag = new Quote(value);
this.values.push({tag, end});
}
addBold(value: string, end: string='\n') {
const tag = new Bold(value);
this.values.push({tag, end});
}
addEnter() {
const tag = {value : ''};
const end = '\n';
this.values.push({tag, end});
}
addItalic(value: string, end='\n') {
const tag = new Italic(value);
this.values.push({tag, end});
}
addInlineCode(value: string, end='\n') {
const tag = new InlineCode(value);
this.values.push({tag, end});
}
addUnorderedList(values: string[]) {
const end = '';
const tag = new UnorderedList(values);
this.values.push({tag, end});
}
addOrderedList(values: string[]) {
const end = '';
const tag = new OrderedList(values);
this.values.push({tag, end});
}
addSplit(value: string) {
const end = '\n';
const tag = new Split();
this.values.push({tag, end});
}
addTable(fieldNames: string[], rows: string[][], align=MarkdownAlign.Left, end='\n') {
const tag = new Table(fieldNames, rows, align);
this.values.push({tag, end});
}
renderMarkdown() {
let markdown = '';
for (const md of this.values) {
markdown += md.tag.value + md.end;
}
return markdown;
}
render() {
const rawMD = this.renderMarkdown();
return converter.makeHtml(rawMD);
}
};
class WavedromString extends RenderString {
value: string;
desc: string;
constructor(line: number, desc: string) {
super(line, RenderType.Wavedrom);
this.value = '';
this.desc = desc;
}
add(text: string) {
this.value += text;
}
render(): string {
const style = getThemeColorKind();
return makeWaveDromSVG(this.value, style);
}
};
function parseJson5(text: string): any {
let json = null;
try {
json = JSON5.parse(text);
} catch (error) {
MainOutput.report('error happen when parse json ', ReportType.Error);
MainOutput.report(error, ReportType.Error);
}
return json;
}
function makeWaveDromSVG(wavedromComment: string, style: ThemeType): string {
const json = parseJson5(wavedromComment);
try {
if (!json) {
return '';
}
const svgString = Wavedrom.renderWaveDrom(Count.svgMakeTimes, json, style);
Count.svgMakeTimes += 1;
return svgString;
} catch (error) {
MainOutput.report('error happen when render ' + wavedromComment, ReportType.Error);
MainOutput.report(error, ReportType.Error);
return '';
}
}
/**
* extract wavedrom comment from hdl file
* @param path
* @returns
*/
async function getWavedromsFromFile(path: string): Promise<WavedromString[] | undefined> {
let lineID = 0;
let findWavedrom = false;
const wavedroms: WavedromString[] = [];
const fileStream = fs.createReadStream(path, 'utf-8');
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
lineID += 1;
if (findWavedrom) {
if (/\*\//g.test(line)) {
findWavedrom = false;
} else {
const currentWav = wavedroms[wavedroms.length - 1];
currentWav.add(line.trim());
}
} else {
if (/\/\*[\s\S]*(@wavedrom)/g.test(line)) {
findWavedrom = true;
let spliters = line.trim().split('@wavedrom');
let desc = spliters[spliters.length - 1];
const newWavedrom = new WavedromString(lineID, desc);
wavedroms.push(newWavedrom);
}
}
}
return wavedroms;
}
function mergeSortByLine(docs: MarkdownString[], svgs: WavedromString[]): RenderString[] {
const renderList = [];
let i = 0, j = 0;
while (i < docs.length && j < svgs.length) {
if (docs[i].line < svgs[j].line) {
renderList.push(docs[i]);
i ++;
} else {
renderList.push(svgs[j]);
j ++;
}
}
while (i < docs.length) {
renderList.push(docs[i]);
i ++;
}
while (j < svgs.length) {
renderList.push(svgs[j]);
j ++;
}
return renderList;
}
export {
converter,
mergeSortByLine,
RenderType,
BaseDoc,
MarkdownString,
WavedromString,
RenderString,
makeWaveDromSVG,
getWavedromsFromFile,
Count
};

227
src/function/hdlDoc/html.ts Normal file
View File

@ -0,0 +1,227 @@
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as fs from 'fs';
import { opeParam, MainOutput, AbsPath } from '../../global';
import { Count, MarkdownString, WavedromString } from './common';
import { getRenderList, getCurrentRenderList } from './markdown';
import { hdlPath, hdlIcon, hdlFile } from '../../hdlFs';
const _cache = {
css : ''
};
function makeFinalHTML(body: string, style: string): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="wrapper">
<div id="write">
${body}
</div>
</div>
</body>
<style>
${style}
</style>
</html>`;
}
function makeExportHTML(cssHref: string, body: string): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="${cssHref}"></link>
</head>
<body>
<div id="wrapper">
<div id="write">
${body}
</div>
</div>
</body>
</html>`;
}
function makeCommonElement(renderResult: string): string {
return renderResult + '<br>\n';
}
function makeSVGElement(renderResult: string, caption: string): string {
let mainHtml;
if (caption) {
mainHtml = '<div align=center>' + renderResult + `<p class="ImgCaption">${caption}</p>` + '</div>';
} else {
mainHtml = '<div align=center>' + renderResult + '</div>';
}
return '<br>' + mainHtml + '<br><br>\n';
}
function makeSVGElementByLink(link: AbsPath, caption: string): string {
let mainHtml;
if (caption) {
mainHtml = `<div align=center><img src="${link}"></img><p class="ImgCaption">${caption}</p></div>`;
} else {
mainHtml = `<div align=center><img src="${link}"></img></div>`;
}
return '<br>' + mainHtml + '<br><br>\n';
}
function getDocCssString() {
if (_cache.css) {
return _cache.css;
} else {
const cssPath = hdlPath.join(opeParam.extensionPath, 'css/documentation.css');
const cssString = fs.readFileSync(cssPath, 'utf-8');
_cache.css = cssString;
return cssString;
}
}
function makeWavedromRenderErrorHTML() {
return `<div class="error-out">
<p class="error">Error Render</p>
</div><br>`;
}
/**
* @description make the html string of a finial display style
* @param usage in whick module is used
*/
async function makeShowHTML(usage: string): Promise<string> {
const renderList = await getCurrentRenderList();
if (!renderList || renderList.length === 0) {
return '';
}
// start to render the real html
let body = '';
for (const r of renderList) {
const renderResult = r.render();
if (renderResult) {
if (r instanceof MarkdownString) {
body += makeCommonElement(renderResult);
} else if (r instanceof WavedromString) {
body += makeSVGElement(renderResult, r.desc);
}
} else {
body += makeWavedromRenderErrorHTML();
}
}
// add css
let cssString = getDocCssString();
if (usage === 'webview') { // if invoked by webview, change background image
const webviewConfig = vscode.workspace.getConfiguration("function.doc.webview");
const imageUrl = webviewConfig.get('backgroundImage', '');
cssString = cssString.replace("--backgroundImage", imageUrl);
} else if (usage === 'pdf') { // if invoked by pdf, transform .vscode-light to #write
cssString = cssString.replace(/\.vscode-light/g, '#write');
}
const html = makeFinalHTML(body, cssString);
return html;
}
async function showDocWebview() {
const htmlPromise = makeShowHTML("webview");
const webview = vscode.window.createWebviewPanel(
'TOOL.doc.webview.show',
'document',
vscode.ViewColumn.Two,
{
enableScripts: true, // enable JS
retainContextWhenHidden: true, // unchange webview when hidden, prevent extra refresh
}
);
webview.iconPath = hdlIcon.getIconConfig('documentation');
webview.webview.html = await htmlPromise;
}
async function exportCurrentFileDocAsHTML() {
if (vscode.window.activeColorTheme.kind !== vscode.ColorThemeKind.Light) {
vscode.window.showErrorMessage('Please export html in a light theme!');
return;
}
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
const hdlFileName = hdlPath.basename(currentFilePath);
const renderList = await getRenderList(currentFilePath);
if (!renderList || renderList.length === 0) {
return;
}
const wsPath = opeParam.workspacePath;
const markdownFolderPath = hdlPath.join(wsPath, 'html');
if (!fs.existsSync(markdownFolderPath)) {
fs.mkdirSync(markdownFolderPath);
}
const currentRoot = hdlPath.join(markdownFolderPath, hdlFileName);
if (fs.existsSync(currentRoot)) {
hdlFile.rmSync(currentRoot);
}
fs.mkdirSync(currentRoot);
const figureFolder = hdlPath.join(currentRoot, 'figure');
fs.mkdirSync(figureFolder);
const cssFolder = hdlPath.join(currentRoot, 'css');
fs.mkdirSync(cssFolder);
const relateCssPath = './css/index.css';
const cssPath = hdlPath.join(cssFolder, 'index.css');
let cssString = getDocCssString();
// only support export in the ligth theme
cssString = cssString.replace(/\.vscode-light/g, '#write');
fs.writeFileSync(cssPath, cssString);
let body = '';
for (const r of renderList) {
const renderResult = r.render();
if (r instanceof MarkdownString) {
body += makeCommonElement(renderResult);
} else if (r instanceof WavedromString) {
const svgName = 'wavedrom-' + Count.svgMakeTimes + '.svg';
const svgPath = hdlPath.join(figureFolder, svgName);
fs.writeFileSync(svgPath, renderResult);
const relatePath = hdlPath.join('./figure', svgName);
body += makeSVGElementByLink(relatePath, r.desc);
}
}
const html = makeExportHTML(relateCssPath, body);
const htmlName = 'index.html';
const htmlPath = hdlPath.join(currentRoot, htmlName);
Count.svgMakeTimes = 0;
fs.writeFileSync(htmlPath, html);
}
async function exportProjectDocAsHTML() {
vscode.window.showInformationMessage('this is exportProjectDocAsHTML');
}
export {
showDocWebview,
exportCurrentFileDocAsHTML,
exportProjectDocAsHTML,
makeShowHTML,
makeSVGElementByLink
};

View File

@ -0,0 +1,70 @@
import * as vscode from 'vscode';
import { hdlIcon } from "../../hdlFs";
import { exportCurrentFileDocAsMarkdown, exportProjectDocAsMarkdown } from './markdown';
import { exportCurrentFileDocAsHTML, exportProjectDocAsHTML, showDocWebview } from './html';
import { exportCurrentFileDocAsPDF, exportProjectDocAsPDF } from './pdf';
const availableFormat = [
'markdown', 'pdf', 'html'
];
class ExportFunctionItem {
label: string;
format: string;
exportFunc: Function;
detail: string;
constructor(format: string, title: string, detail: string, exportFunc: Function) {
// TODO : 等到sv的解析做好后写入对于不同hdl的图标
let iconID = '$(export-' + format + ') ';
this.label = iconID + title;
this.format = format;
this.exportFunc = exportFunc;
this.detail = detail;
}
};
function registerFileDocExport(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.exportFile', () => {
const option = {
placeHolder: 'Select an Export Format'
};
const items = [
new ExportFunctionItem('markdown', ' markdown', 'export markdown folder', exportCurrentFileDocAsMarkdown),
new ExportFunctionItem('pdf', ' pdf', 'only support light theme', exportCurrentFileDocAsPDF),
new ExportFunctionItem('html', ' html', 'only support light theme', exportCurrentFileDocAsHTML)
];
vscode.window.showQuickPick(items, option).then(item => {
if (item) {
item.exportFunc();
}
});
});
}
function registerProjectDocExport(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.exportProject', () => {
const option = {
placeHolder: 'Select an Export Format'
};
const items = [
new ExportFunctionItem('markdown',' markdown', 'export markdown folder', exportProjectDocAsMarkdown),
new ExportFunctionItem('pdf', ' pdf', 'only support light theme', exportProjectDocAsPDF),
new ExportFunctionItem('html', ' html', 'only support light theme', exportProjectDocAsHTML)
];
vscode.window.showQuickPick(items, option).then(item => {
if (item) {
item.exportFunc();
}
});
});
}
export {
registerFileDocExport,
registerProjectDocExport,
showDocWebview
};

View File

@ -0,0 +1,252 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import { AbsPath, opeParam, MainOutput, ReportType } from '../../global';
import { hdlParam, HdlModule } from '../../hdlParser/core';
import { HdlModulePort, HdlModuleParam } from '../../hdlParser/common';
import { MarkdownString, RenderString, RenderType,
mergeSortByLine, getWavedromsFromFile, Count, WavedromString } from './common';
import { hdlPath, hdlFile } from '../../hdlFs';
import { getSymbolComments } from '../lsp/util/feature';
function makeSVGElementByLink(link: AbsPath, caption?: string) {
let mainHtml;
if (caption) {
mainHtml = `<div align=center><img src="${link}"></img><p class="ImgCaption">${caption}</p></div>`;
} else {
mainHtml = `<div align=center><img src="${link}"></img></div>`;
}
return '<br>' + mainHtml + '<br><br>\n';
}
function makeTableFromObjArray(md: MarkdownString, array: any[], name: string, fieldNames: string[], displayNames: string[]) {
const ws = hdlPath.toSlash(opeParam.workspacePath) + '/';
if (array.length === 0) {
md.addText(`no ${name} info`);
} else {
const rows = [];
for (const obj of array) {
const data = [];
for (const name of fieldNames) {
let value = obj[name];
if (name === 'instModPath' && value) {
value = value.replace(ws, '');
}
if (value && value.trim().length === 0) {
value = ' ';
}
data.push(value);
}
rows.push(data);
}
if (displayNames) {
md.addTable(displayNames, rows);
} else {
md.addTable(fieldNames, rows);
}
}
}
/**
* @description add attribute description to each port/param
* @param {string} path
* @param {Array<ModPort|ModParam>} ports
*/
async function patchComment(path: AbsPath, ports: (HdlModulePort | HdlModuleParam)[]) {
if (!ports || !ports.length) {
return;
}
const ranges = ports.map(port => port.range);
const comments = await getSymbolComments(path, ranges);
for (let i = 0; i < ports.length; ++ i) {
const inlineComment = comments[i].replace(/\n/, ' ');
ports[i].desc = inlineComment;
}
}
/**
* @description get basedoc obj from a module
* @param module
*/
async function getDocsFromModule(module: HdlModule): Promise<MarkdownString> {
const moduleName = module.name;
const portNum = module.ports.length;
const paramNum = module.params.length;
// add desc can optimizer in the future version
const paramPP = patchComment(module.path, module.params);
const portPP = patchComment(module.path, module.ports);
let topModuleDesc = '';
if (hdlParam.isTopModule(module.path, module.name)) {
topModuleDesc = '√';
} else {
topModuleDesc = '×';
}
const md = new MarkdownString(module.range.start.line);
// add module name
md.addTitle(moduleName, 1);
md.addTitle('Basic Info', 2);
const infos = [
`${portNum} params, ${paramNum} ports`,
'top module ' + topModuleDesc
];
md.addUnorderedList(infos);
md.addEnter();
// wait param and port patch
await paramPP;
await portPP;
// param section
md.addTitle('params', 2);
makeTableFromObjArray(md, module.params, 'params',
['name', 'init', 'desc'],
['name', 'init', 'description']);
md.addEnter();
// port section
md.addTitle('ports', 2);
makeTableFromObjArray(md, module.ports, 'ports',
['name', 'type', 'width', 'desc'],
['name', 'type', 'width', 'description']);
md.addEnter();
// dependency section
md.addTitle('Dependency', 2);
const insts = [];
for (const inst of module.getAllInstances()) {
insts.push(inst);
}
makeTableFromObjArray(md, insts, 'Dependencies',
['name', 'type', 'instModPath'],
['name', 'module', 'path']);
md.addEnter();
return md;
}
/**
* @description get basedoc obj according to a file
* @param path absolute path of the file
*/
async function getDocsFromFile(path: AbsPath): Promise<MarkdownString[] | undefined> {
const moduleFile = hdlParam.getHdlFile(path);
if (!moduleFile) {
MainOutput.report('Fail to export documentation of ' + path,
ReportType.Error);
return undefined;
}
const markdownStringPromises = [];
for (const module of moduleFile.getAllHdlModules()) {
const markdownStringPromise = getDocsFromModule(module);
markdownStringPromises.push(markdownStringPromise);
}
const fileDocs = [];
for (const p of markdownStringPromises) {
const markdownString = await p;
fileDocs.push(markdownString);
}
return fileDocs;
}
/**
* @description get render list of path
* @param path
*/
async function getRenderList(path: AbsPath): Promise<RenderString[] | undefined> {
if (!hdlFile.isHDLFile(path)) {
vscode.window.showErrorMessage('Please use the command in a HDL file!');
return [];
}
const docs = await getDocsFromFile(path);
const svgs = await getWavedromsFromFile(path);
if (docs && svgs) {
const renderList = mergeSortByLine(docs, svgs);
return renderList;
}
return undefined;
}
/**
* @description return render list of current file
*/
async function getCurrentRenderList(): Promise<RenderString[] | undefined> {
const editor = vscode.window.activeTextEditor;
if (editor) {
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
return await getRenderList(currentFilePath);
}
return;
}
async function exportCurrentFileDocAsMarkdown() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
const hdlFileName = hdlPath.basename(currentFilePath);
const renderList = await getRenderList(currentFilePath);
if (!renderList || renderList.length === 0) {
return;
}
const wsPath = opeParam.workspacePath;
const markdownFolderPath = hdlPath.join(wsPath, 'markdown');
if (!fs.existsSync(markdownFolderPath)) {
fs.mkdirSync(markdownFolderPath);
}
const currentRoot = hdlPath.join(markdownFolderPath, hdlFileName);
if (fs.existsSync(currentRoot)) {
hdlFile.rmSync(currentRoot);
}
fs.mkdirSync(currentRoot);
const figureFolder = hdlPath.join(currentRoot, 'figure');
fs.mkdirSync(figureFolder);
let markdown = '';
for (const r of renderList) {
if (r instanceof MarkdownString) {
markdown += r.renderMarkdown() + '\n';
} else if (r instanceof WavedromString) {
const svgString = r.render();
const svgName = 'wavedrom-' + Count.svgMakeTimes + '.svg';
const svgPath = hdlPath.join(figureFolder, svgName);
fs.writeFileSync(svgPath, svgString);
const relatePath = hdlPath.join('./figure', svgName);
const svgHtml = makeSVGElementByLink(relatePath);
markdown += '\n\n' + svgHtml + '\n\n';
}
}
const markdownName = 'index.md';
const markdownPath = hdlPath.join(currentRoot, markdownName);
Count.svgMakeTimes = 0;
fs.writeFileSync(markdownPath, markdown);
}
async function exportProjectDocAsMarkdown() {
vscode.window.showInformationMessage('this is exportProjectDocAsMarkdown');
}
export {
getDocsFromFile,
getRenderList,
getCurrentRenderList,
exportCurrentFileDocAsMarkdown,
exportProjectDocAsMarkdown
};

115
src/function/hdlDoc/pdf.ts Normal file
View File

@ -0,0 +1,115 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as puppeteer from 'puppeteer-core';
import { makeShowHTML } from './html';
import { hdlFile, hdlPath } from '../../hdlFs';
import { AbsPath, MainOutput, opeParam, ReportType } from '../../global';
// TODO : finish it in each platform
function getDefaultBrowerPath(): AbsPath {
switch (opeParam.os) {
case 'win32': return 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe';
case 'linux': return '';
default: return '';
}
}
/**
* @description transform a html file to pdf file
* @param htmlPath absolute path of input html
* @param pdfPath output path of pdf
*/
async function htmlFile2PdfFile(htmlPath: AbsPath, pdfPath: AbsPath) {
const pdfConfig = vscode.workspace.getConfiguration("function.doc.pdf");
const platformDefaultBrowerPath = getDefaultBrowerPath();
const browserPath = pdfConfig.get('browserPath', platformDefaultBrowerPath);
if (!fs.existsSync(browserPath)) {
vscode.window.showErrorMessage("Path " + browserPath + " is not a valid browser path!");
return;
}
const browser = await puppeteer.launch({
executablePath: browserPath,
args: ['--lang=' + vscode.env.language, '--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
const uriFilePath = vscode.Uri.file(htmlPath).toString();
await page.goto(uriFilePath, { waitUntil: 'networkidle0' });
const options = {
path: pdfPath,
scale: pdfConfig.scale,
displayHeaderFooter: pdfConfig.displayHeaderFooter,
headerTemplate: pdfConfig.headerTemplate,
footerTemplate: pdfConfig.footerTemplate,
printBackground: pdfConfig.printBackground,
landscape: pdfConfig.landscape,
format: pdfConfig.format,
margin: {
top: pdfConfig.margin.top + 'cm',
right: pdfConfig.margin.right + 'cm',
bottom: pdfConfig.margin.bottom + 'cm',
left: pdfConfig.margin.left + 'cm'
}
};
await page.pdf(options);
await browser.close();
}
async function exportCurrentFileDocAsPDF() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
const hdlFileName = hdlPath.basename(currentFilePath);
const wsPath = opeParam.workspacePath;
return vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: '[Digital-IDE]: Export ' + currentFilePath + '...'
}, async progress => {
try {
const html = await makeShowHTML("pdf");
if (!html) {
return;
}
const pdfFolderPath = hdlPath.join(wsPath, 'pdf');
if (!fs.existsSync(pdfFolderPath)) {
fs.mkdirSync(pdfFolderPath);
}
const pdfName = hdlFileName + '.pdf';
const pdfPath = hdlPath.join(pdfFolderPath, pdfName);
if (fs.existsSync(pdfPath)) {
hdlFile.rmSync(pdfPath);
}
const tempHtmlName = hdlFileName + '.tmp.html';
const tempHtmlPath = hdlPath.join(pdfFolderPath, tempHtmlName);
if (fs.existsSync(tempHtmlPath)) {
hdlFile.rmSync(tempHtmlPath);
}
fs.writeFileSync(tempHtmlPath, html);
await htmlFile2PdfFile(tempHtmlPath, pdfPath);
hdlFile.rmSync(tempHtmlPath);
} catch (error) {
MainOutput.report("error happen in export pdf: " + error, ReportType.Error);
}
});
}
function exportProjectDocAsPDF() {
vscode.window.showInformationMessage('this is exportProjectDocAsPDF');
}
export {
exportCurrentFileDocAsPDF,
exportProjectDocAsPDF
};

26
src/function/index.ts Normal file
View File

@ -0,0 +1,26 @@
import * as vscode from 'vscode';
import * as hdlDoc from './hdlDoc';
import * as Sim from './sim';
function registerDocumentation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview);
hdlDoc.registerFileDocExport(context);
hdlDoc.registerProjectDocExport(context);
}
function registerSimulation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.tool.instance', Sim.instantiation);
vscode.commands.registerCommand('digital-ide.tool.testbench', Sim.testbench);
}
function registerAllCommands(context: vscode.ExtensionContext) {
registerDocumentation(context);
registerSimulation(context);
}
export {
registerAllCommands
};

View File

@ -0,0 +1,203 @@
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

@ -0,0 +1,321 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as vscode from 'vscode';
import { AbsPath } from '../../../global';
import { HdlLangID } from '../../../global/enum';
import { hdlPath, hdlFile } from '../../../hdlFs';
import { Range } from '../../../hdlParser/common';
const vlogNumberReg = {
'h' : /[0-9]+?'h([0-9a-fA-F_]+)/g,
'b' : /[0-9]+?'b([0-1_]+)/g,
'o' : /[0-9]+?'o([0-7_]+)/g,
};
const vhdlNumberReg = {
'h' : /x"([0-9a-fA-F_]+)"/g,
'b' : /([0-1_]+)"/g,
};
interface vlogNumber {
unsigned: number
signed: number
};
/**
* @description recognize and transfer number
* @param lineText
* @param character
*/
function transferVlogNumber(lineText: string, character: number): vlogNumber | undefined {
let numberReg = /[0-9]/;
let opt = null;
let numberString = null;
if (numberReg.test(lineText[character])) {
const leftPart = [];
const rightPart = [];
const length = lineText.length;
for (let i = character - 1; i >= 0; -- i) {
const ch = lineText[i];
if (numberReg.test(ch)) {
leftPart.push(ch);
} else if (Object.keys(vlogNumberReg).includes(ch)) {
if (i === 0) {
return undefined;
} else if (lineText[i - 1] === "'") {
opt = ch;
break;
} else {
return undefined;
}
} else {
return undefined;
}
}
for (let i = character + 1; i < length; ++ i) {
const ch = lineText[i];
if (numberReg.test(ch)) {
rightPart.push(ch);
} else {
break;
}
}
const leftWord = leftPart.reverse().join('');
const rightWord = rightPart.join('');
numberString = leftWord + lineText[character] + rightWord;
} else {
return undefined;
}
if (opt && numberString) {
return string2num(numberString, opt);
} else {
return undefined;
}
}
/**
* @description ()
* @param str
* @param opt hex | bin | oct
*/
function string2num(str: string, opt: string): vlogNumber {
let optNumber = -1;
switch (opt) {
case 'h':
optNumber = 16;
break;
case 'b':
optNumber = 2;
break;
case 'o':
optNumber = 8;
break;
default: break;
}
let unsigned = parseInt(str, optNumber);
let pow = Math.pow(optNumber, str.length);
let signed = unsigned;
if (unsigned >= pow >> 1) {
signed = unsigned - pow;
}
return {
'unsigned' : unsigned,
'signed' : signed,
};
}
/**
* @description
* @param bin
* @param exp
* @param fra
*/
function bin2float(bin: string, exp: number, fra: number): number | undefined {
if (bin.length < exp + fra +1) {
return;
} else {
const bais = Math.pow(2, (exp-1))-1;
exp = exp - bais;
return exp;
}
}
async function getFullSymbolInfo(document: vscode.TextDocument, range: Range, nonblank: RegExp, l_comment_symbol: string, l_comment_regExp: RegExp, needDefinition=true) {
const comments = [];
if (needDefinition) {
const startPosition = new vscode.Position(range.start.line, range.start.character);
const endPosition = new vscode.Position(range.end.line, range.end.character);
const definitionString = document.getText(new vscode.Range(startPosition, endPosition));
comments.push(definitionString);
}
let content = '';
let is_b_comment = false;
let line = range.start.line + 1;
while (line) {
line--;
content = document.lineAt(line).text;
// 首先判断该行是否是空白
let isblank = content.match(nonblank);
if (!isblank) {
continue;
}
if (is_b_comment) {
let b_comment_begin_index = content.indexOf('/*');
if (b_comment_begin_index === -1) {
comments.push(content + '\n');
continue;
}
comments.push(content.slice(b_comment_begin_index, content.length) + '\n');
is_b_comment = false;
content = content.slice(0, b_comment_begin_index);
if (content.match(nonblank)) {
break;
}
continue;
}
// 判断该行是否存在行注释
let l_comment_index = content.indexOf(l_comment_symbol);
if (l_comment_index >= 0) {
let before_l_comment = content.slice(0, l_comment_index);
// before_l_comment = del_comments(before_l_comment, b_comment_end_index);
if (before_l_comment.match(nonblank)) {
// 如果去除块注释之后还有字符则认为该注释不属于所要的
if (line === range.start.line) {
// let b_comment_last_index = content.lastIndexOf('*/');
// b_comment_last_index = (b_comment_last_index == -1) ? 0 : (b_comment_last_index + 2);
// comments.push(content.slice(b_comment_last_index, l_comment_index) + '\n');
comments.push(content.slice(l_comment_index, content.length) + '\n');
continue;
}
break;
}
// 否则该行全为该定义的注释
comments.push(content + '\n');
continue;
}
// 判断该行是否存在块注释
let b_comment_end_index = content.indexOf('*/');
if (b_comment_end_index >= 0) {
b_comment_end_index += 2;
let behind_b_comment = content.slice(b_comment_end_index, content.length);
behind_b_comment = del_comments(behind_b_comment, l_comment_regExp);
if (behind_b_comment.match(nonblank)) {
// 如果去除块注释之后还有字符则认为该注释不属于所要的
if (line === range.start.line) {
comments.push(content.slice(0, b_comment_end_index) + '\n');
is_b_comment = true;
continue;
}
break;
}
comments.push(content + '\n');
is_b_comment = true;
continue;
}
// 说明既不是块注释又不是行注释所以就是到了代码块
if (line !== range.start.line) {
break;
}
}
return comments.reverse().join('');
}
/**
* @description get definition and comment of a range
* @param path
* @param range
*/
async function getSymbolComment(path: AbsPath, range: Range) {
let languageId = hdlFile.getLanguageId(path);
const uri = vscode.Uri.file(path);
const documentPromise = vscode.workspace.openTextDocument(uri);
// get comment reg util
const nonblank = /\S+/g;
const l_comment = getCommentUtilByLanguageId(languageId);
if (l_comment) {
let l_comment_symbol = l_comment.l_comment_symbol;
let l_comment_regExp = l_comment.l_comment_regExp;
// add definition first
const document = await documentPromise;
return await getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp);
}
return;
}
/**
* @description get definition and comment of a range
* @param path
* @param ranges
*/
async function getSymbolComments(path: string, ranges: Range[]): Promise<string[]> {
let languageId = hdlFile.getLanguageId(path);
const uri = vscode.Uri.file(path);
const documentPromise = vscode.workspace.openTextDocument(uri);
// get comment reg util
const nonblank = /\S+/g;
const l_comment = getCommentUtilByLanguageId(languageId);
if (!l_comment) {
return [];
}
let l_comment_symbol = l_comment.l_comment_symbol;
let l_comment_regExp = l_comment.l_comment_regExp;
// add definition first
const document = await documentPromise;
const commentPromises = [];
const comments = [];
for (const range of ranges) {
const commentP = getFullSymbolInfo(document, range, nonblank, l_comment_symbol, l_comment_regExp, false);
commentPromises.push(commentP);
}
for (const cp of commentPromises) {
comments.push(await cp);
}
return comments;
}
interface CommentUtil {
l_comment_symbol: string
l_comment_regExp: RegExp
}
function getCommentUtilByLanguageId(languageId: HdlLangID): CommentUtil | undefined {
switch (languageId) {
case "verilog":
case "systemverilog":
return {
l_comment_symbol: '//',
l_comment_regExp: /\/\/.*/g
};
case "vhdl":
return {
l_comment_symbol: '--',
l_comment_regExp: /--.*/g
};
default: return undefined;
}
}
/**
* @description delete all comment form verilog code
* @param {string} text Verilog code input
* @returns Verilog code output after deleting all comment content
*/
function del_comments(text: string, regExp: RegExp): string {
let match = text.match(regExp);
if (match !== null) {
for (let i = 0; i < match.length; i++) {
const element = match[i];
const newElement = ' '.repeat(element.length);
text = text.replace(element,newElement);
}
}
return text;
}
export {
transferVlogNumber,
getSymbolComment,
getSymbolComments
};

View File

@ -0,0 +1,570 @@
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,8 @@
import { instantiation } from './instance';
import { testbench } from './testbench';
export {
instantiation,
testbench
};

View File

@ -0,0 +1,272 @@
import * as vscode from 'vscode';
import { HdlLangID } from '../../global/enum';
import { hdlParam } from '../../hdlParser';
import { HdlModulePort, HdlModuleParam } from '../../hdlParser/common';
import { HdlModule } from '../../hdlParser/core';
class ModuleInfoItem {
label: string;
description: string;
detail: string;
module: HdlModule;
/**
* @param module
*/
constructor(module: HdlModule) {
// TODO : 等到sv的解析做好后写入对于不同hdl的图标
let iconID = '$(instance-' + module.file.languageId + ') ';
this.label = iconID + module.name;
this.description = module.params.length + ' $(instance-param) ' +
module.ports.length + ' $(instance-port) ' +
module.getInstanceNum() + ' $(instance-module)';
this.detail = module.path;
this.module = module;
}
};
/**
* @description verilog模式下生成整个例化的内容
* @param module
*/
function instanceVlogCode(module: HdlModule) {
let vlogPortStr = vlogPort(module.ports);
let vlogParamStr = vlogParam(module.params);
let instContent = '';
instContent += vlogPortStr.wireStr;
instContent += module.name + ' ';
if (vlogParamStr !== '') {
instContent += `#(\n${vlogParamStr})\n`;
}
instContent += `u_${module.name}(\n`;
instContent += vlogPortStr.portStr;
instContent += ');\n';
return instContent;
}
/**
* @description vhdl模式下生成整个例化的内容
* @param module
*/
function instanceVhdlCode(module: HdlModule) {
// module 2001 style
let port = vhdlPort(module.ports);
let param = vhdlParam(module.params);
let instContent = `u_${module.name} : ${module.name}\n`;
if (param !== '') {
instContent += `generic map(\n${param})\n`;
}
instContent += `port map(\n${port});\n`;
return instContent;
}
/**
* @description verilog模式下对端口信息生成要例化的内容
* @param ports
*/
function vlogPort(ports: HdlModulePort[]) : { wireStr: string, portStr: string} {
let nmax = getlmax(ports, 'name');
let wmax = getlmax(ports, 'width');
let portStr = `\t// ports\n`;
let wireStr = '// outports wire\n';
for (let i = 0; i < ports.length; i++) {
const port = ports[i];
if (port.type === 'output') {
let width = port.width;
let wpadding = wmax - width.length + 1;
width += ' '.repeat(wpadding);
// TODO: vhdl type
wireStr += `wire ${width}\t${port.name};\n`;
}
let name = port.name;
let npadding = nmax - name.length + 1;
name += ' '.repeat(npadding);
portStr += `\t.${name}\t( ${name} )`;
if (i !== ports.length - 1) {
portStr += ',';
}
portStr += '\n';
}
return { wireStr, portStr };
}
/**
* @description verilog模式下对参数信息生成要例化的内容
* @param params
*/
function vlogParam(params: HdlModuleParam[]): string {
let paramStr = '';
let nmax = getlmax(params, 'name');
let imax = getlmax(params, 'init');
// .NAME ( INIT ),
for (let i = 0; i < params.length; i++) {
let name = params[i].name;
let init = params[i].init;
let namePadding = nmax - name.length + 1;
let initPadding = imax - init.length + 1;
name +=' '.repeat(namePadding);
init +=' '.repeat(initPadding);
paramStr += `\t.${name}\t( ${init} )`;
if (i !== (params.length - 1)) {
paramStr += ',';
paramStr += '\n';
}
}
return paramStr;
}
/**
* @description vhdl模式下对端口信息生成要例化的内容
* @param ports
*/
function vhdlPort(ports: HdlModulePort[]): string {
let nmax = getlmax(ports, 'name');
// NAME => NAME,
let portStr = `\n\t-- ports\n`;
for (let i = 0; i < ports.length; i++) {
let name = ports[i].name;
let padding = nmax - name.length + 1;
name += ' '.repeat(padding);
portStr += `\t${name} => ${name}`;
if (i !== (ports.length - 1)) {
portStr += ',';
}
portStr += '\n';
}
return portStr;
}
/**
* @description vhdl模式下对参数信息生成要例化的内容
* @param params
*/
function vhdlParam(params: HdlModuleParam[]): string {
let paramStr = '';
let nmax = getlmax(params, 'name');
// NAME => NAME,
for (let i = 0; i < params.length; i++) {
let name = params[i].name;
const init = params[i].init;
let npadding = nmax - name.length + 1;
name += ' '.repeat(npadding);
paramStr += `\t${name} => ${init}`;
if (i !== (params.length - 1)) {
paramStr += ',';
paramStr += '\n';
}
}
return paramStr;
}
/**
* @description arr中找到pro属性的最大字符长度
* @param {Array} arr
* @param {String} pro
* @returns {Number} pro属性的最大字符长度
*/
function getlmax(arr: any[], pro: string): number {
let lmax = 0;
for (let i = 0; i < arr.length; i++) {
const len = arr[i][pro].length;
if (len <= lmax) {
continue;
}
lmax = len;
}
return lmax;
}
/**
* @description
* @param content
* @param editor vscode.window.activeTextEditor
*/
function selectInsert(content: string, editor: vscode.TextEditor): boolean {
if (editor === undefined) {
return false;
}
let selections = editor.selections;
editor.edit((editBuilder) => {
selections.forEach((selection) => {
// position, content
editBuilder.insert(selection.active, content);
});
});
return true;
}
function getSelectItem(modules: HdlModule[]) {
// make ModuleInfoList
const items = [];
for (const module of modules) {
items.push(new ModuleInfoItem(module));
}
return items;
}
/**
* @description vscode的窗体Module中选择模块
*/
async function selectModuleFromAll() {
const option = {
placeHolder: 'Select a Module'
};
const selectModuleInfo = await vscode.window.showQuickPick(
getSelectItem(hdlParam.getAllHdlModules()), option
);
if (selectModuleInfo) {
return selectModuleInfo.module;
} else {
return null;
}
}
function instanceByLangID(module: HdlModule): string {
switch (module.languageId) {
case HdlLangID.Verilog: return instanceVlogCode(module);
case HdlLangID.Vhdl: return instanceVhdlCode(module);
// TODO : add support for svlog
case HdlLangID.SystemVerilog: return instanceVlogCode(module);
default: return '';
}
}
async function instantiation() {
const module = await selectModuleFromAll();
if (module) {
const code = instanceByLangID(module);
const editor = vscode.window.activeTextEditor;
if (editor) {
selectInsert(code, editor);
}
}
}
export {
instantiation,
instanceByLangID,
getSelectItem
};

View File

@ -0,0 +1,82 @@
import * as vscode from 'vscode';
import { MainOutput, opeParam } from '../../global';
import { hdlPath, hdlFile} from '../../hdlFs';
import { HdlModule, hdlParam } from '../../hdlParser/core';
import { instanceByLangID, getSelectItem } from './instance';
function overwrite() {
const options = {
preview: false,
viewColumn: vscode.ViewColumn.Active
};
const tbSrcPath = hdlPath.join(opeParam.extensionPath, 'lib', 'testbench.v');
const uri = vscode.Uri.file(tbSrcPath);
vscode.window.showTextDocument(uri, options);
}
function generateTestbenchFile(module: HdlModule) {
const tbSrcPath = hdlPath.join(opeParam.extensionPath, 'lib', 'testbench.v');
const tbDisPath = hdlPath.join(opeParam.prjInfo.arch.hardware.sim, 'testbench.v');
if (!hdlFile.isFile(tbDisPath)) {
var temp = hdlFile.readFile(tbSrcPath);
} else {
var temp = hdlFile.readFile(tbDisPath);
}
if (!temp) {
return null;
}
let content = '';
const lines = temp.split('\n');
const len = lines.length;
for (let index = 0; index < len; index++) {
const line = lines[index];
content += line + '\n';
if (line.indexOf("//Instance ") !== -1) {
content += instanceByLangID(module) + '\n';
}
}
try {
hdlFile.writeFile(tbDisPath, content);
MainOutput.report("Generate testbench successed");
} catch (err) {
vscode.window.showErrorMessage("Generate testbench failed:" + err);
}
}
async function testbench() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showErrorMessage('please select a editor!');
return;
}
const uri = editor.document.uri;
const option = {
placeHolder: 'Select a Module to generate testbench'
};
const path = hdlPath.toSlash(uri.fsPath);
console.log(path);
if (!hdlFile.isHDLFile(path)) {
return;
}
const currentHdlFile = hdlParam.getHdlFile(path);
if (!currentHdlFile) {
vscode.window.showErrorMessage('There is no hdlFile respect to ' + path);
return;
}
const currentHdlModules = currentHdlFile.getAllHdlModules();
const items = getSelectItem(currentHdlModules);
const select = await vscode.window.showQuickPick(items, option);
if (select) {
generateTestbenchFile(items[0].module);
}
}
export {
testbench
};

View File

@ -1,5 +1,6 @@
import { opeParam, OpeParamDefaults } from './opeParam';
import { PrjInfo, PrjInfoDefaults } from './prjInfo';
import { MainOutput, YosysOutput, ReportType } from './outputChannel';
import * as Enum from './enum';
import * as Lang from './lang';
@ -15,5 +16,8 @@ export {
Enum,
Lang,
AbsPath,
RelPath
RelPath,
MainOutput,
YosysOutput,
ReportType
};

View File

@ -24,7 +24,7 @@ class Output {
return this._ignoreTypes.includes(type);
}
report(message: string, type: ReportType = ReportType.Debug) {
report(message: string | unknown, type: ReportType = ReportType.Info) {
if (!this.skipMessage(type) && message) {
this._output.show(true);
this._output.appendLine('[' + type + '] ' + message);

View File

@ -75,7 +75,8 @@ function isHDLFile(path: AbsPath): boolean {
function getHDLFiles(path: AbsPath | AbsPath[] | Set<AbsPath>, ignores?: AbsPath[]) {
return pickFileRecursive(path, ignores, filePath => isHDLFile(filePath));
return pickFileRecursive(path, ignores,
filePath => isHDLFile(filePath));
}
@ -83,7 +84,7 @@ function pickFileRecursive(path: AbsPath | AbsPath[] | Set<AbsPath>, ignores?: A
if ((path instanceof Array) ||
(path instanceof Set)) {
const hdlFiles: AbsPath[] = [];
path.forEach(p => hdlFiles.push(...pickFileRecursive(p)));
path.forEach(p => hdlFiles.push(...pickFileRecursive(p, ignores, condition)));
return hdlFiles;
}
@ -124,11 +125,11 @@ function getLanguageId(path: AbsPath | RelPath): HdlLangID {
return HdlLangID.Unknown;
}
const ext = hdlPath.extname(path, false);
if (verilogExts.includes(path)) {
if (verilogExts.includes(ext)) {
return HdlLangID.Verilog;
} else if (vhdlExts.includes(path)) {
} else if (vhdlExts.includes(ext)) {
return HdlLangID.Vhdl;
} else if (systemVerilogExts.includes(path)) {
} else if (systemVerilogExts.includes(ext)) {
return HdlLangID.SystemVerilog;
} else {
return HdlLangID.Unknown;

View File

@ -1,16 +1,17 @@
import * as vscode from 'vscode';
import { opeParam, AbsPath, Enum } from '../global';
import * as hdlPath from './path';
interface IconConfig {
light: AbsPath
dark: AbsPath
readonly light: vscode.Uri
readonly dark: vscode.Uri
};
function getIconPath(themeType: Enum.ThemeType, iconName: string): AbsPath {
function getIconPath(themeType: Enum.ThemeType, iconName: string): vscode.Uri {
const iconFile = iconName + '.svg';
const svgDir = hdlPath.join(opeParam.extensionPath, 'images', 'svg');
const iconPath = hdlPath.join(svgDir, themeType, iconFile);
return iconPath;
return vscode.Uri.file(iconPath);
}
function getIconConfig(iconName: string): IconConfig {
@ -20,7 +21,7 @@ function getIconConfig(iconName: string): IconConfig {
};
}
module.exports = {
export {
getIconPath,
getIconConfig
};

View File

@ -72,6 +72,7 @@ interface HdlModulePort {
type: HdlModulePortType
width: string
range: Range
desc?: string
};
interface HdlModuleParam {
@ -80,6 +81,7 @@ interface HdlModuleParam {
type: string
init: string
range: Range
desc?: string
};
type InstRange = Range | null;

View File

@ -1,8 +1,10 @@
import { AbsPath } from '../global';
import { HdlLangID } from '../global/enum';
import { MainOutput, ReportType } from '../global/outputChannel';
import * as common from './common';
import { hdlFile, hdlPath } from '../hdlFs';
import { HdlSymbol } from './util';
class HdlParam {
private readonly topModules : Set<HdlModule> = new Set<HdlModule>();
@ -57,6 +59,12 @@ class HdlParam {
return hdlFile.getHdlModule(name);
}
public getAllHdlModules(): HdlModule[] {
const hdlModules: HdlModule[] = [];
this.modules.forEach(m => hdlModules.push(m));
return hdlModules;
}
public addHdlModule(hdlModule: HdlModule) {
this.modules.add(hdlModule);
}
@ -73,6 +81,30 @@ class HdlParam {
this.topModules.delete(hdlModule);
}
public getAllTopModules(global :boolean = false): HdlModule[] {
const topModules: HdlModule[] = [];
if (global) {
this.topModules.forEach(m => topModules.push(m));
} else {
this.srcTopModules.forEach(m => topModules.push(m));
this.simTopModules.forEach(m => topModules.push(m));
}
return topModules;
}
public isTopModule(path: AbsPath, name: string, global = false): boolean {
const module = this.getHdlModule(path, name);
if (!module) {
return false;
}
if (global) {
return this.topModules.has(module);
} else {
const sourceTopModule = this.selectTopModuleSourceByFileType(module);
return sourceTopModule.has(module);
}
}
public selectTopModuleSourceByFileType(hdlModule: HdlModule): Set<HdlModule> {
switch (hdlModule.file.type) {
case common.HdlFileType.Src: return this.srcTopModules;
@ -149,6 +181,29 @@ class HdlParam {
this.unhandleInstances.delete(inst);
}
public async initHdlFiles(hdlFiles: AbsPath[] | Generator<AbsPath>) {
for (const path of hdlFiles) {
// TODO : only support verilog now
const langID = hdlFile.getLanguageId(path);
if (langID === HdlLangID.Verilog) {
const fast = await HdlSymbol.fast(path);
if (fast) {
new HdlFile(path,
fast.languageId,
fast.macro,
fast.content.modules);
}
}
}
}
public async initialize(hdlFiles: AbsPath[] | Generator<AbsPath>) {
await this.initHdlFiles(hdlFiles);
for (const hdlFile of this.getAllHdlFiles()) {
hdlFile.makeInstance();
}
}
};
const hdlParam = new HdlParam();
@ -226,7 +281,7 @@ class HdlModule {
range: common.Range;
params: common.HdlModuleParam[];
ports: common.HdlModulePort[];
private rawInstances: common.RawHdlInstance[];
private rawInstances: common.RawHdlInstance[] | undefined;
private nameToInstances: Map<string, HdlInstance>;
private unhandleInstances: Set<HdlInstance>;
private globalRefers: Set<HdlInstance>;
@ -272,6 +327,10 @@ class HdlModule {
return this.file.path;
}
public get languageId(): HdlLangID {
return this.file.languageId;
}
public getInstance(name: string): HdlInstance | undefined {
return this.nameToInstances.get(name);
}
@ -309,6 +368,19 @@ class HdlModule {
return hdlInstance;
}
public makeNameToInstances() {
if (this.rawInstances) {
this.nameToInstances.clear();
for (const inst of this.rawInstances) {
this.createHdlInstance(inst);
}
this.rawInstances = undefined;
} else {
MainOutput.report('call makeNameToInstances but this.rawInstances is undefined',
ReportType.Warn);
}
}
public deleteInstanceByName(name: string) {
const inst = this.getInstance(name);
this.deleteInstance(inst);
@ -518,9 +590,17 @@ class HdlFile {
}
}
public makeInstance() {
for (const module of this.getAllHdlModules()) {
module.makeNameToInstances();
}
}
}
export {
hdlParam
hdlParam,
HdlModule,
HdlInstance,
hdlFile
};

5
src/hdlParser/index.ts Normal file
View File

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

View File

@ -1,5 +1,30 @@
import { vlogAll, vlogFast } from '../../wasm/hdlParser';
import { Fast, vlogAll, vlogFast, vhdlAll, svFast, svAll, vhdlFast, All } from '../../resources/hdlParser';
import { hdlFile } from '../hdlFs';
import { HdlLangID } from '../global/enum';
import { AbsPath } from '../global';
function hello(path: string) {
vlogAll(path);
namespace HdlSymbol {
export async function fast(path: AbsPath): Promise<Fast | undefined> {
const langID = hdlFile.getLanguageId(path);
switch (langID) {
case HdlLangID.Verilog: return vlogFast(path);
case HdlLangID.Vhdl: return vhdlFast(path);
case HdlLangID.SystemVerilog: return svFast(path);
default: return undefined;
}
}
export async function all(path: AbsPath): Promise<All | undefined> {
const langID = hdlFile.getLanguageId(path);
switch (langID) {
case HdlLangID.Verilog: return vlogAll(path);
case HdlLangID.Vhdl: return vhdlAll(path);
case HdlLangID.SystemVerilog: return svAll(path);
default: return undefined;
}
}
}
export {
HdlSymbol
};

View File

@ -23,7 +23,7 @@ interface PSConfig {
/**
* @state finish-untest
* @descriptionCn xilinx工具链下PS端的操作类
* @description xilinx工具链下PS端的操作类
*/
class XilinxOperation {
public get config(): XilinxOperationConfig {

View File

@ -86,6 +86,7 @@ class PrjManage {
searchPathSet.checkAdd(hardwareInfo.sim);
const searchPaths = searchPathSet.files;
return hdlFile.getHDLFiles(searchPaths, []);
}

74
test.js
View File

@ -1,20 +1,36 @@
/* eslint-disable @typescript-eslint/naming-convention */
const fs = require('fs');
const path = require('path');
const fspath = require('path');
const sysvlog_build = require('./wasm/hdlParser/parser');
const { vlogFast } = require('./resources/hdlParser');
const COMMON_PATH = path.resolve('./lib/common/Driver');
const COMMON_PATH = fspath.resolve('./lib/common/Driver');
const TEST_FILE = './parser_stuck.v';
const TEST_FILE2 = './src/test/vlog/dependence_test/parent.v';
const TEST_FILE3 = './src/test/vlog/formatter_test.v';
function isFile(path) {
if (!fs.existsSync(path)) {
return false;
}
const state = fs.statSync(path);
if (state.isDirectory()) {
return false;
}
return true;
}
/**
* judge if the path represent a Dir
* @param path
* @returns
*/
function isDir(path) {
if (!fs.existsSync(path)) {
return false;
}
const state = fs.statSync(path);
if (state.isDirectory()) {
return true;
@ -22,38 +38,38 @@ function isDir(path) {
return false;
}
function getHDLFiles(path) {
if (isDir(path)) {
const hdlFiles = [];
function* walk(path, condition) {
if (isFile(path)) {
if (!condition || condition(path)) {
yield path;
}
}
else {
for (const file of fs.readdirSync(path)) {
const filePath = path + '/' + file;
const filePath = fspath.join(path, file);
if (isDir(filePath)) {
const subHdlFiles = getHDLFiles(filePath);
if (subHdlFiles.length > 0) {
hdlFiles.push(...subHdlFiles);
for (const targetPath of walk(filePath, condition)) {
yield targetPath;
}
}
else if (isFile(filePath)) {
if (!condition || condition(filePath)) {
yield filePath;
}
} else if (filePath.endsWith('.v')) {
hdlFiles.push(filePath);
}
}
return hdlFiles;
} else if (path.endsWith('.v')) {
return [path];
} else {
return [];
}
}
(async() => {
const Module = await sysvlog_build();
console.log(Object.keys(Module).filter(name => name.startsWith('_') && !name.startsWith('__')));
const source = fs.readFileSync(TEST_FILE2, 'utf-8') + '\n';
Module.FS.writeFile('/sysvlog_build', source, { encoding: 'utf8' });
const start = Date.now();
const fast = Module.ccall('vlog_fast', 'string', ['string'], ['/sysvlog_build']);
const costTime = (Date.now() - start) / 1000;
console.log(JSON.stringify(JSON.parse(fast), null, ' '));
console.log('cost time', costTime);
console.time('test');
for (const file of walk('./lib', f => f.endsWith('.v'))) {
console.log(file);
try {
await vlogFast(file);
} catch (err) {
console.log(err);
}
}
console.timeEnd('test');
})();

View File

@ -3,6 +3,7 @@
"module": "commonjs",
"target": "ES2020",
"outDir": "out",
"skipLibCheck": true,
"lib": [
"ES2020"
],