Skip to content

Commit

Permalink
Implement 'more' toolbar item for the explorer
Browse files Browse the repository at this point in the history
Fixes #5951

- implement `more` toolbar item to display workspace root specific commands.
- implement 'new file root' and 'new folder root' commands which call the generic createFile and createFolder commands.

Signed-off-by: Vincent Fugnitto <vincent.fugnitto@ericsson.com>
  • Loading branch information
vince-fugnitto committed Aug 20, 2019
1 parent e8a29ca commit d0ec6b2
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 40 deletions.
43 changes: 41 additions & 2 deletions packages/navigator/src/browser/navigator-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
OpenerService, FrontendApplicationContribution, FrontendApplication, CompositeTreeNode
} from '@theia/core/lib/browser';
import { FileDownloadCommands } from '@theia/filesystem/lib/browser/download/file-download-command-contribution';
import { CommandRegistry, MenuModelRegistry, MenuPath, isOSX, Command, DisposableCollection } from '@theia/core/lib/common';
import { CommandRegistry, MenuModelRegistry, MenuPath, isOSX, Command, DisposableCollection, Mutable } from '@theia/core/lib/common';
import { SHELL_TABBAR_CONTEXT_MENU } from '@theia/core/lib/browser';
import { WorkspaceCommands, WorkspaceService, WorkspacePreferences } from '@theia/workspace/lib/browser';
import { FILE_NAVIGATOR_ID, FileNavigatorWidget, EXPLORER_VIEW_CONTAINER_ID } from './navigator-widget';
Expand All @@ -30,7 +30,7 @@ import { NavigatorKeybindingContexts } from './navigator-keybinding-context';
import { FileNavigatorFilter } from './navigator-filter';
import { WorkspaceNode } from './navigator-tree';
import { NavigatorContextKeyService } from './navigator-context-key-service';
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { FileSystemCommands } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
import { NavigatorDiff, NavigatorDiffCommands } from './navigator-diff';
import { UriSelection } from '@theia/core/lib/common/selection';
Expand Down Expand Up @@ -61,6 +61,13 @@ export namespace FileNavigatorCommands {
};
}

/**
* Navigator `More Actions...` toolbar item groups.
*/
export namespace NavigatorMoreToolbarGroups {
export const NEW_OPEN = '1_navigator_new_open';
}

export const NAVIGATOR_CONTEXT_MENU: MenuPath = ['navigator-context-menu'];

/**
Expand Down Expand Up @@ -95,6 +102,9 @@ export namespace NavigatorContextMenu {
@injectable()
export class FileNavigatorContribution extends AbstractViewContribution<FileNavigatorWidget> implements FrontendApplicationContribution, TabBarToolbarContribution {

@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;

@inject(NavigatorContextKeyService)
protected readonly contextKeyService: NavigatorContextKeyService;

Expand Down Expand Up @@ -340,6 +350,35 @@ export class FileNavigatorContribution extends AbstractViewContribution<FileNavi
tooltip: 'Collapse All',
priority: 1,
});

// Register 'more actions' toolbar item (consists of multiple commands for the workspace root).
const navigatorRegisterItem = (item: Mutable<TabBarToolbarItem>) => {
const commandId = item.command;
const id = 'navigator.tabbar.toolbar.' + commandId;
const command = this.commandRegistry.getCommand(commandId);
this.commandRegistry.registerCommand({ id, iconClass: command && command.iconClass }, {
execute: (w, ...args) => w instanceof FileNavigatorWidget
&& this.commandRegistry.executeCommand(commandId, ...args),
isEnabled: (w, ...args) => w instanceof FileNavigatorWidget
&& this.commandRegistry.isEnabled(commandId, ...args),
isVisible: (w, ...args) => w instanceof FileNavigatorWidget
&& this.commandRegistry.isVisible(commandId, ...args),
});
item.command = id;
toolbarRegistry.registerItem(item);
};
navigatorRegisterItem({
id: WorkspaceCommands.NEW_FILE_ROOT.id,
command: WorkspaceCommands.NEW_FILE_ROOT.id,
tooltip: 'New File',
group: NavigatorMoreToolbarGroups.NEW_OPEN,
});
navigatorRegisterItem({
id: WorkspaceCommands.NEW_FOLDER_ROOT.id,
command: WorkspaceCommands.NEW_FOLDER_ROOT.id,
tooltip: 'New Folder',
group: NavigatorMoreToolbarGroups.NEW_OPEN,
});
}

/**
Expand Down
106 changes: 68 additions & 38 deletions packages/workspace/src/browser/workspace-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,17 @@ export namespace WorkspaceCommands {
category: FILE_CATEGORY,
label: 'New File'
};
export const NEW_FILE_ROOT: Command = {
id: 'file.newFile.root',
};
export const NEW_FOLDER: Command = {
id: 'file.newFolder',
category: FILE_CATEGORY,
label: 'New Folder'
};
export const NEW_FOLDER_ROOT: Command = {
id: 'file.newFolder.root',
};
export const FILE_OPEN_WITH = (opener: OpenHandler): Command => ({
id: `file.openWith.${opener.id}`
});
Expand Down Expand Up @@ -195,47 +201,21 @@ export class WorkspaceCommandContribution implements CommandContribution {
}
});
registry.registerCommand(WorkspaceCommands.NEW_FILE, this.newWorkspaceRootUriAwareCommandHandler({
execute: uri => this.getDirectory(uri).then(parent => {
if (parent) {
const parentUri = new URI(parent.uri);
const { fileName, fileExtension } = this.getDefaultFileConfig();
const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, parent, fileName, fileExtension);

const dialog = new SingleTextInputDialog({
title: 'New File',
initialValue: vacantChildUri.path.base,
validate: name => this.validateFileName(name, parent, true)
});

dialog.open().then(name => {
if (name) {
const fileUri = parentUri.resolve(name);
this.fileSystem.createFile(fileUri.toString()).then(() => {
open(this.openerService, fileUri);
});
}
});
}
})
execute: uri => this.getDirectory(uri).then(parent => this.createNewFile(parent))
}));
registry.registerCommand(WorkspaceCommands.NEW_FILE_ROOT, {
isEnabled: () => !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened,
isVisible: () => !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened,
execute: () => this.createNewFile(this.workspaceService.workspace),
});
registry.registerCommand(WorkspaceCommands.NEW_FOLDER, this.newWorkspaceRootUriAwareCommandHandler({
execute: uri => this.getDirectory(uri).then(parent => {
if (parent) {
const parentUri = new URI(parent.uri);
const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, parent, 'Untitled');
const dialog = new SingleTextInputDialog({
title: 'New Folder',
initialValue: vacantChildUri.path.base,
validate: name => this.validateFileName(name, parent, true)
});
dialog.open().then(name => {
if (name) {
this.fileSystem.createFolder(parentUri.resolve(name).toString());
}
});
}
})
execute: uri => this.getDirectory(uri).then(parent => this.createNewFolder(parent))
}));
registry.registerCommand(WorkspaceCommands.NEW_FOLDER_ROOT, {
isEnabled: () => !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened,
isVisible: () => !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened,
execute: () => this.createNewFolder(this.workspaceService.workspace),
});
registry.registerCommand(WorkspaceCommands.FILE_RENAME, this.newMultiUriAwareCommandHandler({
isEnabled: uris => uris.some(uri => !this.isWorkspaceRoot(uri)) && uris.length === 1,
isVisible: uris => uris.some(uri => !this.isWorkspaceRoot(uri)) && uris.length === 1,
Expand Down Expand Up @@ -323,6 +303,56 @@ export class WorkspaceCommandContribution implements CommandContribution {
return new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, handler);
}

/**
* Create a new file given a file stat with the default name and extension params.
* @param stat {FileStat} the given file stat.
*/
protected createNewFile(stat: FileStat | undefined): void {
if (!stat) {
return;
}
const parentUri = new URI(stat.uri);
const { fileName, fileExtension } = this.getDefaultFileConfig();
const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, stat, fileName, fileExtension);

const dialog = new SingleTextInputDialog({
title: 'New File',
initialValue: vacantChildUri.path.base,
validate: name => this.validateFileName(name, stat, true)
});

dialog.open().then(name => {
if (name) {
const fileUri = parentUri.resolve(name);
this.fileSystem.createFile(fileUri.toString()).then(() => {
open(this.openerService, fileUri);
});
}
});
}

/**
* Create a new folder given a file stat.
* @param stat {FileStat} the given file stat.
*/
protected createNewFolder(stat: FileStat | undefined): void {
if (!stat) {
return;
}
const parentUri = new URI(stat.uri);
const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, stat, 'Untitled');
const dialog = new SingleTextInputDialog({
title: 'New Folder',
initialValue: vacantChildUri.path.base,
validate: name => this.validateFileName(name, stat, true)
});
dialog.open().then(name => {
if (name) {
this.fileSystem.createFolder(parentUri.resolve(name).toString());
}
});
}

/**
* Returns an error message if the file name is invalid. Otherwise, an empty string.
*
Expand Down

0 comments on commit d0ec6b2

Please sign in to comment.