From f63119b8e380c7be8f5ac5463a029554c47b57c4 Mon Sep 17 00:00:00 2001 From: Dayoung Lee Date: Tue, 24 Oct 2023 14:04:44 +0900 Subject: [PATCH] temp 5 ONE-vscode-DCO-1.0-Signed-off-by: Dayoung Lee --- src/OneExplorer/ArtifactLocator.ts | 77 +----- src/OneExplorer/ConfigObject.ts | 101 ++------ src/OneExplorer/OneExplorer.ts | 392 ++++++++++------------------- src/OneExplorer/OneStorage.ts | 20 +- tsconfig.json | 3 +- 5 files changed, 171 insertions(+), 422 deletions(-) diff --git a/src/OneExplorer/ArtifactLocator.ts b/src/OneExplorer/ArtifactLocator.ts index 671c3745c..dd7e21bc2 100644 --- a/src/OneExplorer/ArtifactLocator.ts +++ b/src/OneExplorer/ArtifactLocator.ts @@ -16,67 +16,7 @@ import * as assert from "assert"; import * as path from "path"; -import * as vscode from "vscode"; - -/** - * 'Artifact' - * The collective term is and inclusive, it includes two types of files: - * (1) Pre-existing files to run ONE config, a.k.a. base models (.tflite, .onnx, ...) - * (2) Result files after running ONE config, a.k.a. products (.circle, .log, ...) - */ -export interface Artifact { - /** - * An artifact's attribute - */ - attr: ArtifactAttr; - - /** - * A full path in file system - */ - path: string; -} - -export interface ArtifactAttr { - /** - * ABOUT EXTENDED EXTENSION (WITH MULTIPLE PERIODS, *.extended.ext) - * - * Generally, file name extensions are defined from the last period. - * Let's define 'extended file extension' with multiple periods. - * - * EXAMPLE - * - * (File name) model.opt.circle.log - * (Extension) .log - * (Extended Extension) .circle.log OR opt.circle.log (selective) - */ - ext: string; - - /** - * An icon for the artifact - * - * If not set, it is set by OneExplorer Node. - */ - icon?: vscode.ThemeIcon; - - /** - * A openViewType for the artifact - * - * It is used as an argument for 'vscode.openWith' command - * to open the file with specified editor. - * - * If not set, it is set by OneExplorer Node. - * If 'default'(string), open with text editor - * - * @reference vscode.openWith - */ - openViewType?: string; - - /** - * Hidden from the default view. - * The status can be unhide by command - */ - canHide?: boolean; -} +import {ArtifactType} from "./Artifact"; /** * 'Locator' is to grep matching paths inside Ini Object @@ -210,7 +150,7 @@ export class Locator { // TODO Move to backend side with some modification export class LocatorRunner { - private artifactLocators: { artifactAttr: ArtifactAttr; locator: Locator }[] = + private artifactLocators: { type: ArtifactType, locator: Locator }[] = []; /** @@ -266,7 +206,7 @@ export class LocatorRunner { }; public register(artifactLocator: { - artifactAttr: ArtifactAttr; + type: ArtifactType; locator: Locator; }) { this.artifactLocators.push(artifactLocator); @@ -275,22 +215,21 @@ export class LocatorRunner { /** * @brief Run registered locators * - * @returns Artifact[] with paths + * @returns {ArtifactType: type, path: string}[] with paths */ - public run(iniObj: object, dir: string): Artifact[] { + public run(iniObj: object, dir: string): {type:ArtifactType, path:string}[] { assert.strictEqual( path.isAbsolute(dir), true, "FIX CALLER: dir argument must be an absolute path" ); - let artifacts: Artifact[] = []; + let artifacts: {type:ArtifactType, path:string}[] = []; - // Get Artifacts with {type, ext, path} - this.artifactLocators.forEach(({ artifactAttr, locator }) => { + this.artifactLocators.forEach(({ type, locator }) => { let filePaths: string[] = locator.locate(iniObj, dir); filePaths.forEach((filePath) => { - let artifact: Artifact = { attr: artifactAttr, path: filePath }; + let artifact = { type: type, path: filePath }; artifacts.push(artifact); }); }); diff --git a/src/OneExplorer/ConfigObject.ts b/src/OneExplorer/ConfigObject.ts index 638f4a0ea..0c7db312f 100644 --- a/src/OneExplorer/ConfigObject.ts +++ b/src/OneExplorer/ConfigObject.ts @@ -23,7 +23,8 @@ import * as vscode from "vscode"; import { RealPath } from "../Utils/Helpers"; import { Logger } from "../Utils/Logger"; -import { Artifact, Locator, LocatorRunner } from "./ArtifactLocator"; +import { Locator, LocatorRunner } from "./ArtifactLocator"; +import { Artifact, ArtifactType} from "./Artifact"; type Cfg = { "one-import-tflite": CfgOneImportTflite; @@ -64,7 +65,7 @@ export class ConfigObj { /** * a parsed config object */ - obj: { baseModels: Artifact[]; products: Artifact[] }; + obj: { baseModels: {type: ArtifactType, path: string}[]; products: {type: ArtifactType, path: string}[] }; get getBaseModels() { return this.obj.baseModels; @@ -77,7 +78,7 @@ export class ConfigObj { /** * @brief Returns only the baseModels which exists in file system */ - get getBaseModelsExists() { + get getBaseModelsExists() : {type: ArtifactType, path: string}[]{ return this.obj.baseModels.filter((artifact) => RealPath.exists(artifact.path) ); @@ -86,7 +87,7 @@ export class ConfigObj { /** * @brief Returns only the products which exists in file system */ - get getProductsExists() { + get getProductsExists() : {type: ArtifactType, path: string}[]{ return this.obj.products.filter((artifact) => RealPath.exists(artifact.path) ); @@ -211,42 +212,33 @@ export class ConfigObj { private static parseBaseModels = ( filePath: string, iniObj: object - ): Artifact[] => { + ): {type: ArtifactType, path: string}[] => { const dir = path.dirname(filePath); let locatorRunner = new LocatorRunner(); locatorRunner.register({ - artifactAttr: { - ext: ".tflite", - icon: new vscode.ThemeIcon("symbol-variable"), - }, + type: "BASEMODEL_TFLITE", locator: new Locator((value: string) => LocatorRunner.searchWithExt(".tflite", value) ), }); locatorRunner.register({ - artifactAttr: { - ext: ".pb", - icon: new vscode.ThemeIcon("symbol-variable"), - }, + type: "BASEMODEL_PB", locator: new Locator((value: string) => LocatorRunner.searchWithExt(".pb", value) ), }); locatorRunner.register({ - artifactAttr: { - ext: ".onnx", - icon: new vscode.ThemeIcon("symbol-variable"), - }, + type: "BASEMODEL_ONNX", locator: new Locator((value: string) => LocatorRunner.searchWithExt(".onnx", value) ), }); - let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); + let artifacts: {type: ArtifactType, path: string}[] = locatorRunner.run(iniObj, dir); if (artifacts.length > 1) { // TODO Notify the error with a better UX @@ -279,7 +271,7 @@ export class ConfigObj { private static parseProducts = ( filePath: string, iniObj: object - ): Artifact[] => { + ): {path: string, type: ArtifactType} [] => { const dir = path.dirname(filePath); let locatorRunner = new LocatorRunner(); @@ -291,34 +283,21 @@ export class ConfigObj { */ locatorRunner.register({ - artifactAttr: { - ext: ".circle", - icon: new vscode.ThemeIcon("symbol-variable"), - openViewType: "one.viewer.circle", - }, + type: "PRODUCT_CIRCLE", locator: new Locator((value: string) => LocatorRunner.searchWithExt(".circle", value) ), }); locatorRunner.register({ - artifactAttr: { - ext: ".tvn", - icon: new vscode.ThemeIcon("symbol-variable"), - openViewType: "default", - }, + type: "PRODUCT_TVN", locator: new Locator((value: string) => LocatorRunner.searchWithExt(".tvn", value) ), }); locatorRunner.register({ - artifactAttr: { - ext: ".tracealloc.json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "one.viewer.mondrian", - canHide: true, - }, + type: "PRODUCT_MONDRIAN", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => filepath.replace(".tvn", ".tracealloc.json") @@ -331,12 +310,7 @@ export class ConfigObj { // REQUIRES: .tvn be written in the config file. // This rule is added to show a trace.json file generated by `one.toolchain.profileModel` command. locatorRunner.register({ - artifactAttr: { - ext: ".trace.json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "default", - canHide: true, - }, + type: "PRODUCT_CHROME_TRACE", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => filepath.replace(".tvn", ".trace.json") @@ -345,12 +319,7 @@ export class ConfigObj { }); locatorRunner.register({ - artifactAttr: { - ext: ".json", - icon: new vscode.ThemeIcon("graph"), - openViewType: "default", - canHide: true, - }, + type: "PRODUCT_CHROME_TRACE", locator: new Locator( (value: string) => { return LocatorRunner.searchWithCommandOption( @@ -365,12 +334,7 @@ export class ConfigObj { }); locatorRunner.register({ - artifactAttr: { - ext: ".tv2m", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, + type: "PRODUCT_TV2M", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => filepath.replace(".tvn", ".tv2m") @@ -379,12 +343,7 @@ export class ConfigObj { }); locatorRunner.register({ - artifactAttr: { - ext: ".tv2o", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, + type: "PRODUCT_TV2O", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => filepath.replace(".tvn", ".tv2o") @@ -393,12 +352,7 @@ export class ConfigObj { }); locatorRunner.register({ - artifactAttr: { - ext: ".tv2w", - icon: new vscode.ThemeIcon("symbol-method"), - openViewType: "default", - canHide: true, - }, + type: "PRODUCT_TV2W", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".tvn", value).map((filepath) => filepath.replace(".tvn", ".tv2w") @@ -407,13 +361,7 @@ export class ConfigObj { }); locatorRunner.register({ - // 'default' view type is 'text editor' (vscode.openWith) - artifactAttr: { - ext: ".circle.log", - openViewType: "default", - icon: vscode.ThemeIcon.File, - canHide: true, - }, + type: "PRODUCT_CIRCLE_LOG", locator: new Locator((value: string) => { return LocatorRunner.searchWithExt(".circle", value).map((filepath) => filepath.replace(".circle", ".circle.log") @@ -421,14 +369,7 @@ export class ConfigObj { }), }); - /** - * When you add a new product type, please append the ext type to - * OneTreeDataProvider.fileWatcher too, to prevent a bug. - * - * TODO Provide better structure to remove this extra work - */ - - let artifacts: Artifact[] = locatorRunner.run(iniObj, dir); + let artifacts: {path: string, type: ArtifactType} [] = locatorRunner.run(iniObj, dir); return artifacts; }; diff --git a/src/OneExplorer/OneExplorer.ts b/src/OneExplorer/OneExplorer.ts index 8a39b0f67..ce4871c3c 100644 --- a/src/OneExplorer/OneExplorer.ts +++ b/src/OneExplorer/OneExplorer.ts @@ -23,8 +23,9 @@ import { CfgEditorPanel } from "../CfgEditor/CfgEditorPanel"; import { obtainWorkspaceRoots } from "../Utils/Helpers"; import { Logger } from "../Utils/Logger"; -import { ArtifactAttr } from "./ArtifactLocator"; import { OneStorage } from "./OneStorage"; +import { ArtifactAttrProvider, ArtifactType } from "./Artifact"; +import { OneTreeItemGenerator } from "./OneTreeItemGenerator"; // Exported for unit testing only export { @@ -33,7 +34,7 @@ export { DirectoryNode as _unit_test_DirectoryNode, NodeFactory as _unit_test_NodeFactory, NodeType as _unit_test_NodeType, - OneNode as _unit_test_OneNode, + OneTreeItem as _unit_test_OneNode, ProductNode as _unit_test_ProductNode, }; @@ -65,63 +66,58 @@ export { * the config file. * */ -export enum NodeType { +export type NodeType = /** * A directory which contains one or more baseModel. */ - directory, + "directory" | /** * A base model from which ONE imports 'circle'. * EXAMPLE: .onnx, .tflite, .tf */ - baseModel, + "baseModel"| - /** + /* * An ONE configuration file for onecc. * Which imports a targeted 'baseModel' (NOTE baseModel:config has 1:N relationship) */ - config, + "config"| /** * All the result files obtained by running ONE config. * * EXAMPLE: .circle, .tvn, .log */ - product, -} + "product"; + export abstract class Node { abstract readonly type: NodeType; public readonly id: string; + public artifactType: ArtifactType; /** * @protected _childNodes * `undefined` when it's not build yet. * If it has no child, it is an empty array. */ - protected _childNodes: Node[] | undefined; + protected _childNodes?: Node[]; + protected _parent?: Node; + uri: vscode.Uri; /** - * @protected _parent - * `undefined` only if it has no parent (tree root) + * + * @param uri + * @param artifactType + * @param parent 'undefined' if it's root */ - protected _parent: Node | undefined; - uri: vscode.Uri; - - abstract icon: vscode.ThemeIcon; - abstract openViewType: string | undefined; - abstract canHide: boolean; - - constructor(uri: vscode.Uri, parent: Node | undefined) { + constructor(uri: vscode.Uri, artifactType: ArtifactType, parent?: Node) { this.id = Math.random().toString(); - this._childNodes = undefined; + this.artifactType = artifactType; this.uri = uri; this._parent = parent; } - /** - * Build `_childNodes` on demand, which is initially undefined - */ abstract _buildChildren: () => void; getChildren(): Node[] { @@ -137,12 +133,29 @@ export abstract class Node { this._childNodes = undefined; } - get path(): string { - return this.uri.fsPath; + dropChild(child: Node): void { + this._childNodes = this._childNodes?.filter( + (node) => node.path !== child.path + ); } - get parent(): Node | undefined { - return this._parent; + adoptChild(child: Node): void { + if (!this._childNodes) { + this._childNodes = [child]; + } else { + this._childNodes.push(child); + } + } + + set parent(adopter: Node) { + const dropper = this._parent; + dropper!.dropChild(this); + adopter.adoptChild(this); + this._parent = adopter; + } + + get path(): string { + return this.uri.fsPath; } get name(): string { @@ -152,60 +165,28 @@ export abstract class Node { get ext(): string { return path.extname(this.uri.fsPath); } - - get typeAsString(): string { - // Return a NodeType as a string value - return NodeType[this.type]; - } } class NodeFactory { - static create( - type: NodeType, - fpath: string, - parent: Node | undefined, - attr?: ArtifactAttr - ): Node { + static create(type: NodeType, fpath: string, parent?: Node, artifactType?: ArtifactType): Node { const uri = vscode.Uri.file(fpath); let node: Node; switch (type) { - case NodeType.directory: { - assert.strictEqual( - attr, - undefined, - "Directory nodes cannot have attributes" - ); - node = new DirectoryNode(uri, parent); + case "directory": { + node = new DirectoryNode(uri, "DIRECTORY", parent); break; } - case NodeType.baseModel: { - node = new BaseModelNode( - uri, - parent, - attr?.openViewType, - attr?.icon, - attr?.canHide - ); + case "baseModel": { + node = new BaseModelNode(uri, artifactType!, parent); break; } - case NodeType.config: { - assert.strictEqual( - attr, - undefined, - "Config nodes cannot have attributes" - ); - node = new ConfigNode(uri, parent); + case "config": { + node = new ConfigNode(uri, "CONFIG_ONE", parent); break; } - case NodeType.product: { - node = new ProductNode( - uri, - parent, - attr?.openViewType, - attr?.icon, - attr?.canHide - ); + case "product": { + node = new ProductNode(uri, artifactType!, parent); break; } default: { @@ -219,21 +200,11 @@ class NodeFactory { } } -class DirectoryNode extends Node { - readonly type = NodeType.directory; - - // DO NOT OPEN DIRECTORY AS ALWAYS - readonly openViewType = undefined; - // DISPLAY FOLDER ICON AS ALWAYS - readonly icon = vscode.ThemeIcon.Folder; - // DO NOT HIDE DIRECTORY NODE AS ALWAYS - readonly canHide = false; +class DirectoryNode extends Node{ + readonly type = "directory"; - constructor(uri: vscode.Uri, parent: Node | undefined) { - assert.ok(fs.statSync(uri.fsPath)); - assert.strictEqual(fs.statSync(uri.fsPath).isDirectory(), true); - - super(uri, parent); + constructor(uri: vscode.Uri, artifactType: ArtifactType, parent?: Node) { + super(uri, artifactType, parent); } /** @@ -254,45 +225,40 @@ class DirectoryNode extends Node { const fstat = fs.statSync(fpath); if (fstat.isDirectory()) { - const dirNode = NodeFactory.create(NodeType.directory, fpath, this); + const dirNode = NodeFactory.create( + "directory", + fpath, + this + ); if (dirNode && dirNode.getChildren().length > 0) { this._childNodes!.push(dirNode); } } else if (fstat.isFile()) { - if (fname.endsWith(".pb")) { + if(fname.endsWith(".pb")){ const baseModelNode = NodeFactory.create( - NodeType.baseModel, + "baseModel", fpath, this, - { - ext: ".pb", - openViewType: "default", - } + "BASEMODEL_PB" ); this._childNodes!.push(baseModelNode); - } else if (fname.endsWith(".tflite")) { + }else if(fname.endsWith(".tflite")){ const baseModelNode = NodeFactory.create( - NodeType.baseModel, + "baseModel", fpath, this, - { - ext: ".tflite", - openViewType: "one.viewer.circle", - } + "BASEMODEL_TFLITE" ); this._childNodes!.push(baseModelNode); - } else if (fname.endsWith(".onnx")) { + }else if(fname.endsWith(".onnx")){ const baseModelNode = NodeFactory.create( - NodeType.baseModel, + "baseModel", fpath, this, - { - ext: ".onnx", - openViewType: "one.viewer.circle", - } + "BASEMODEL_ONNX" ); this._childNodes!.push(baseModelNode); @@ -303,34 +269,10 @@ class DirectoryNode extends Node { } class BaseModelNode extends Node { - readonly type = NodeType.baseModel; - - static readonly extList = [".tflite", ".pb", ".onnx"]; - // Do not open file as default - static defaultOpenViewType = undefined; - // Display 'symbol-variable' icon to represent model file as default - static defaultIcon = new vscode.ThemeIcon("symbol-variable"); - // Show file always as default - static defaultCanHide = false; - - openViewType: string | undefined = BaseModelNode.defaultOpenViewType; - icon = BaseModelNode.defaultIcon; - canHide = BaseModelNode.defaultCanHide; - - constructor( - uri: vscode.Uri, - parent: Node | undefined, - openViewType: string | undefined = BaseModelNode.defaultOpenViewType, - icon: vscode.ThemeIcon = BaseModelNode.defaultIcon, - canHide: boolean = BaseModelNode.defaultCanHide - ) { - assert.ok(fs.statSync(uri.fsPath)); - assert.strictEqual(fs.statSync(uri.fsPath).isFile(), true); - - super(uri, parent); - this.openViewType = openViewType; - this.icon = icon; - this.canHide = canHide; + readonly type = "baseModel"; + + constructor(uri: vscode.Uri, artifactType: ArtifactType, parent?: Node) { + super(uri, artifactType, parent); } /** @@ -350,7 +292,7 @@ class BaseModelNode extends Node { return; } configPaths.forEach((configPath) => { - const configNode = NodeFactory.create(NodeType.config, configPath, this); + const configNode = NodeFactory.create("config", configPath, this); if (configNode) { this._childNodes!.push(configNode); @@ -360,36 +302,11 @@ class BaseModelNode extends Node { } class ConfigNode extends Node { - readonly type = NodeType.config; - - static readonly extList = [".cfg"]; - // Open file with one.editor.cfg as default - static defaultOpenViewType = "one.editor.cfg"; - // Display gear icon as default - static defaultIcon = new vscode.ThemeIcon("gear"); - // Show file always as default - static defaultCanHide = false; - - openViewType = ConfigNode.defaultOpenViewType; - icon = ConfigNode.defaultIcon; - canHide = ConfigNode.defaultCanHide; - - constructor( - uri: vscode.Uri, - parent: Node | undefined, - openViewType: string = ConfigNode.defaultOpenViewType, - icon: vscode.ThemeIcon = ConfigNode.defaultIcon, - canHide: boolean = ConfigNode.defaultCanHide - ) { - assert.ok(fs.statSync(uri.fsPath)); - assert.strictEqual(fs.statSync(uri.fsPath).isFile(), true); - - super(uri, parent); - this.openViewType = openViewType; - this.icon = icon; - this.canHide = canHide; - } + readonly type = "config"; + constructor(uri: vscode.Uri, artifactType: ArtifactType, parent?: Node) { + super(uri, artifactType, parent); + } /** * Build a sub-tree under the node * @@ -407,15 +324,10 @@ class ConfigNode extends Node { return; } - const products = cfgObj.getProductsExists; + const products: {type: ArtifactType, path: string}[] = cfgObj.getProductsExists; - products.forEach((product) => { - const productNode = NodeFactory.create( - NodeType.product, - product.path, - this, - product.attr - ); + products.forEach(({type, path}) => { + const productNode = NodeFactory.create("product", path, this, type); if (productNode) { this._childNodes!.push(productNode); @@ -425,42 +337,10 @@ class ConfigNode extends Node { } class ProductNode extends Node { - readonly type = NodeType.product; - - static readonly extList = [ - ".circle", - ".tvn", - ".tv2m", - ".tv2w", - ".tv2o", - ".json", - ".circle.log", - ]; - // Do not open file as default - static defaultOpenViewType = undefined; - // Display file icon as default - static defaultIcon = vscode.ThemeIcon.File; - // Show file always as default - static defaultCanHide = false; - - openViewType: string | undefined = ProductNode.defaultOpenViewType; - icon = ProductNode.defaultIcon; - canHide = ProductNode.defaultCanHide; - - constructor( - uri: vscode.Uri, - parent: Node | undefined, - openViewType: string | undefined = ProductNode.defaultOpenViewType, - icon: vscode.ThemeIcon = ProductNode.defaultIcon, - canHide: boolean = ProductNode.defaultCanHide - ) { - assert.ok(fs.statSync(uri.fsPath)); - assert.strictEqual(fs.statSync(uri.fsPath).isFile(), true); - - super(uri, parent); - this.openViewType = openViewType; - this.icon = icon; - this.canHide = canHide; + readonly type = "product"; + + constructor(uri: vscode.Uri, artifactType: ArtifactType, parent?: Node) { + super(uri, artifactType, parent); } _buildChildren = (): void => { @@ -468,11 +348,12 @@ class ProductNode extends Node { }; } -export class OneNode extends vscode.TreeItem { - constructor( - public readonly collapsibleState: vscode.TreeItemCollapsibleState, - public readonly node: Node - ) { + +export class OneTreeItem extends vscode.TreeItem { + constructor(public readonly node: Node, + icon: vscode.ThemeIcon, + collapsibleState: vscode.TreeItemCollapsibleState, + openViewType?: string) { super(node.name, collapsibleState); this.id = node.id; @@ -480,15 +361,15 @@ export class OneNode extends vscode.TreeItem { this.description = true; this.tooltip = `${this.node.path}`; - if (node.openViewType) { + if (openViewType) { this.command = { command: "vscode.openWith", title: "Open with Custom Viewer", - arguments: [node.uri, node.openViewType], + arguments: [node.uri, openViewType], }; } - this.iconPath = node.icon; + this.iconPath = icon; // To show contextual menu on items in OneExplorer, // we have to use "when" clause under "view/item/context" under "menus". @@ -505,7 +386,7 @@ export class OneNode extends vscode.TreeItem { // basemode.tflite // product.tvn const extname = path.extname(node.uri.fsPath); - this.contextValue = node.typeAsString + extname; + this.contextValue = node.type as string + extname; } } @@ -522,13 +403,17 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { private _treeView: vscode.TreeView | undefined; private _workspaceRoots: vscode.Uri[] = []; - public static didHideExtra: boolean = false; + public _oneTreeItemGenerator?: OneTreeItemGenerator; + public _nodePropDef?: NodePropDef; + public static didHideExtra: boolean = false; public static hasSelectedCfg: boolean = false; public static register(context: vscode.ExtensionContext) { const provider = new OneTreeDataProvider(context.extension.extensionKind); + provider._nodePropProvider = new NodePropProvider(new NodeOptionProvider); + provider._oneTreeItemGenerator = new OneTreeItemGenerator(new NodeOptionProvider); provider._treeView = vscode.window.createTreeView("OneExplorerView", { treeDataProvider: provider, showCollapseAll: true, @@ -543,13 +428,7 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { provider.refresh(); }), provider.fileWatcher.onDidChange((uri: vscode.Uri) => { - if ( - [ - ...BaseModelNode.extList, - ...ConfigNode.extList, - ...ProductNode.extList, - ].includes(path.parse(uri.path).ext) - ) { + if (path.extname(uri.fsPath) === ".cfg") { Logger.info( "OneExploer", `Refresh explorer view on a file change in '${uri.path}'` @@ -798,6 +677,17 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { this.refresh(undefined, false); } + init(): void { + this._tree = this._workspaceRoots.map( + (root) => + NodeFactory.create( + "directory", + root.fsPath, + undefined + ) as DirectoryNode + ); + } + /** * Refresh the tree under the given Node * @command one.explorer.refresh @@ -903,9 +793,9 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { * @todo prohibit special characters from new name for security ('..', '*', etc) */ async rename(node: Node): Promise { - assert.ok(node.type === NodeType.config); + assert.ok(node.type === "config"); - if (node.type !== NodeType.config) { + if (node.type !== "config") { return; } @@ -929,7 +819,7 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { * @todo prohibit special characters from new name for security ('..', '*', etc) */ async refactor(node: Node): Promise { - assert.ok(node.type === NodeType.baseModel); + assert.ok(node.type === "baseModel"); // Ask the new name of the model file const newname = await this.askNewName(node); @@ -995,7 +885,7 @@ export class OneTreeDataProvider implements vscode.TreeDataProvider { * @command one.explorer.delete */ async delete(node: Node): Promise { - const isDirectory = node.type === NodeType.directory; + const isDirectory = node.type === "directory"; const title = isDirectory ? `Are you sure you want to delete '${node.name}' and its contents?` @@ -1114,7 +1004,7 @@ input_path=${modelName}.${extName} const configs: Node[] = []; for (var node of this._treeView!.selection) { - if (node.type === NodeType.config) { + if (node.type === "config") { configs.push(node); } } @@ -1131,23 +1021,8 @@ input_path=${modelName}.${extName} return element.parent; } - getTreeItem(node: Node): OneNode { - switch (node.type) { - case NodeType.directory: - return new OneNode(vscode.TreeItemCollapsibleState.Expanded, node); - case NodeType.product: - return new OneNode(vscode.TreeItemCollapsibleState.None, node); - case NodeType.baseModel: - case NodeType.config: - return new OneNode( - node.getChildren().length > 0 - ? vscode.TreeItemCollapsibleState.Collapsed - : vscode.TreeItemCollapsibleState.None, - node - ); - default: - throw Error("Undefined NodeType"); - } + getTreeItem(node: Node): OneTreeItem { + return this._oneTreeItemGenerator!.generate(node); } /** @@ -1156,34 +1031,27 @@ input_path=${modelName}.${extName} * (2) mapping the nodes to OneNode(Tree Item) using getTreeItem() * Therefore, to decide whether to display node or not, getChildren() should make decision. */ - getChildren(element?: Node): vscode.ProviderResult { - if (!element) { + getChildren(node?: Node): vscode.ProviderResult { + if (!node) { return this.getTree(); } - return OneTreeDataProvider.didHideExtra - ? element.getChildren().filter((node) => !node.canHide) - : element.getChildren(); + const getVisibleChildren = (node: Node): Node[] => { + return OneTreeDataProvider.didHideExtra + ? node.getChildren().filter((node) => !node.canHide) + : node.getChildren(); + }; + + return node.getVisibleChildren(); } /** * Get the root of the tree */ private getTree(): Node[] | undefined { - if (!this._workspaceRoots || this._workspaceRoots.length === 0) { - return undefined; - } - - if (!this._tree) { - this._tree = this._workspaceRoots.map( - (root) => - NodeFactory.create( - NodeType.directory, - root.fsPath, - undefined - ) as DirectoryNode - ); - } + this._tree = + this._tree ?? + this._workspaceRoots?.map((root) => NodeFactory.create("directory", root.fsPath, undefined)); return this._tree; } diff --git a/src/OneExplorer/OneStorage.ts b/src/OneExplorer/OneStorage.ts index 3572cb3d7..bf4d0151d 100644 --- a/src/OneExplorer/OneStorage.ts +++ b/src/OneExplorer/OneStorage.ts @@ -106,12 +106,12 @@ class CfgToCfgObjMap { // TODO Revisit here public reset(type: NodeType, path: string) { switch (type) { - case NodeType.config: + case "config": this._map.delete(path); break; - case NodeType.baseModel: - case NodeType.product: - case NodeType.directory: + case "baseModel": + case "product": + case "directory": default: assert.fail(`Cannot reach here`); break; @@ -126,7 +126,7 @@ class CfgToCfgObjMap { */ public update(type: NodeType, path: string, newpath?: string) { switch (type) { - case NodeType.config: { + case "config": { this._map.delete(path); if (newpath) { @@ -139,9 +139,9 @@ class CfgToCfgObjMap { break; } - case NodeType.baseModel: - case NodeType.product: - case NodeType.directory: + case "baseModel": + case "product": + case "directory": default: assert.fail(`Cannot reach here`); break; @@ -228,7 +228,7 @@ export class OneStorage { instance._nodeMap.delete(node.path); switch (node.type) { - case NodeType.config: + case "config": instance._cfgToCfgObjMap.reset(node.type, node.path); break; default: @@ -289,7 +289,7 @@ export class OneStorage { inst._nodeMap.insert(node.path, node); - if (node.type === NodeType.config) { + if (node.type === "config") { if (!inst._cfgToCfgObjMap.get(node.path)) { const cfgObj = ConfigObj.createConfigObj(node.uri); if (cfgObj) { diff --git a/tsconfig.json b/tsconfig.json index e8e14110d..d078f662c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "rootDir": "src", "strict": true, /* enable all strict type-checking options */ "resolveJsonModule" : true, - "esModuleInterop": true + "esModuleInterop": true, + "experimentalDecorators": true, /* Additional Checks */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */