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

- implemented `More Actions...` toolbar item for the explorer which is
used to host commands specific to the workspace root whenever there is a
single-root workspace currently opened. The initial implementation adds
only a subset of available commands which make sense for the workspace root.
The following commands have been added:
1. new file
2. new folder
3. open in terminal
4. find in folder
5. add folder to workspace
6. compare with...

Signed-off-by: Vincent Fugnitto <vincent.fugnitto@ericsson.com>
  • Loading branch information
vince-fugnitto committed Aug 20, 2019
1 parent d059f9e commit 4baff8d
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 92 deletions.
124 changes: 94 additions & 30 deletions packages/git/src/browser/diff/git-diff-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { CommandRegistry, Command, MenuModelRegistry, SelectionService, MessageService } from '@theia/core/lib/common';
import { CommandRegistry, Command, MenuModelRegistry, SelectionService, MessageService, Mutable } from '@theia/core/lib/common';
import { FrontendApplication, AbstractViewContribution } from '@theia/core/lib/browser';
import { WidgetManager } from '@theia/core/lib/browser/widget-manager';
import { injectable, inject } from 'inversify';
import { GitDiffWidget, GIT_DIFF } from './git-diff-widget';
import { open, OpenerService } from '@theia/core/lib/browser';
import { NavigatorContextMenu } from '@theia/navigator/lib/browser/navigator-contribution';
import { NavigatorContextMenu, NavigatorMoreToolbarGroups } from '@theia/navigator/lib/browser/navigator-contribution';
import { UriCommandHandler, UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import { GitQuickOpenService } from '../git-quick-open-service';
import { FileSystem } from '@theia/filesystem/lib/common';
Expand All @@ -29,17 +29,29 @@ import URI from '@theia/core/lib/common/uri';
import { GIT_RESOURCE_SCHEME } from '../git-resource';
import { Git } from '../../common';
import { GitRepositoryProvider } from '../git-repository-provider';
import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { FileNavigatorWidget } from '@theia/navigator/lib/browser/navigator-widget';

export namespace GitDiffCommands {
export const OPEN_FILE_DIFF: Command = {
id: 'git-diff:open-file-diff',
category: 'Git Diff',
label: 'Compare With...'
};
export const OPEN_FILE_DIFF_ROOT: Command = {
id: 'git-diff:open-file-diff:root',
};
}

@injectable()
export class GitDiffContribution extends AbstractViewContribution<GitDiffWidget> {
export class GitDiffContribution extends AbstractViewContribution<GitDiffWidget> implements TabBarToolbarContribution {

@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;

@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;

constructor(
@inject(SelectionService) protected readonly selectionService: SelectionService,
Expand Down Expand Up @@ -71,34 +83,58 @@ export class GitDiffContribution extends AbstractViewContribution<GitDiffWidget>
commands.registerCommand(GitDiffCommands.OPEN_FILE_DIFF, this.newUriAwareCommandHandler({
isVisible: uri => !!this.repositoryProvider.findRepository(uri),
isEnabled: uri => !!this.repositoryProvider.findRepository(uri),
execute: async fileUri => {
await this.quickOpenService.chooseTagsAndBranches(
async (fromRevision, toRevision) => {
const uri = fileUri.toString();
const fileStat = await this.fileSystem.getFileStat(uri);
const options: Git.Options.Diff = {
uri,
range: {
fromRevision
}
};
if (fileStat) {
if (fileStat.isDirectory) {
this.showWidget(options);
} else {
const fromURI = fileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery(fromRevision);
const toURI = fileUri;
const diffUri = DiffUris.encode(fromURI, toURI);
if (diffUri) {
open(this.openerService, diffUri).catch(e => {
this.notifications.error(e.message);
});
}
}
}
}, this.repositoryProvider.findRepository(fileUri));
}
execute: async fileUri => this.openDiff(fileUri)
}));
commands.registerCommand(GitDiffCommands.OPEN_FILE_DIFF_ROOT, {
isVisible: () => {
if (this.workspaceService.workspace) {
const uri: URI = new URI(this.workspaceService.workspace.uri);
return !!this.repositoryProvider.findRepository(uri);
}
return false;
},
isEnabled: () => {
if (this.workspaceService.workspace) {
const uri: URI = new URI(this.workspaceService.workspace.uri);
return !!this.repositoryProvider.findRepository(uri);
}
return false;
},
execute: async () => {
if (this.workspaceService.workspace) {
const uri: URI = new URI(this.workspaceService.workspace.uri);
this.openDiff(uri);
}
}
});
}

registerToolbarItems(registry: TabBarToolbarRegistry): void {
// Register 'more actions' toolbar item (consists of multiple commands for the workspace root).
// Commands are available when there is a single-root workspace currently opened.
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.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& this.commandRegistry.isEnabled(commandId, ...args),
isVisible: (w, ...args) => w instanceof FileNavigatorWidget
&& !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& this.commandRegistry.isVisible(commandId, ...args),
});
item.command = id;
registry.registerItem(item);
};
navigatorRegisterItem({
id: GitDiffCommands.OPEN_FILE_DIFF_ROOT.id,
command: GitDiffCommands.OPEN_FILE_DIFF_ROOT.id,
tooltip: 'Compare With...',
group: NavigatorMoreToolbarGroups.WORKSPACE,
});
}

async showWidget(options: Git.Options.Diff): Promise<GitDiffWidget> {
Expand All @@ -109,6 +145,34 @@ export class GitDiffContribution extends AbstractViewContribution<GitDiffWidget>
});
}

protected async openDiff(fileUri: URI): Promise<void> {
await this.quickOpenService.chooseTagsAndBranches(
async (fromRevision, toRevision) => {
const uri = fileUri.toString();
const fileStat = await this.fileSystem.getFileStat(uri);
const options: Git.Options.Diff = {
uri,
range: {
fromRevision
}
};
if (fileStat) {
if (fileStat.isDirectory) {
this.showWidget(options);
} else {
const fromURI = fileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery(fromRevision);
const toURI = fileUri;
const diffUri = DiffUris.encode(fromURI, toURI);
if (diffUri) {
open(this.openerService, diffUri).catch(e => {
this.notifications.error(e.message);
});
}
}
}
}, this.repositoryProvider.findRepository(fileUri));
}

protected newUriAwareCommandHandler(handler: UriCommandHandler<URI>): UriAwareCommandHandler<URI> {
return new UriAwareCommandHandler(this.selectionService, handler);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/git/src/browser/diff/git-diff-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { WidgetFactory, bindViewContribution } from '@theia/core/lib/browser';
import { GitDiffWidget, GIT_DIFF } from './git-diff-widget';

import '../../../src/browser/style/diff.css';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';

export function bindGitDiffModule(bind: interfaces.Bind): void {

Expand All @@ -30,5 +31,5 @@ export function bindGitDiffModule(bind: interfaces.Bind): void {
}));

bindViewContribution(bind, GitDiffContribution);

bind(TabBarToolbarContribution).toService(GitDiffContribution);
}
54 changes: 52 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,15 @@ export namespace FileNavigatorCommands {
};
}

/**
* Navigator `More Actions...` toolbar item groups.
*/
export namespace NavigatorMoreToolbarGroups {
export const NEW_OPEN = '1_navigator_new_open';
export const WORKSPACE = '2_navigator.workspace';
export const SEARCH = '3_navigator_search';
}

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

/**
Expand Down Expand Up @@ -95,6 +104,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 +352,44 @@ export class FileNavigatorContribution extends AbstractViewContribution<FileNavi
tooltip: 'Collapse All',
priority: 1,
});

// Register 'more actions' toolbar item (consists of multiple commands for the workspace root).
// Commands are available when there is a single-root workspace currently opened.
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.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& this.commandRegistry.isEnabled(commandId, ...args),
isVisible: (w, ...args) => w instanceof FileNavigatorWidget
&& !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& 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,
});
navigatorRegisterItem({
id: WorkspaceCommands.ADD_FOLDER.id,
command: WorkspaceCommands.ADD_FOLDER.id,
tooltip: WorkspaceCommands.ADD_FOLDER.label,
group: NavigatorMoreToolbarGroups.WORKSPACE,
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@
import { AbstractViewContribution, KeybindingRegistry, LabelProvider, CommonMenus, FrontendApplication, FrontendApplicationContribution } from '@theia/core/lib/browser';
import { SearchInWorkspaceWidget } from './search-in-workspace-widget';
import { injectable, inject, postConstruct } from 'inversify';
import { CommandRegistry, MenuModelRegistry, SelectionService, Command } from '@theia/core';
import { CommandRegistry, MenuModelRegistry, SelectionService, Command, Mutable } from '@theia/core';
import { Widget } from '@theia/core/lib/browser/widgets';
import { NavigatorContextMenu } from '@theia/navigator/lib/browser/navigator-contribution';
import { NavigatorContextMenu, NavigatorMoreToolbarGroups } from '@theia/navigator/lib/browser/navigator-contribution';
import { UriCommandHandler, UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import URI from '@theia/core/lib/common/uri';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { FileSystem } from '@theia/filesystem/lib/common';
import { SearchInWorkspaceContextKeyService } from './search-in-workspace-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 { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { Range } from 'vscode-languageserver-types';
import { FileNavigatorWidget } from '@theia/navigator/lib/browser/navigator-widget';

export namespace SearchInWorkspaceCommands {
const SEARCH_CATEGORY = 'Search';
Expand All @@ -45,6 +46,9 @@ export namespace SearchInWorkspaceCommands {
category: SEARCH_CATEGORY,
label: 'Find in Folder'
};
export const FIND_IN_FOLDER_ROOT: Command = {
id: 'search-in-workspace.in-folder.root',
};
export const REFRESH_RESULTS: Command = {
id: 'search-in-workspace.refresh',
category: SEARCH_CATEGORY,
Expand All @@ -68,6 +72,7 @@ export namespace SearchInWorkspaceCommands {
@injectable()
export class SearchInWorkspaceFrontendContribution extends AbstractViewContribution<SearchInWorkspaceWidget> implements FrontendApplicationContribution, TabBarToolbarContribution {

@inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
@inject(SelectionService) protected readonly selectionService: SelectionService;
@inject(LabelProvider) protected readonly labelProvider: LabelProvider;
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
Expand Down Expand Up @@ -110,7 +115,13 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
widget.updateSearchTerm(this.getSearchTerm());
}
});

commands.registerCommand(SearchInWorkspaceCommands.FIND_IN_FOLDER_ROOT, {
execute: async () => {
const widget = await this.openView({ activate: true });
widget.clear();
widget.updateSearchTerm(this.getSearchTerm());
}
});
commands.registerCommand(SearchInWorkspaceCommands.FIND_IN_FOLDER, this.newMultiUriAwareCommandHandler({
execute: async uris => {
const resources: string[] = [];
Expand Down Expand Up @@ -222,6 +233,32 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
priority: 2,
onDidChange
});
// Register 'more actions' toolbar item (consists of multiple commands for the workspace root).
// Commands are available when there is a single-root workspace currently opened.
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.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& !!this.commandRegistry.isEnabled(commandId, ...args),
isVisible: (w, ...args) => w instanceof FileNavigatorWidget
&& !!this.workspaceService.workspace && !this.workspaceService.isMultiRootWorkspaceOpened
&& this.commandRegistry.isVisible(commandId, ...args),
});
item.command = id;
toolbarRegistry.registerItem(item);
};
navigatorRegisterItem({
id: SearchInWorkspaceCommands.FIND_IN_FOLDER_ROOT.id,
command: SearchInWorkspaceCommands.FIND_IN_FOLDER_ROOT.id,
tooltip: 'Find in Folder',
group: NavigatorMoreToolbarGroups.SEARCH,
});

}

protected newUriAwareCommandHandler(handler: UriCommandHandler<URI>): UriAwareCommandHandler<URI> {
Expand Down
1 change: 1 addition & 0 deletions packages/terminal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@theia/core": "^0.9.0",
"@theia/editor": "^0.9.0",
"@theia/filesystem": "^0.9.0",
"@theia/navigator": "^0.9.0",
"@theia/process": "^0.9.0",
"@theia/workspace": "^0.9.0",
"xterm": "3.13.0"
Expand Down
Loading

0 comments on commit 4baff8d

Please sign in to comment.