finish treeView partly

This commit is contained in:
锦恢 2023-04-11 22:56:09 +08:00
parent 8baadf492f
commit 0fb86a70a1
8 changed files with 588 additions and 57 deletions

View File

@ -1,7 +1,8 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as hdlDoc from './hdlDoc'; import * as hdlDoc from './hdlDoc';
import * as Sim from './sim'; import * as sim from './sim';
import * as treeView from './treeView';
function registerDocumentation(context: vscode.ExtensionContext) { function registerDocumentation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview); vscode.commands.registerCommand('digital-ide.hdlDoc.showWebview', hdlDoc.showDocWebview);
@ -11,14 +12,28 @@ function registerDocumentation(context: vscode.ExtensionContext) {
function registerSimulation(context: vscode.ExtensionContext) { function registerSimulation(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('digital-ide.tool.instance', Sim.instantiation); vscode.commands.registerCommand('digital-ide.tool.instance', sim.instantiation);
vscode.commands.registerCommand('digital-ide.tool.testbench', Sim.testbench); vscode.commands.registerCommand('digital-ide.tool.testbench', sim.testbench);
vscode.commands.registerCommand('digital-ide.tool.icarus.simulateFile', Sim.Icarus.simulateFile); vscode.commands.registerCommand('digital-ide.tool.icarus.simulateFile', sim.Icarus.simulateFile);
} }
function registerAllCommands(context: vscode.ExtensionContext) { function registerAllCommands(context: vscode.ExtensionContext) {
registerDocumentation(context); registerDocumentation(context);
registerSimulation(context); registerSimulation(context);
registerTreeView(context);
}
function registerTreeView(context: vscode.ExtensionContext) {
// register normal tree
vscode.window.registerTreeDataProvider('digital-ide.treeView.arch', treeView.moduleTreeProvider);
vscode.window.registerTreeDataProvider('digital-ide.treeView.tool', treeView.toolTreeProvider);
vscode.window.registerTreeDataProvider('digital-ide.treeView.hardware', treeView.hardwareTreeProvider);
vscode.window.registerTreeDataProvider('digital-ide.treeView.software', treeView.softwareTreeProvider);
// constant used in tree
vscode.commands.executeCommand('setContext', 'TOOL-tree-expand', false);
} }

View File

@ -0,0 +1,195 @@
/* eslint-disable @typescript-eslint/naming-convention */
import * as vscode from 'vscode';
import { getIconConfig } from '../../hdlFs/icons';
interface CommandDataItem {
name: string,
cmd: string,
icon: string,
tip: string,
children: any[]
}
type CommandConfig = Record<string, {
cmd: string,
icon: string,
tip: string,
children?: CommandConfig,
}>;
class BaseCommandTreeProvider implements vscode.TreeDataProvider<CommandDataItem> {
config: CommandConfig;
contextValue: 'HARD' | 'SOFT' | 'TOOL';
constructor(config: CommandConfig, contextValue: 'HARD' | 'SOFT' | 'TOOL') {
this.config = config;
this.contextValue = contextValue;
}
// 根据对象遍历属性返回CommandDataItem数组
public makeCommandDataItem(object: any): CommandDataItem[] {
const childDataItemList = [];
for (const key of Object.keys(object)) {
const el = object[key];
const dataItem: CommandDataItem = { name: key, cmd: el.cmd, icon: el.icon, tip: el.tip, children: el.children };
childDataItemList.push(dataItem);
}
return childDataItemList;
}
public getChildren(element: CommandDataItem): CommandDataItem[] {
if (element) {
if (element.children) {
return this.makeCommandDataItem(element.children);
} else {
return [];
}
} else { // 第一层
return this.makeCommandDataItem(this.config);
}
}
// 根据输入的CommandDataItem转化为vscode.TreeItem
getTreeItem(element: CommandDataItem): vscode.TreeItem | Thenable<vscode.TreeItem> {
const childNum = Object.keys(element.children).length;
const treeItem = new vscode.TreeItem(
element.name,
childNum === 0 ?
vscode.TreeItemCollapsibleState.None :
vscode.TreeItemCollapsibleState.Collapsed
);
treeItem.contextValue = this.contextValue;
treeItem.command = {
title: element.cmd,
command: element.cmd,
};
treeItem.tooltip = element.tip;
treeItem.iconPath = getIconConfig(element.icon);
return treeItem;
}
};
class HardwareTreeProvider extends BaseCommandTreeProvider {
constructor() {
const config: CommandConfig = {
Launch: {
cmd: 'HARD.Launch',
icon: 'cmd',
tip: 'Launch FPGA development assist function'
},
Simulate: {
cmd: 'HARD.Simulate',
icon: 'toolBox',
tip: 'Launch the manufacturer Simulation',
children: {
CLI: {
cmd: 'HARD.simCLI',
icon: 'branch',
tip: 'Launch the manufacturer Simulation in CLI'
},
GUI: {
cmd: 'HARD.simGUI',
icon: 'branch',
tip: 'Launch the manufacturer Simulation in GUI'
},
}
},
Refresh: {
cmd: 'HARD.Refresh',
icon: 'cmd',
tip: 'Refresh the current project file'
},
Build: {
cmd: 'HARD.Build',
icon: 'toolBox',
tip: 'Build the current fpga project',
children: {
Synth: {
cmd: 'HARD.Synth',
icon: 'branch',
tip: 'Synth the current project'
},
Impl: {
cmd: 'HARD.Impl',
icon: 'branch',
tip: 'Impl the current project'
},
BitStream: {
cmd: 'HARD.Bit',
icon: 'branch',
tip: 'Generate the BIT File'
},
}
},
Program: {
cmd: 'HARD.Program',
icon: 'cmd',
tip: 'Download the bit file into the device'
},
GUI: {
cmd: 'HARD.GUI',
icon: 'cmd',
tip: 'Open the GUI'
},
Exit: {
cmd: 'HARD.Exit',
icon: 'cmd',
tip: 'Exit the current project'
}
};
super(config, 'HARD');
}
};
class SoftwareTreeProvider extends BaseCommandTreeProvider {
constructor() {
const config: CommandConfig = {
Launch: {
cmd: 'SOFT.Launch',
icon: 'cmd',
tip: 'Launch SDK development assist function'
},
Build: {
cmd: 'SOFT.Launch',
icon: 'cmd',
tip: 'Build the current SDK project'
},
Download: {
cmd: 'SOFT.Launch',
icon: 'cmd',
tip: 'Download the boot file into the device'
},
};
super(config, 'SOFT');
}
}
class ToolTreeProvider extends BaseCommandTreeProvider {
constructor() {
const config: CommandConfig = {
Clean: {
cmd: 'TOOL.Clean',
icon: 'clean',
tip: 'Clean the current project'
}
};
super(config, 'TOOL');
}
}
const hardwareTreeProvider = new HardwareTreeProvider();
const softwareTreeProvider = new SoftwareTreeProvider();
const toolTreeProvider = new ToolTreeProvider();
export {
hardwareTreeProvider,
softwareTreeProvider,
toolTreeProvider,
};

View File

@ -43,9 +43,16 @@ const xilinx = new Set<string>([
]); ]);
enum ItemMode { vhdl, systemverilog, verilog, remote, cells }; const itemModes = new Set<string>([
'vhdl', 'systemverilog', 'verilog', 'remote', 'cells'
]);
const otherModes = new Set<string>([
'src', 'sim', 'File Error', 'cells'
]);
export { export {
xilinx xilinx,
itemModes,
otherModes
}; };

View File

@ -1,53 +1,38 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { AbsPath } from '../../global'; import { hdlPath } from '../../hdlFs';
interface ModuleDataItem { import { hardwareTreeProvider, softwareTreeProvider, toolTreeProvider } from './command';
icon: string, // 图标 import { moduleTreeProvider, ModuleDataItem } from './tree';
name: string, // module name
type: string,
path: AbsPath, // path of the file function openFileByUri(uri: string) {
parent: ModuleDataItem | null // parent file if (hdlPath.exist(uri)) {
vscode.window.showTextDocument(vscode.Uri.file(uri));
}
} }
interface SelectTop { function refreshArchTree(element: ModuleDataItem) {
src: any, // TODO : diff and optimize
sim: any moduleTreeProvider.refresh(element);
}
function expandTreeView() {
vscode.commands.executeCommand('setContext', 'TOOL-tree-expand', false);
}
function collapseTreeView() {
vscode.commands.executeCommand('workbench.actions.treeView.TOOL-tree-arch.collapseAll');
vscode.commands.executeCommand('setContext', 'TOOL-tree-expand', true);
} }
class ModuleTreeProvider implements vscode.TreeDataProvider<ModuleDataItem> { export {
treeEventEmitter: vscode.EventEmitter<ModuleDataItem>; hardwareTreeProvider,
treeEvent: vscode.Event<ModuleDataItem>; softwareTreeProvider,
toolTreeProvider,
constructor() { moduleTreeProvider,
this.treeEventEmitter = new vscode.EventEmitter<ModuleDataItem>(); expandTreeView,
this.treeEvent = this.treeEventEmitter.event; collapseTreeView,
openFileByUri,
refreshArchTree
} };
getTreeItem(element: ModuleDataItem): vscode.TreeItem | Thenable<vscode.TreeItem> {
}
getChildren(element?: ModuleDataItem | undefined): vscode.ProviderResult<ModuleDataItem[]> {
if (element) {
const name = element.name;
if (name === 'sim' || name === 'src') {
element.parent = null;
}
} else {
}
}
getParent(element: ModuleDataItem): vscode.ProviderResult<ModuleDataItem> {
}
getTopModuleItemList(element: ModuleDataItem): ModuleDataItem[] {
}
}

View File

@ -0,0 +1,278 @@
import * as vscode from 'vscode';
import { AbsPath, MainOutput, opeParam, ReportType } from '../../global';
import { SimPath, SrcPath } from '../../global/prjInfo';
import { HdlInstance, hdlParam } from '../../hdlParser/core';
import { HdlFileType } from '../../hdlParser/common';
import { hdlFile, hdlPath } from '../../hdlFs';
import { xilinx, itemModes, otherModes } from './common';
import { getIconConfig } from '../../hdlFs/icons';
let needExpand = true;
interface ModuleDataItem {
icon: string, // 图标
name: string, // module name
type: string,
path: AbsPath | undefined, // path of the file
parent: ModuleDataItem | null // parent file
}
interface FirstTopItem {
name: string,
path: AbsPath | undefined
}
interface FirstTop {
src: FirstTopItem | null,
sim: FirstTopItem | null
}
function canExpandable(element: ModuleDataItem) {
if (element.icon === 'src' || element.icon === 'sim') { // src and sim can expand anytime
return true;
} else {
const modulePath = element.path;
if (!modulePath) { // unsolved module cannot expand
return false;
}
const moduleName = element.name;
if (!hdlParam.hasHdlModule(modulePath, moduleName)) { // test or bug
return false;
}
const module = hdlParam.getHdlModule(modulePath, moduleName);
if (module) {
return module.getInstanceNum() > 0;
} else {
return false;
}
}
}
class ModuleTreeProvider implements vscode.TreeDataProvider<ModuleDataItem> {
treeEventEmitter: vscode.EventEmitter<ModuleDataItem>;
treeEvent: vscode.Event<ModuleDataItem>;
firstTop: FirstTop;
srcRootItem: ModuleDataItem;
simRootItem: ModuleDataItem;
constructor() {
this.treeEventEmitter = new vscode.EventEmitter<ModuleDataItem>();
this.treeEvent = this.treeEventEmitter.event;
this.firstTop = {
src: null,
sim: null,
};
this.srcRootItem = {icon: 'src', type: HdlFileType.Src, name: 'src', path: '', parent: null};
this.simRootItem = {icon: 'sim', type: HdlFileType.Sim, name: 'sim', path: '', parent: null};
}
public refresh(element?: ModuleDataItem) {
if (element) {
this.treeEventEmitter.fire(element);
} else {
// refresh all the root in default
this.refreshSim();
this.refreshSrc();
}
}
public refreshSrc() {
this.treeEventEmitter.fire(this.srcRootItem);
}
public refreshSim() {
this.treeEventEmitter.fire(this.simRootItem);
}
public getTreeItem(element: ModuleDataItem): vscode.TreeItem | Thenable<vscode.TreeItem> {
let itemName = element.name;
if (itemModes.has(element.icon)) {
itemName = `${element.type}(${itemName})`;
}
const expandable = canExpandable(element);
let collapsibleState;
if (!expandable) {
collapsibleState = vscode.TreeItemCollapsibleState.None;
} else if (needExpand) {
collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
} else {
collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
}
const treeItem = new vscode.TreeItem(itemName, collapsibleState);
// set contextValue file -> simulate / netlist
if (otherModes.has(element.icon)) {
treeItem.contextValue = 'other';
} else {
treeItem.contextValue = 'file';
}
// set tooltip
treeItem.tooltip = element.path;
if (!treeItem.tooltip) {
treeItem.tooltip = "can't find the module of this instance";
}
// set iconPath
treeItem.iconPath = getIconConfig(element.icon);
// set command
treeItem.command = {
title: "Open this HDL File",
// TODO : 修改这里的指令前缀
command: 'TOOL.tree.arch.openFile',
arguments: [element.path],
};
return treeItem;
}
public getChildren(element?: ModuleDataItem | undefined): vscode.ProviderResult<ModuleDataItem[]> {
if (element) {
const name = element.name;
if (name === 'sim' || name === 'src') {
element.parent = null;
return this.getTopModuleItemList(element);
} else {
return this.getInstanceItemList(element);
}
} else {
// use roots in default
return [
this.srcRootItem,
this.simRootItem,
];
}
}
public getParent(element: ModuleDataItem): vscode.ProviderResult<ModuleDataItem> {
return element.parent;
}
public getTopModuleItemList(element: ModuleDataItem): ModuleDataItem[] {
// src or sim
const hardwarePath = opeParam.prjInfo.arch.hardware;
const moduleType = element.name as keyof (SrcPath & SimPath);
const topModules = hdlParam.getTopModulesByType(moduleType);
const topModuleItemList = topModules.map<ModuleDataItem>(module => ({
icon: 'top',
type: moduleType,
name: module.name,
path: module.path,
parent: element,
}));
if (topModuleItemList.length > 0) {
const type = moduleType as keyof FirstTop;
const firstTop = topModuleItemList[0];
if (!this.firstTop[type]) {
this.setFirstTop(type, firstTop.name, firstTop.path);
}
const name = this.firstTop[type]!.name;
const path = this.firstTop[type]!.path;
const icon = this.makeFirstTopIconName(type);
const parent = element;
const tops = topModuleItemList.filter(item => item.path === path && item.name === name);
const adjustItemList = [];
if (tops.length > 0 || !hdlParam.hasHdlModule(path, name)) {
// mean that the seleted top is an original top module
// push it to the top of the *topModuleItemList*
const headItem = tops[0] ? tops[0] : topModuleItemList[0];
headItem.icon = icon;
adjustItemList.push(headItem);
for (const item of topModuleItemList) {
if (item !== headItem) {
adjustItemList.push(item);
}
}
} else {
// mean the selected top is not an original top module
// create it and add it to the head of *topModuleItemList*
const selectedTopItem: ModuleDataItem = {icon, type, name, path, parent};
adjustItemList.push(selectedTopItem);
adjustItemList.push(...topModuleItemList);
}
return adjustItemList;
}
return topModuleItemList;
}
// 获取当前模块下的子模块
public getInstanceItemList(element: ModuleDataItem): ModuleDataItem[] {
if (!element.path) {
return [];
}
const moduleDataItemList: ModuleDataItem[] = [];
const targetModule = hdlParam.getHdlModule(element.path, element.name);
if (targetModule) {
for (const instance of targetModule.getAllInstances()) {
const item: ModuleDataItem = {
icon: 'file',
type: instance.name,
name: instance.type,
path: instance.instModPath,
parent: element
};
if (item.type === element.type && // 防止递归
item.name === element.name &&
item.path === element.path) {
continue;
}
item.icon = this.judgeIcon(item, instance);
moduleDataItemList.push(item);
}
} else {
MainOutput.report(`cannot find ${element} in hdlParam when constructing treeView`, ReportType.Error);
}
return moduleDataItemList;
}
public setFirstTop(type: keyof FirstTop, name: string, path: AbsPath | undefined) {
this.firstTop[type] = {name, path};
}
private makeFirstTopIconName(type: string): string {
return 'current-' + type + '-top';
}
private judgeIcon(item: ModuleDataItem, instance: HdlInstance): string {
const workspacePath = opeParam.workspacePath;
if (hdlPath.exist(item.path)) {
if (!item.path?.includes(workspacePath)) {
return 'remote';
} else {
const langID = hdlFile.getLanguageId(item.path);
return langID;
}
} else {
if (xilinx.has(instance.type)) {
return 'cells';
} else {
return 'File Error';
}
}
}
}
const moduleTreeProvider = new ModuleTreeProvider();
export {
moduleTreeProvider,
ModuleDataItem
};

View File

@ -466,5 +466,8 @@ export {
Library, Library,
RawPrjInfo, RawPrjInfo,
toSlash, toSlash,
resolve resolve,
SimPath,
SrcPath,
DataPath
}; };

View File

@ -1,4 +1,5 @@
import * as fspath from 'path'; import * as fspath from 'path';
import * as fs from 'fs';
import { AbsPath, RelPath } from '../global'; import { AbsPath, RelPath } from '../global';
@ -76,6 +77,13 @@ function filename(path: AbsPath | RelPath): string {
return fspath.basename(path, ext); return fspath.basename(path, ext);
} }
function exist(path: AbsPath | undefined): boolean {
if (!path) {
return false;
}
return fs.existsSync(path);
}
export { export {
toSlash, toSlash,
rel2abs, rel2abs,
@ -83,5 +91,6 @@ export {
resolve, resolve,
filename, filename,
extname, extname,
basename basename,
exist
}; };

View File

@ -1,4 +1,4 @@
import { AbsPath } from '../global'; import { AbsPath, opeParam } from '../global';
import { HdlLangID } from '../global/enum'; import { HdlLangID } from '../global/enum';
import { MainOutput, ReportType } from '../global/outputChannel'; import { MainOutput, ReportType } from '../global/outputChannel';
@ -39,7 +39,10 @@ class HdlParam {
this.pathToHdlFiles.set(path, hdlFile); this.pathToHdlFiles.set(path, hdlFile);
} }
public hasHdlModule(path: AbsPath, name: string): boolean { public hasHdlModule(path: AbsPath | undefined, name: string): boolean {
if (!path) {
return false;
}
const hdlFile = this.getHdlFile(path); const hdlFile = this.getHdlFile(path);
if (!hdlFile) { if (!hdlFile) {
return false; return false;
@ -208,7 +211,43 @@ class HdlParam {
hdlFile.makeInstance(); hdlFile.makeInstance();
} }
} }
public getTopModulesByType(type: string): HdlModule[] {
const hardware = opeParam.prjInfo.arch.hardware;
if (hardware.sim === hardware.src) {
return this.getAllTopModules();
}
switch (type) {
case common.HdlFileType.Src: return this.getSrcTopModules();
case common.HdlFileType.Sim: return this.getSimTopModules();
default: return [];
}
}
public getSrcTopModules(): HdlModule[] {
const srcTopModules = this.srcTopModules;
if (!srcTopModules) {
return [];
}
const moduleFiles = [];
for (const module of srcTopModules) {
moduleFiles.push(module);
}
return moduleFiles;
}
public getSimTopModules(): HdlModule[] {
const simTopModules = this.simTopModules;
if (!simTopModules) {
return [];
}
const moduleFiles = [];
for (const module of simTopModules) {
moduleFiles.push(module);
}
return moduleFiles;
}
}; };
const hdlParam = new HdlParam(); const hdlParam = new HdlParam();