Skip to content

Commit

Permalink
Fixed #5543: implemented "show all opened terminals" quick-open command
Browse files Browse the repository at this point in the history
- Implemented the "Show All Opened Terminals" command in the quick command menu, which can display all the currently opened terminal widgets.
- User can navigate to each terminal widget by selection, and has the option to create a new terminal.
- The command has a global prefix: `term `, to be aligned to VS Code.

Signed-off-by: fangnx <naxin.fang@ericsson.com>
  • Loading branch information
fangnx authored and vince-fugnitto committed Jul 3, 2019
1 parent d2d4f75 commit 150c158
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ export namespace TerminalCommands {
category: TERMINAL_CATEGORY,
label: 'Split Terminal'
};
/**
* Command that displays all terminals that are currently opened
*/
export const SHOW_ALL_OPENED_TERMINALS: Command = {
id: 'workbench.action.showAllTerminals',
category: 'View',
label: 'Show All Opened Terminals'
};
}

@injectable()
Expand Down
10 changes: 9 additions & 1 deletion packages/terminal/src/browser/terminal-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { ContainerModule, Container } from 'inversify';
import { CommandContribution, MenuContribution } from '@theia/core/lib/common';
import { bindContributionProvider } from '@theia/core';
import { KeybindingContribution, WebSocketConnectionProvider, WidgetFactory, KeybindingContext } from '@theia/core/lib/browser';
import { KeybindingContribution, WebSocketConnectionProvider, WidgetFactory, KeybindingContext, QuickOpenContribution } from '@theia/core/lib/browser';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TerminalFrontendContribution } from './terminal-frontend-contribution';
import { TerminalWidgetImpl, TERMINAL_WIDGET_FACTORY_ID } from './terminal-widget-impl';
Expand All @@ -33,6 +33,7 @@ import { URLMatcher, LocalhostMatcher } from './terminal-linkmatcher';
import { TerminalContribution } from './terminal-contribution';
import { TerminalLinkmatcherFiles } from './terminal-linkmatcher-files';
import { TerminalLinkmatcherDiffPre, TerminalLinkmatcherDiffPost } from './terminal-linkmatcher-diff';
import { TerminalQuickOpenService, TerminalQuickOpenContribution } from './terminal-quick-open-service';

import '../../src/browser/terminal.css';
import 'xterm/lib/xterm.css';
Expand Down Expand Up @@ -65,6 +66,13 @@ export default new ContainerModule(bind => {
}
}));

bind(TerminalQuickOpenService).toSelf().inSingletonScope();

bind(TerminalQuickOpenContribution).toSelf().inSingletonScope();
for (const identifier of [CommandContribution, QuickOpenContribution]) {
bind(identifier).toService(TerminalQuickOpenContribution);
}

bind(TerminalFrontendContribution).toSelf().inSingletonScope();
bind(TerminalService).toService(TerminalFrontendContribution);
for (const identifier of [CommandContribution, MenuContribution, KeybindingContribution, TabBarToolbarContribution]) {
Expand Down
173 changes: 173 additions & 0 deletions packages/terminal/src/browser/terminal-quick-open-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/********************************************************************************
* Copyright (C) 2019 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable } from 'inversify';
import {
QuickOpenModel, QuickOpenGroupItem, QuickOpenHandler,
QuickOpenOptions, QuickOpenItemOptions, QuickOpenMode,
PrefixQuickOpenService,
QuickOpenContribution, QuickOpenHandlerRegistry, QuickOpenGroupItemOptions
} from '@theia/core/lib/browser';
import { CommandContribution, CommandRegistry, CommandService } from '@theia/core/lib/common';
import { TerminalWidget } from './base/terminal-widget';
import { TerminalService } from './base/terminal-service';
import { TerminalCommands } from './terminal-frontend-contribution';

@injectable()
export class TerminalQuickOpenService implements QuickOpenModel, QuickOpenHandler {

@inject(PrefixQuickOpenService)
protected readonly prefixQuickOpenService: PrefixQuickOpenService;

@inject(CommandService)
protected readonly commandService: CommandService;

@inject(TerminalService)
protected readonly terminalService: TerminalService;

readonly prefix: string = 'term ';

get description(): string {
return 'Show All Opened Terminals';
}

getModel(): QuickOpenModel {
return this;
}

getOptions(): QuickOpenOptions {
return {
fuzzyMatchLabel: {
enableSeparateSubstringMatching: true
},
fuzzyMatchDescription: {
enableSeparateSubstringMatching: true
}
};
}

open(): void {
this.prefixQuickOpenService.open(this.prefix);
}

async onType(lookFor: string, acceptor: (items: QuickOpenGroupItem[]) => void): Promise<void> {
const terminalItems: QuickOpenGroupItem[] = [];

// Get the sorted list of currently opened terminal widgets
const widgets: TerminalWidget[] = this.terminalService.all
.sort((a: TerminalWidget, b: TerminalWidget) => this.compareItems(a, b));

for (const widget of widgets) {
const item = await this.toItem(widget);
terminalItems.push(item);
}
// Append a quick open item to create a new terminal.
const createNewTerminalItem = new QuickOpenGroupItem<QuickOpenGroupItemOptions>({
label: 'Open New Terminal',
iconClass: 'fa fa-plus',
run: this.doCreateNewTerminal(),
groupLabel: undefined,
showBorder: !!terminalItems.length
});
terminalItems.push(createNewTerminalItem);

acceptor(terminalItems);
return;
}

/**
* Compare two terminal widgets by label. If labels are identical, compare by the widget id.
* @param a `TerminalWidget` for comparison
* @param b `TerminalWidget` for comparison
*/
protected compareItems(a: TerminalWidget, b: TerminalWidget): number {
const normalize = (str: string) => str.trim().toLowerCase();

if (normalize(a.title.label) !== normalize(b.title.label)) {
return normalize(a.title.label).localeCompare(normalize(b.title.label));
} else {
return normalize(a.id).localeCompare(normalize(b.id));
}
}

/**
* Get the function that can create a new terminal.
* @param {TerminalWidget} widget - the terminal widget to be opened.
* @returns Function that would create a new terminal if mode === QuickOpenMode.OPEN.
*/
protected doCreateNewTerminal(): (mode: QuickOpenMode) => boolean {
return (mode: QuickOpenMode) => {
if (mode !== QuickOpenMode.OPEN) {
return false;
}
this.commandService.executeCommand(TerminalCommands.NEW.id);
return true;
};
}

/**
* Convert the terminal widget to the quick open item.
* @param {TerminalWidget} widget - the terminal widget.
* @returns The quick open group item.
*/
protected async toItem(widget: TerminalWidget): Promise<QuickOpenGroupItem<QuickOpenItemOptions>> {
const options: QuickOpenGroupItemOptions = {
label: widget.title.label,
description: widget.id,
tooltip: widget.title.label,
hidden: false,
run: this.getRunFunction(widget),
groupLabel: undefined,
showBorder: false
};
return new QuickOpenGroupItem<QuickOpenGroupItemOptions>(options);
}

/**
* Get the function that can open the editor file.
* @param {TerminalWidget} widget - the terminal widget to be opened.
* @returns Function that would open the terminal if mode === QuickOpenMode.OPEN.
*/
protected getRunFunction(widget: TerminalWidget): (mode: QuickOpenMode) => boolean {
return (mode: QuickOpenMode) => {
if (mode !== QuickOpenMode.OPEN) {
return false;
}
this.terminalService.open(widget);
return true;
};
}
}

/**
* TODO: merge it to TerminalFrontendContribution.
*/
@injectable()
export class TerminalQuickOpenContribution implements CommandContribution, QuickOpenContribution {

@inject(TerminalQuickOpenService)
protected readonly terminalQuickOpenService: TerminalQuickOpenService;

registerQuickOpenHandlers(handlers: QuickOpenHandlerRegistry): void {
handlers.registerHandler(this.terminalQuickOpenService);
}

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(TerminalCommands.SHOW_ALL_OPENED_TERMINALS, {
execute: () => this.terminalQuickOpenService.open()
});
}
}

0 comments on commit 150c158

Please sign in to comment.