Skip to content

Commit

Permalink
terminal: add context menu
Browse files Browse the repository at this point in the history
Contributed on behalf of STMicroelectronics.
Fixes #7632

Change-Id: Icb23f6fbdc44cdc5e018277e44ed5fe46c8ac26a
  • Loading branch information
planger committed Mar 23, 2023
1 parent 656cd9b commit c246077
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 24 deletions.
5 changes: 5 additions & 0 deletions packages/terminal/src/browser/base/terminal-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export abstract class TerminalWidget extends BaseWidget {
*/
abstract clearOutput(): void;

/**
* Select entire content in the terminal.
*/
abstract selectAll(): void;

abstract writeLine(line: string): void;

abstract write(data: string): void;
Expand Down
87 changes: 64 additions & 23 deletions packages/terminal/src/browser/terminal-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ import {
} from '@theia/core/lib/common';
import {
ApplicationShell, KeybindingContribution, KeyCode, Key, WidgetManager, PreferenceService,
KeybindingRegistry, Widget, LabelProvider, WidgetOpenerOptions, StorageService,
QuickInputService, codicon, CommonCommands, FrontendApplicationContribution, OnWillStopAction, Dialog, ConfirmDialog, FrontendApplication, PreferenceScope
KeybindingRegistry, LabelProvider, WidgetOpenerOptions, StorageService, QuickInputService,
codicon, CommonCommands, FrontendApplicationContribution, OnWillStopAction, Dialog, ConfirmDialog, FrontendApplication, PreferenceScope
} from '@theia/core/lib/browser';
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TERMINAL_WIDGET_FACTORY_ID, TerminalWidgetFactoryOptions, TerminalWidgetImpl } from './terminal-widget-impl';
Expand Down Expand Up @@ -70,6 +70,8 @@ export namespace TerminalMenus {
export const TERMINAL_TASKS_CONFIG = [...TERMINAL_TASKS, '4_terminal'];
export const TERMINAL_NAVIGATOR_CONTEXT_MENU = ['navigator-context-menu', 'navigation'];
export const TERMINAL_OPEN_EDITORS_CONTEXT_MENU = ['open-editors-context-menu', 'navigation'];

export const TERMINAL_CONTEXT_MENU = ['terminal-context-menu'];
}

export namespace TerminalCommands {
Expand Down Expand Up @@ -150,6 +152,16 @@ export namespace TerminalCommands {
category: TERMINAL_CATEGORY,
label: 'Toggle Terminal'
});
export const KILL_TERMINAL = Command.toDefaultLocalizedCommand({
id: 'terminal:kill',
category: TERMINAL_CATEGORY,
label: 'Kill Terminal'
});
export const SELECT_ALL: Command = {
id: 'terminal:select:all',
label: CommonCommands.SELECT_ALL.label,
category: TERMINAL_CATEGORY,
};

/**
* Command that displays all terminals that are currently opened
Expand Down Expand Up @@ -530,9 +542,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
execute: () => this.openActiveWorkspaceTerminal()
});
commands.registerCommand(TerminalCommands.SPLIT, {
execute: widget => this.splitTerminal(widget),
isEnabled: widget => !!this.getTerminalRef(widget),
isVisible: widget => !!this.getTerminalRef(widget)
execute: () => this.splitTerminal()
});
commands.registerCommand(TerminalCommands.TERMINAL_CLEAR);
commands.registerHandler(TerminalCommands.TERMINAL_CLEAR.id, {
Expand Down Expand Up @@ -607,6 +617,14 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
commands.registerCommand(TerminalCommands.TOGGLE_TERMINAL, {
execute: () => this.toggleTerminal()
});
commands.registerCommand(TerminalCommands.KILL_TERMINAL, {
isEnabled: () => !!this.currentTerminal,
execute: () => this.currentTerminal?.close()
});
commands.registerCommand(TerminalCommands.SELECT_ALL, {
isEnabled: () => !!this.currentTerminal,
execute: () => this.currentTerminal?.selectAll()
});
}

protected toggleTerminal(): void {
Expand Down Expand Up @@ -685,6 +703,29 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
commandId: TerminalCommands.TERMINAL_CONTEXT.id,
order: 'z'
});

menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_1'], {
commandId: TerminalCommands.NEW_ACTIVE_WORKSPACE.id,
label: nls.localizeByDefault('New Terminal')
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_1'], {
commandId: TerminalCommands.SPLIT.id
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_2'], {
commandId: CommonCommands.COPY.id
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_2'], {
commandId: CommonCommands.PASTE.id
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_2'], {
commandId: TerminalCommands.SELECT_ALL.id
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_3'], {
commandId: TerminalCommands.TERMINAL_CLEAR.id
});
menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_4'], {
commandId: TerminalCommands.KILL_TERMINAL.id
});
}

registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
Expand All @@ -707,7 +748,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
keybindings.registerKeybinding({
command: KeybindingRegistry.PASSTHROUGH_PSEUDO_COMMAND,
keybinding: KeyCode.createKeyCode({ key: k, ctrl: true }).toString(),
context: TerminalKeybindingContexts.terminalActive,
when: 'terminalFocus',
});
};

Expand All @@ -717,7 +758,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
keybindings.registerKeybinding({
command: KeybindingRegistry.PASSTHROUGH_PSEUDO_COMMAND,
keybinding: KeyCode.createKeyCode({ key: k, alt: true }).toString(),
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
};

Expand Down Expand Up @@ -776,7 +817,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
keybindings.registerKeybinding({
command: KeybindingRegistry.PASSTHROUGH_PSEUDO_COMMAND,
keybinding: 'ctrlcmd+a',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
}

Expand All @@ -791,12 +832,12 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
keybindings.registerKeybinding({
command: TerminalCommands.TERMINAL_CLEAR.id,
keybinding: 'ctrlcmd+k',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.TERMINAL_FIND_TEXT.id,
keybinding: 'ctrlcmd+f',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.TERMINAL_FIND_TEXT_CANCEL.id,
Expand All @@ -806,32 +847,37 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
keybindings.registerKeybinding({
command: TerminalCommands.SCROLL_LINE_UP.id,
keybinding: 'ctrl+shift+up',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.SCROLL_LINE_DOWN.id,
keybinding: 'ctrl+shift+down',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.SCROLL_TO_TOP.id,
keybinding: 'shift-home',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.SCROLL_PAGE_UP.id,
keybinding: 'shift-pageUp',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.SCROLL_PAGE_DOWN.id,
keybinding: 'shift-pageDown',
context: TerminalKeybindingContexts.terminalActive
when: 'terminalFocus'
});
keybindings.registerKeybinding({
command: TerminalCommands.TOGGLE_TERMINAL.id,
keybinding: 'ctrl+`',
});
keybindings.registerKeybinding({
command: TerminalCommands.SELECT_ALL.id,
keybinding: 'ctrlcmd+a',
when: 'terminalFocus'
});
}

async newTerminal(options: TerminalWidgetOptions): Promise<TerminalWidget> {
Expand Down Expand Up @@ -927,18 +973,13 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
});
}

protected async splitTerminal(widget?: Widget): Promise<void> {
const ref = this.getTerminalRef(widget);
if (ref) {
protected async splitTerminal(referenceTerminal?: TerminalWidget): Promise<void> {
if (referenceTerminal || this.currentTerminal) {
const ref = referenceTerminal ?? this.currentTerminal;
await this.openTerminal({ ref, mode: 'split-right' });
}
}

protected getTerminalRef(widget?: Widget): TerminalWidget | undefined {
const ref = widget ? widget : this.shell.currentWidget;
return ref instanceof TerminalWidget ? ref : undefined;
}

protected async openTerminal(options?: ApplicationShell.WidgetOptions, terminalProfile?: TerminalProfile): Promise<void> {
let profile = terminalProfile;
if (!terminalProfile) {
Expand Down
18 changes: 17 additions & 1 deletion packages/terminal/src/browser/terminal-widget-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import { Terminal, RendererType } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core';
import { Widget, Message, WebSocketConnectionProvider, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget } from '@theia/core/lib/browser';
import {
Widget, Message, WebSocketConnectionProvider, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer
} from '@theia/core/lib/browser';
import { isOSX } from '@theia/core/lib/common';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { ShellTerminalServerProxy, IShellTerminalPreferences } from '../common/shell-terminal-protocol';
Expand All @@ -39,6 +41,7 @@ import { TerminalThemeService } from './terminal-theme-service';
import { CommandLineOptions, ShellCommandBuilder } from '@theia/process/lib/common/shell-command-builder';
import { Key } from '@theia/core/lib/browser/keys';
import { nls } from '@theia/core/lib/common/nls';
import { TerminalMenus } from './terminal-frontend-contribution';

export const TERMINAL_WIDGET_FACTORY_ID = 'terminal';

Expand Down Expand Up @@ -93,6 +96,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
@inject(TerminalCopyOnSelectionHandler) protected readonly copyOnSelectionHandler: TerminalCopyOnSelectionHandler;
@inject(TerminalThemeService) protected readonly themeService: TerminalThemeService;
@inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder;
@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;

protected readonly onDidOpenEmitter = new Emitter<void>();
readonly onDidOpen: Event<void> = this.onDidOpenEmitter.event;
Expand Down Expand Up @@ -251,6 +255,14 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
this.node.removeEventListener('mousemove', mouseListener);
});

const contextMenuListener = (event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
this.contextMenuRenderer.render({ menuPath: TerminalMenus.TERMINAL_CONTEXT_MENU, anchor: event });
};
this.node.addEventListener('contextmenu', contextMenuListener);
this.onDispose(() => this.node.removeEventListener('contextmenu', contextMenuListener));

this.toDispose.push(this.term.onSelectionChange(() => {
if (this.copyOnSelection) {
this.copyOnSelectionHandler.copy(this.term.getSelection());
Expand Down Expand Up @@ -437,6 +449,10 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
this.term.clear();
}

selectAll(): void {
this.term.selectAll();
}

async hasChildProcesses(): Promise<boolean> {
return this.shellTerminalServer.hasChildProcesses(await this.processId);
}
Expand Down

0 comments on commit c246077

Please sign in to comment.