Skip to content

Commit

Permalink
clone
Browse files Browse the repository at this point in the history
  • Loading branch information
maxisam committed Oct 21, 2024
1 parent ae64bff commit 729af97
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 22 deletions.
12 changes: 12 additions & 0 deletions src/commands/CrudCommands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum vsCodeCommands {
Open = 'vscode.open',
}

export enum DirectoryProviderCommands {
SelectItem = 'directoryprovider/selectitem',
OpenItem = 'directoryprovider/openitem',
RefreshEntry = 'directoryprovider/refreshentry',
CantRemoveItem = 'directoryprovider/cantremoveitem',
RemoveItem = 'directoryprovider/removeitem',
RemoveAllItems = 'directoryprovider/removeallitems',
}
53 changes: 31 additions & 22 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
import { DirectoryProviderCommands, vsCodeCommands } from './commands/CrudCommands';
import { DirectoryWorker } from './operator/DirectoryWorker';
import { DirectoryProvider } from './provider/DirectoryProvider';

export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log(
'Congratulations, your extension "awesome-vscode-extension-boilerplate" is now active!',
);
const directoryOperator = new DirectoryWorker(context, vscode.workspace.workspaceFolders);

const directoryProvider = new DirectoryProvider(directoryOperator);

// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand(
'awesome-vscode-extension-boilerplate.helloWorld',
() => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.registerTreeDataProvider('explorer-bookmark', directoryProvider);

context.subscriptions.push(
vscode.commands.registerCommand(DirectoryProviderCommands.RefreshEntry, () =>
directoryProvider.refresh(),
),
vscode.commands.registerCommand(DirectoryProviderCommands.OpenItem, (file) => {
vscode.commands.executeCommand(
vsCodeCommands.Open,
vscode.Uri.parse(file.resourceUri.path),
);
}),
vscode.commands.registerCommand(DirectoryProviderCommands.SelectItem, (args) =>
directoryProvider.selectItem(vscode.Uri.parse(args.path)),
),
vscode.commands.registerCommand(DirectoryProviderCommands.RemoveItem, (args) => {
directoryProvider.removeItem(args.resourceUri);
}),
vscode.commands.registerCommand(DirectoryProviderCommands.CantRemoveItem, () => {
vscode.window.showInformationMessage(
'Hello World from Awesome VSCode Extension Boilerplate!',
'You can only remove items that were directly added to the view',
);
},
}),
vscode.commands.registerCommand(DirectoryProviderCommands.RemoveAllItems, () =>
directoryProvider.removeAllItems(),
),
);

context.subscriptions.push(disposable);
}

// This method is called when your extension is deactivated
export function deactivate() {}
123 changes: 123 additions & 0 deletions src/operator/DirectoryWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import * as path from 'path';

import * as vscode from 'vscode';

import { FileSystemObject } from '../types/FileSystemObject';
import type { TypedDirectory } from '../types/TypedDirectory';
import { buildTypedDirectory } from '../types/TypedDirectory';

export class DirectoryWorker {
readonly vsCodeExtensionConfigurationKey: string = 'explorer-bookmark';
readonly saveWorkspaceConfigurationSettingKey: string = 'saveWorkspace';
readonly storedBookmarksContextKey: string = 'storedBookmarks';
readonly bookmarkedDirectoryContextValue: string = 'directlyBookmarkedDirectory';

private bookmarkedDirectories: TypedDirectory[] = [];
private saveWorkspaceSetting: boolean | undefined = false;

constructor(
private extensionContext: vscode.ExtensionContext,
private workspaceRoot: readonly vscode.WorkspaceFolder[] | undefined,
) {
this.hydrateState();
}

public async getChildren(element?: FileSystemObject): Promise<FileSystemObject[]> {
if (element) {
return this.directorySearch(element.resourceUri);
} else {
return this.bookmarkedDirectories.length > 0
? this.createEntries(this.bookmarkedDirectories)
: Promise.resolve([]);
}
}

public async selectItem(uri: vscode.Uri | undefined) {
if (uri) {
this.bookmarkedDirectories.push(await buildTypedDirectory(uri));
}
this.saveBookmarks();
}

public async removeItem(uri: vscode.Uri | undefined) {
if (uri) {
const typedDirectory = await buildTypedDirectory(uri);
const index = this.bookmarkedDirectories
.map((e) => e.path)
.indexOf(typedDirectory.path);
if (index > -1) {
this.bookmarkedDirectories.splice(index, 1);
}
}
this.saveBookmarks();
}

public removeAllItems() {
this.bookmarkedDirectories = [];
this.saveBookmarks();
}

private async directorySearch(uri: vscode.Uri) {
const entries = await vscode.workspace.fs.readDirectory(uri);
return entries
.sort((a, b) => a[0].localeCompare(b[0]))
.map((item) => {
const [name, type] = item;
const isDirectory =
type === vscode.FileType.Directory
? vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None;

return new FileSystemObject(
name,
isDirectory,
vscode.Uri.file(`${uri.path}/${name}`),
);
});
}

private async createEntries(bookmarkedDirectories: TypedDirectory[]) {
const fileSystem: FileSystemObject[] = [];

for (const dir of bookmarkedDirectories) {
const { path: filePath, type: type } = dir;
const file = vscode.Uri.file(filePath);

fileSystem.push(
new FileSystemObject(
`${path.basename(dir.path)}`,
type === vscode.FileType.File
? vscode.TreeItemCollapsibleState.None
: vscode.TreeItemCollapsibleState.Collapsed,
file,
).setContextValue(this.bookmarkedDirectoryContextValue),
);
}

return fileSystem;
}

private hydrateState(): void {
this.saveWorkspaceSetting = vscode.workspace
.getConfiguration(this.saveWorkspaceConfigurationSettingKey)
.get(this.saveWorkspaceConfigurationSettingKey);
this.bookmarkedDirectories =
(this.workspaceRoot
? this.extensionContext.workspaceState.get(this.storedBookmarksContextKey)
: this.extensionContext.globalState.get(this.storedBookmarksContextKey)) || [];
}

private saveBookmarks() {
if (this.workspaceRoot) {
this.extensionContext.workspaceState.update(
this.storedBookmarksContextKey,
this.bookmarkedDirectories,
);
} else {
this.extensionContext.globalState.update(
this.storedBookmarksContextKey,
this.bookmarkedDirectories,
);
}
}
}
41 changes: 41 additions & 0 deletions src/provider/DirectoryProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as vscode from 'vscode';

import type { DirectoryWorker } from '../operator/DirectoryWorker';
import type { FileSystemObject } from '../types/FileSystemObject';

export class DirectoryProvider implements vscode.TreeDataProvider<FileSystemObject> {
private _onDidChangeTreeData: vscode.EventEmitter<FileSystemObject | undefined | null | void> =
new vscode.EventEmitter<FileSystemObject | undefined | null | void>();

readonly onDidChangeTreeData: vscode.Event<FileSystemObject | undefined | null | void> =
this._onDidChangeTreeData.event;

constructor(private directoryOperator: DirectoryWorker) {}

getTreeItem(element: FileSystemObject): vscode.TreeItem | Thenable<vscode.TreeItem> {
return element;
}

async getChildren(element?: FileSystemObject): Promise<FileSystemObject[]> {
return await this.directoryOperator.getChildren(element);
}

async selectItem(uri: vscode.Uri | undefined) {
await this.directoryOperator.selectItem(uri);
this.refresh();
}

async removeItem(uri: vscode.Uri | undefined) {
await this.directoryOperator.removeItem(uri);
this.refresh();
}

removeAllItems() {
this.directoryOperator.removeAllItems();
this.refresh();
}

refresh(): void {
this._onDidChangeTreeData.fire();
}
}
31 changes: 31 additions & 0 deletions src/types/FileSystemObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as vscode from 'vscode';

import { DirectoryProviderCommands } from '../commands/CrudCommands';

export class FileSystemObject extends vscode.TreeItem {
resourceUri: vscode.Uri;
command?: vscode.Command;

constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
uri: vscode.Uri,
) {
super(label, collapsibleState);
this.tooltip = uri.fsPath;
this.resourceUri = uri;
this.command =
collapsibleState === vscode.TreeItemCollapsibleState.None
? {
arguments: [this],
command: DirectoryProviderCommands.OpenItem,
title: this.label,
}
: undefined;
}

setContextValue(value: string) {
this.contextValue = value;
return this;
}
}
16 changes: 16 additions & 0 deletions src/types/TypedDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as vscode from 'vscode';

export class TypedDirectory {
path: string;
type: vscode.FileType;

constructor(path: string, type: vscode.FileType) {
this.path = path;
this.type = type;
}
}

export async function buildTypedDirectory(uri: vscode.Uri) {
const type = (await vscode.workspace.fs.stat(uri)).type;
return new TypedDirectory(uri.path, type);
}

0 comments on commit 729af97

Please sign in to comment.