diff --git a/package.json b/package.json index 0718231..afc4800 100644 --- a/package.json +++ b/package.json @@ -232,6 +232,11 @@ { "id": "bazelTaskOutline", "name": "Bazel Run Targets" + }, + { + "id": "rootFileViewer", + "name": "Project Root (Files)", + "when": "isMultiRoot" } ] }, diff --git a/src/extension.ts b/src/extension.ts index 7cdeabc..611d09f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,6 +21,7 @@ import { registerLSClient } from './loggingTCPServer'; import { ProjectViewManager } from './projectViewManager'; import { BazelRunTargetProvider } from './provider/bazelRunTargetProvider'; import { BazelTaskProvider } from './provider/bazelTaskProvider'; +import { RootFileViewProvider } from './provider/rootFileViewProvider'; import { getWorkspaceRoot, initBazelProjectFile, @@ -45,6 +46,10 @@ export async function activate(context: ExtensionContext) { BazelRunTargetProvider.instance ); tasks.registerTaskProvider('bazel', new BazelTaskProvider()); + window.registerTreeDataProvider( + 'rootFileViewer', + RootFileViewProvider.instance + ); BazelLanguageServerTerminal.trace('extension activated'); @@ -66,6 +71,11 @@ export async function activate(context: ExtensionContext) { 'isBazelWorkspaceRoot', isBazelWorkspaceRoot() ); + commands.executeCommand( + 'setContext', + 'isMultiRoot', + workspace.workspaceFile?.fsPath.includes('code-workspace') + ); // create .eclipse/.bazelproject file if DNE if (isBazelWorkspaceRoot()) { initBazelProjectFile(); @@ -134,7 +144,7 @@ export async function activate(context: ExtensionContext) { registerLSClient(); } -export function deactivate() {} +export function deactivate() { } function syncProjectView(): void { if (!isRedhatJavaReady()) { diff --git a/src/provider/rootFileViewProvider.ts b/src/provider/rootFileViewProvider.ts new file mode 100644 index 0000000..7ae8d90 --- /dev/null +++ b/src/provider/rootFileViewProvider.ts @@ -0,0 +1,76 @@ +import path from 'path'; +import { + Event, + EventEmitter, + FileSystemWatcher, + ProviderResult, + RelativePattern, + ThemeIcon, + TreeDataProvider, + TreeItem, + TreeItemCollapsibleState, + Uri, + workspace, +} from 'vscode'; +import { getWorkspaceRoot } from '../util'; + +const WORKSPACE_ROOT = getWorkspaceRoot(); + +export class RootFileViewProvider implements TreeDataProvider { + private static _instance: RootFileViewProvider; + + private _filesWatcher: FileSystemWatcher; + private _onDidChangeTreeData: EventEmitter = + new EventEmitter(); + readonly onDidChangeTreeData: Event = + this._onDidChangeTreeData.event; + + private constructor() { + this._filesWatcher = workspace.createFileSystemWatcher( + new RelativePattern(WORKSPACE_ROOT, '*') + ); + this._filesWatcher.onDidChange((f) => this._onDidChangeTreeData.fire()); + this._filesWatcher.onDidCreate((f) => this._onDidChangeTreeData.fire()); + this._filesWatcher.onDidDelete((f) => this._onDidChangeTreeData.fire()); + } + + public static get instance(): RootFileViewProvider { + if (!this._instance) { + this._instance = new RootFileViewProvider(); + } + return this._instance; + } + + getTreeItem(element: string): TreeItem | Thenable { + return new FileItem(element); + } + getChildren(element?: string | undefined): ProviderResult { + if (!element) { + return workspace.fs + .readDirectory(Uri.file(WORKSPACE_ROOT)) + .then((val) => { + return val.filter((v) => v[1] === 1).map((v) => v[0]); + }); + } + return []; + } +} + +class FileItem extends TreeItem { + constructor(fileName: string, collapsibleState?: TreeItemCollapsibleState) { + super(fileName, collapsibleState); + this.command = { + title: fileName, + command: 'vscode.open', + arguments: [Uri.file(`${WORKSPACE_ROOT}${path.sep}${fileName}`)], + }; + switch (fileName.split('.').reverse()[0]) { + case 'json': + this.iconPath = new ThemeIcon('json'); + break; + default: + this.iconPath = new ThemeIcon('file'); + break; + } + } +}