2023-07-14 23:47:21 +08:00

217 lines
8.3 KiB
JavaScript

const fs = require("fs");
const fspath = require("path");
const vscode = require("vscode");
const parse = require("./utils/fsm_parser");
function getLanguageId(path) {
let vhdlExtensions = [".vhd",".vhdl",".vho",".vht"];
let vlogExtensions = [".v",".V",".vh",".vl"];
let svlogExtensions = [".sv",".SV"];
let value = fspath.extname(path);
if (vlogExtensions.includes(value)) {
return 'verilog';
}
else if (svlogExtensions.includes(value)) {
return 'systemverilog';
}
else if (vhdlExtensions.includes(value)) {
return 'vhdl';
}
return null;
}
class FsmViewer {
/**
*
* @param {vscode.ExtensionContext} context
*/
constructor(context) {
this.panel = undefined;
this.sources = [];
this.context = context;
this.rootPath = context.extensionPath.replace(/\\/g, '/');
}
/**
* CN: 打开FSM图表的 webview 网页
* @param {*} uri 指定所需转换为图表的文件uri信息
*/
open(uri) {
this.current_uri = uri;
const docPath = uri.fsPath.replace(/\\/g, '/');
if (this.panel == undefined) {
this.create();
}
this.send(docPath);
}
async create() {
// Create panel
this.panel = vscode.window.createWebviewPanel(
'state_machine_viewer',
'State machine viewer',
vscode.ViewColumn.Two,
{
enableScripts: true
}
);
this.panel.onDidDispose(() => {
// When the panel is closed, cancel any future updates to the webview content
this.panel = undefined;
}, null, this.context.subscriptions);
// Handle messages from the webview
this.panel.webview.onDidReceiveMessage((message) => {
switch (message.command) {
case 'export':
this.export_as(message.text);
return;
case 'go_to_state':
this.go_to_state(message.stm_index, message.state);
return;
case 'go_to_condition':
this.go_to_condition(message.stm_index, message.transition, message.condition);
return;
}
}, undefined, this.context.subscriptions);
let previewHtml = this.getWebviewContent(this.context);
this.panel.webview.html = previewHtml;
}
async send(path) {
let languageId = getLanguageId(path);
let content = fs.readFileSync(path, "utf-8");
let state_machines = await parse(languageId, content);
this.state_machines = state_machines;
this.panel.webview.postMessage({
command: "update",
svg: state_machines.svg,
stms: state_machines.stm
});
}
go_to_state(stm_index, state) {
if (this.state_machines === undefined) {
return;
}
let states = this.state_machines.stm[stm_index].states;
let state_stm;
for (let i = 0; i < states.length; ++i) {
if (states[i].name.replace(/\"/g, '').replace(/\'/g, '') === state) {
state_stm = states[i];
}
}
if (state_stm !== undefined) {
let start_position = state_stm.start_position;
let end_position = state_stm.end_position;
let pos_1 = new vscode.Position(start_position[0], start_position[1]);
let pos_2 = new vscode.Position(end_position[0], end_position[1]);
vscode.workspace.openTextDocument(this.current_uri).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.One).then(editor => {
// Line added - by having a selection at the same position twice, the cursor jumps there
editor.selections = [new vscode.Selection(pos_1, pos_2)];
// And the visible range jumps there too
var range = new vscode.Range(pos_1, pos_2);
editor.revealRange(range);
});
});
}
}
go_to_condition(stm_index, transition, condition) {
let normalized_condition = this.normalize_string(condition);
let state_origen = transition[0];
let state_destination = transition[1];
if (this.state_machines === undefined) {
return;
}
let states = this.state_machines.stm[stm_index].states;
let transition_match;
//Search state
for (let i = 0; i < states.length; ++i) {
if (states[i].name.replace(/\"/g, '').replace(/\'/g, '') === state_origen) {
let transitions = states[i].transitions;
//Search condition
for (let j = 0; j < transitions.length; ++j) {
let normalized_condition_state = this.normalize_string(transitions[j].condition);
if (transitions[j].destination.replace(/\"/g, '').replace(/\'/g, '') === state_destination
&& normalized_condition_state === normalized_condition) {
transition_match = transitions[j];
}
}
}
}
if (transition_match !== undefined) {
if (transition_match.start_position === undefined || transition_match.end_position === undefined) {
return;
}
let start_position = transition_match.start_position;
let end_position = transition_match.end_position;
let pos_1 = new vscode.Position(start_position[0], start_position[1]);
let pos_2 = new vscode.Position(end_position[0], end_position[1]);
vscode.workspace.openTextDocument(this.current_uri).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.One).then(editor => {
// Line added - by having a selection at the same position twice, the cursor jumps there
editor.selections = [new vscode.Selection(pos_1, pos_2)];
// And the visible range jumps there too
var range = new vscode.Range(pos_1, pos_2);
editor.revealRange(range);
});
});
}
}
export_as(type) {
if (type === "svg") {
let filter = { 'svg': ['svg'] };
vscode.window.showSaveDialog({ filters: filter }).then(fileInfos => {
if ((fileInfos === null || fileInfos === void 0 ? void 0 : fileInfos.path) !== undefined) {
let path_full = this.normalize_path(fileInfos === null || fileInfos === void 0 ? void 0 : fileInfos.path);
let dir_name = fspath.dirname(path_full);
let file_name = fspath.basename(path_full).split('.')[0];
for (let i = 0; i < this.state_machines.svg.length; ++i) {
let custom_path = `${dir_name}${fspath.sep}${file_name}_${i}.svg`;
fs.writeFileSync(custom_path, this.state_machines.svg[i].svg);
}
vscode.window.showInformationMessage('Documentation has been saved.');
}
});
}
else {
console.log("Error export documentation.");
}
}
normalize_path(path) {
if (path[0] === '/' && require('os').platform() === 'win32') {
return path.substring(1);
}
else {
return path;
}
}
normalize_string(str) {
let n_string = str.replace(/[^ -~]+/g, '');
n_string = n_string.replace(/ /g, '');
n_string = n_string.replace(/\n/g, '');
return n_string;
}
getWebviewContent() {
const resource_path = fspath.join(this.rootPath, 'resources', 'fsm', 'fsm_viewer.html');
const dir_path = fspath.dirname(resource_path);
let html = fs.readFileSync(resource_path, 'utf-8');
html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => {
const absLocalPath = fspath.resolve(dir_path, $2);
const webviewUri = this.panel.webview.asWebviewUri(vscode.Uri.file(absLocalPath));
const replaceHref = $1 + webviewUri.toString() + '"';
return replaceHref;
});
return html;
}
}
module.exports = {
FsmViewer
};