In my TypeScript extension, I have a custom view that lists all of the JSON files available inside of workspaceRoot/Data
What I'd like to do is run the toggleSelectedIcon()
method I have on a type extending TreeItem. Problem so far is I'm not sure how to properly do this after I've initially built my view.
My class file
import * as vscode from "vscode";
import * as path from "path";
import * as fs from "fs";
export class DataMenuProvider implements vscode.TreeDataProvider<DataMenu> {
private _onDidChangeTreeData: vscode.EventEmitter<DataMenu | undefined> = new vscode.EventEmitter<DataMenu | undefined>();
readonly onDidChangeTreeData: vscode.Event<DataMenu | undefined> = this._onDidChangeTreeData.event;
private _workspaceRoot;
private _workspaceFolders;
private _dataFolder;
constructor() {
this._workspaceRoot = vscode.workspace.rootPath;
this._workspaceFolders = fs.readdirSync(this._workspaceRoot);
let workspaceFolders: Array<string> = this._workspaceFolders;
// check if there is a Data folder or not
for (let folder in workspaceFolders) {
if (workspaceFolders[folder] === "Data") {
// check that the finding is a directory
let checkIsDirectory = fs
.statSync(`${this._workspaceRoot}\\Data`)
.isDirectory();
if (checkIsDirectory === true) {
this._dataFolder = `${this._workspaceRoot}\\Data`;
} else {
vscode.window.showErrorMessage(
"Workspace root does not contain a folder named 'Data'"
);
}
}
}
}
toggleChild() {
}
refresh() {
this._onDidChangeTreeData.fire();
}
getTreeItem(element?: DataMenu): vscode.TreeItem {
return element;
}
getChildren(element?: DataMenu): DataMenu[] {
if (element) {
return element.children;
} else {
let jsonChildren = this.getJSONFiles(this._dataFolder);
let listParent = new DataMenu(
"Objects",
vscode.TreeItemCollapsibleState.Expanded,
this._workspaceRoot,
null,
null,
[]
);
let dataMenu = this.createDataMenu(listParent, jsonChildren);
return [dataMenu];
}
}
createDataMenu(parent: DataMenu, children: any[]) {
let menuChildren: DataMenu[] = [];
let myCommand: vscode.Command = {command: 'dataMenu.toggleSelection', title: 'Toggle Selection', };
// create jsonChildren from getChildren as DataMenu objects
for (let item in children) {
let childMenuItem = new DataMenu(
children[item],
vscode.TreeItemCollapsibleState.None,
vscode.Uri.file(`${this._workspaceRoot}\\${children[item]}`),
vscode.FileType.File,
false,
null,
myCommand,
{
light: path.join(
__filename,
"..",
"..",
"resources",
"light",
"check-empty.svg"
),
dark: path.join(
__filename,
"..",
"..",
"resources",
"dark",
"check-empty.svg"
)
}
);
menuChildren.push(childMenuItem);
}
parent.children = menuChildren;
return parent;
}
getJSONFiles(source: fs.PathLike) {
let children = fs.readdirSync(source, "utf-8");
let extensionType = ".json";
let targetChildren = [];
for (let child in children) {
if (typeof children[child] === "string") {
let currentChild = children[child].toString();
if (path.extname(currentChild).toLowerCase() === extensionType) {
targetChildren.push(currentChild);
}
}
}
if (targetChildren.length > 0) {
return targetChildren;
} else {
return null;
}
}
normalizeNFC(items: string): string;
normalizeNFC(items: string[]): string[];
normalizeNFC(items: string | string[]): string | string[] {
if (process.platform !== "darwin") {
return items;
}
if (Array.isArray(items)) {
return items.map(item => item.normalize("NFC"));
}
return items.normalize("NFC");
}
}
export class FileStat implements vscode.FileStat {
constructor(private fsStat: fs.Stats) {}
get type(): vscode.FileType {
return this.fsStat.isFile()
? vscode.FileType.File
: this.fsStat.isDirectory()
? vscode.FileType.Directory
: this.fsStat.isSymbolicLink()
? vscode.FileType.SymbolicLink
: vscode.FileType.Unknown;
}
get isFile(): boolean | undefined {
return this.fsStat.isFile();
}
get isDirectory(): boolean | undefined {
return this.fsStat.isDirectory();
}
get isSymbolicLink(): boolean | undefined {
return this.fsStat.isSymbolicLink();
}
get size(): number {
return this.fsStat.size;
}
get ctime(): number {
return this.fsStat.ctime.getTime();
}
get mtime(): number {
return this.fsStat.mtime.getTime();
}
}
export class DataMenu extends vscode.TreeItem {
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
public uri: vscode.Uri,
public type?: vscode.FileType,
public isSelected?: boolean,
public children?: DataMenu[],
public readonly command?: vscode.Command,
public iconPath?
) {
super(label, collapsibleState);
}
toggleSelectedIcon(): void {
let newLightUncheckedIcon: any;
let newDarkUncheckedIcon: any;
let newLightCheckedIcon: any;
let newDarkCheckedIcon: any;
newLightUncheckedIcon = path.join(
__filename,
"..",
"..",
"resources",
"light",
"check-empty.svg"
);
newDarkUncheckedIcon = path.join(
__filename,
"..",
"..",
"resources",
"dark",
"check-empty.svg"
);
newLightCheckedIcon = path.join(
__filename,
"..",
"..",
"resources",
"light",
"check.svg"
);
newDarkCheckedIcon = path.join(
__filename,
"..",
"..",
"resources",
"dark",
"check.svg"
);
if (this.iconPath === undefined) {
this.iconPath = {
light: newLightUncheckedIcon,
dark: newDarkUncheckedIcon
};
this.isSelected = false;
} else if (
this.iconPath.light === newLightUncheckedIcon ||
this.iconPath.dark === newDarkUncheckedIcon
) {
this.iconPath = { light: newLightCheckedIcon, dark: newDarkCheckedIcon };
this.isSelected = true;
} else if (
this.iconPath.light === newLightCheckedIcon ||
this.iconPath.dark === newDarkCheckedIcon
) {
this.iconPath = {
light: newLightUncheckedIcon,
dark: newDarkUncheckedIcon
};
this.isSelected = false;
}
}
}
The commands section of my package.json
"commands": [
{
"command": "validateMenu.refreshList",
"title": "Validate My System",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "showValidateReport",
"title": "Show Validation Report",
"icon": {
"light": "resources/light/menu.svg",
"dark": "resources/light/menu.svg"
}
},
{
"command": "dataMenu.refreshFile",
"title": "Refresh File",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "dataMenu.toggleSelection",
"title": "Add file to sync",
"icon": {
"light": "resources/light/check-empty.svg",
"dark": "resources/dark/check-empty.svg"
}
},
{
"command": "dataMenu.refreshList",
"title": "Refresh List",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
}
],
Using this
inside of the DataMenuProvider doesn't offer me a way to get the TreeItem I clicked on in the UI, so I'm not able to use it as an index to target a specific DataMenu object and run its toggleSelectedIcon() method.
My extension.ts file:
import * as vscode from 'vscode';
import { DataMenuProvider } from './dataMenu';
export async function activate(context: vscode.ExtensionContext) {
const dataMenuProvider = new DataMenuProvider();
vscode.window.registerTreeDataProvider('dataMenu', dataMenuProvider);
vscode.commands.registerCommand('dataMenu.refreshList', () => dataMenuProvider.refresh());
vscode.commands.registerCommand('dataMenu.toggleSelection', () => dataMenuProvider.toggleChild());
}
export function deactivate() {}
How could I map a command I contribute to each DataMenu object? Or other approach?
Any help appreciated!