Merge pull request #114566 from microsoft/tyriar/92038

Remove getAction from terminal
This commit is contained in:
Daniel Imms 2021-02-05 07:44:10 -08:00 committed by GitHub
commit 6b3d751206
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 489 additions and 486 deletions

View file

@ -148,6 +148,7 @@ export class MenuId {
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
static readonly AccountsContext = new MenuId('AccountsContext');
static readonly PanelTitle = new MenuId('PanelTitle');
static readonly TerminalContext = new MenuId('TerminalContext');
readonly id: number;
readonly _debugName: string;

View file

@ -10,18 +10,16 @@ import 'vs/css!./media/terminal';
import 'vs/css!./media/widgets';
import 'vs/css!./media/xterm';
import * as nls from 'vs/nls';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight, KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import * as panel from 'vs/workbench/browser/panel';
import { getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views';
import { registerTerminalActions, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, KillTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { registerTerminalActions, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView';
import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED } from 'vs/workbench/contrib/terminal/common/terminal';
import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, TERMINAL_VIEW_ID, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands';
import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu';
@ -29,7 +27,6 @@ import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/co
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ITerminalService, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
@ -90,47 +87,7 @@ Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry).registerViews
}], VIEW_CONTAINER);
// Register actions
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registerTerminalActions();
const category = TERMINAL_ACTION_CATEGORY;
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(KillTerminalAction), 'Terminal: Kill the Active Terminal Instance', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CreateNewTerminalAction, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK,
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK }
}), 'Terminal: Create New Integrated Terminal', category);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectAllTerminalAction, {
// Don't use ctrl+a by default as that would override the common go to start
// of prompt shell binding
primary: 0,
// Technically this doesn't need to be here as it will fall back to this
// behavior anyway when handed to xterm.js, having this handled by VS Code
// makes it easier for users to see how it works though.
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
// Weight is higher than work workbench contributions so the keybinding remains
// highest priority when chords are registered afterwards
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ClearTerminalAction, {
primary: 0,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SelectDefaultShellWindowsTerminalAction), 'Terminal: Select Default Shell', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SplitTerminalAction, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH,
secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5]
}
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SplitInActiveWorkspaceTerminalAction), 'Terminal: Split Terminal (In Active Workspace)', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
// Commands might be affected by Web restrictons
if (BrowserFeatures.clipboard.writeText) {
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(CopyTerminalSelectionAction, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C] },
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C }
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
}
function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyExpression } & IKeybindings): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
@ -149,14 +106,6 @@ function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyE
// The text representation of `^<letter>` is `'A'.charCodeAt(0) + 1`.
const CTRL_LETTER_OFFSET = 64;
if (BrowserFeatures.clipboard.readText) {
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(TerminalPasteAction, platform.isMacintosh && platform.isWeb ? undefined : {
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] },
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED);
}
// An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the
// shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is
// disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen

View file

@ -3,45 +3,49 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action, IAction } from 'vs/base/common/actions';
import { EndOfLinePreference } from 'vs/editor/common/model';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, IRemoteTerminalAttachTarget, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE } from 'vs/workbench/contrib/terminal/common/terminal';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { Action } from 'vs/base/common/actions';
import { Codicon } from 'vs/base/common/codicons';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
import { withNullAsUndefined } from 'vs/base/common/types';
import { ITerminalInstance, ITerminalService, Direction, IRemoteTerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
import { Action2, registerAction2, ILocalizedString } from 'vs/platform/actions/common/actions';
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EndOfLinePreference } from 'vs/editor/common/model';
import { localize } from 'vs/nls';
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions';
import { Action2, ICommandActionTitle, ILocalizedString, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ILabelService } from 'vs/platform/label/common/label';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { RemoteNameContext } from 'vs/workbench/browser/contextkeys';
import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions';
import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, TERMINAL_VIEW_ID, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { killTerminalIcon, newTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons';
import { Codicon } from 'vs/base/common/codicons';
import { equals } from 'vs/base/common/objects';
export const switchTerminalActionViewItemSeparator = '─────────';
export const selectDefaultShellTitle = localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell");
export const configureTerminalSettingsTitle = localize('workbench.action.terminal.openSettings', "Configure Terminal Settings");
const enum ContextMenuGroup {
Create = '1_create',
Edit = '2_edit',
Clear = '3_clear',
Kill = '4_kill'
}
async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise<string | URI | undefined> {
switch (configHelper.config.splitCwd) {
@ -70,68 +74,6 @@ async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITe
}
}
export class KillTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.KILL;
public static readonly LABEL = localize('workbench.action.terminal.kill', "Kill the Active Terminal Instance");
public static readonly PANEL_LABEL = localize('workbench.action.terminal.kill.short', "Kill Terminal");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label, 'terminal-action ' + ThemeIcon.asClassName(killTerminalIcon));
}
async run() {
await this._terminalService.doWithActiveInstance(async t => {
t.dispose(true);
if (this._terminalService.terminalInstances.length > 0) {
await this._terminalService.showPanel(true);
}
});
}
}
/**
* Copies the terminal selection. Note that since the command palette takes focus from the terminal,
* this cannot be triggered through the command palette.
*/
export class CopyTerminalSelectionAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.COPY_SELECTION;
public static readonly LABEL = localize('workbench.action.terminal.copySelection', "Copy Selection");
public static readonly SHORT_LABEL = localize('workbench.action.terminal.copySelection.short', "Copy");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
await this._terminalService.getActiveInstance()?.copySelection();
}
}
export class SelectAllTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.SELECT_ALL;
public static readonly LABEL = localize('workbench.action.terminal.selectAll', "Select All");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
this._terminalService.getActiveInstance()?.selectAll();
}
}
export const terminalSendSequenceCommand = (accessor: ServicesAccessor, args: { text?: string } | undefined) => {
accessor.get(ITerminalService).doWithActiveInstance(t => {
if (!args?.text) {
@ -147,280 +89,8 @@ export const terminalSendSequenceCommand = (accessor: ServicesAccessor, args: {
});
};
export class CreateNewTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.NEW;
public static readonly LABEL = localize('workbench.action.terminal.new', "Create New Integrated Terminal");
public static readonly SHORT_LABEL = localize('workbench.action.terminal.new.short', "New Terminal");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService,
@ICommandService private readonly _commandService: ICommandService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
) {
super(id, label, 'terminal-action ' + ThemeIcon.asClassName(newTerminalIcon));
}
async run(event?: any) {
const folders = this._workspaceContextService.getWorkspace().folders;
if (event instanceof MouseEvent && (event.altKey || event.ctrlKey)) {
const activeInstance = this._terminalService.getActiveInstance();
if (activeInstance) {
const cwd = await getCwdForSplit(this._terminalService.configHelper, activeInstance);
this._terminalService.splitInstance(activeInstance, { cwd });
return;
}
}
if (this._terminalService.isProcessSupportRegistered) {
let instance: ITerminalInstance | undefined;
if (folders.length <= 1) {
// Allow terminal service to handle the path when there is only a
// single root
instance = this._terminalService.createTerminal(undefined);
} else {
const options: IPickOptions<IQuickPickItem> = {
placeHolder: localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal")
};
const workspace = await this._commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]);
if (!workspace) {
// Don't create the instance if the workspace picker was canceled
return;
}
instance = this._terminalService.createTerminal({ cwd: workspace.uri });
}
this._terminalService.setActiveInstance(instance);
}
await this._terminalService.showPanel(true);
}
}
export class SplitTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.SPLIT;
public static readonly LABEL = localize('workbench.action.terminal.split', "Split Terminal");
public static readonly SHORT_LABEL = localize('workbench.action.terminal.split.short', "Split");
public static readonly HORIZONTAL_CLASS = 'terminal-action ' + Codicon.splitHorizontal.classNames;
public static readonly VERTICAL_CLASS = 'terminal-action ' + Codicon.splitVertical.classNames;
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService,
@ICommandService private readonly _commandService: ICommandService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
) {
super(id, label, SplitTerminalAction.HORIZONTAL_CLASS);
}
public async run(): Promise<any> {
await this._terminalService.doWithActiveInstance(async t => {
const cwd = await getCwdForSplit(this._terminalService.configHelper, t, this._workspaceContextService.getWorkspace().folders, this._commandService);
if (cwd === undefined) {
return undefined;
}
this._terminalService.splitInstance(t, { cwd });
return this._terminalService.showPanel(true);
});
}
}
export class SplitInActiveWorkspaceTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.SPLIT_IN_ACTIVE_WORKSPACE;
public static readonly LABEL = localize('workbench.action.terminal.splitInActiveWorkspace', "Split Terminal (In Active Workspace)");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
await this._terminalService.doWithActiveInstance(async t => {
const cwd = await getCwdForSplit(this._terminalService.configHelper, t);
this._terminalService.splitInstance(t, { cwd });
await this._terminalService.showPanel(true);
});
}
}
export class TerminalPasteAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.PASTE;
public static readonly LABEL = localize('workbench.action.terminal.paste', "Paste into Active Terminal");
public static readonly SHORT_LABEL = localize('workbench.action.terminal.paste.short', "Paste");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
this._terminalService.getActiveOrCreateInstance()?.paste();
}
}
export class SelectDefaultShellWindowsTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.SELECT_DEFAULT_SHELL;
public static readonly LABEL = localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
this._terminalService.selectDefaultShell();
}
}
export class ConfigureTerminalSettingsAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS;
public static readonly LABEL = localize('workbench.action.terminal.openSettings', "Configure Terminal Settings");
constructor(
id: string, label: string,
@IPreferencesService private readonly _preferencesService: IPreferencesService
) {
super(id, label);
}
async run() {
this._preferencesService.openSettings(false, '@feature:terminal');
}
}
const terminalIndexRe = /^([0-9]+): /;
export class SwitchTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.SWITCH_TERMINAL;
public static readonly LABEL = localize('workbench.action.terminal.switchTerminal', "Switch Terminal");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService,
@ITerminalContributionService private readonly _contributions: ITerminalContributionService,
@ICommandService private readonly _commands: ICommandService,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label, 'terminal-action switch-terminal');
}
public run(item?: string): Promise<any> {
if (!item || !item.split) {
return Promise.resolve(null);
}
if (item === SwitchTerminalActionViewItem.SEPARATOR) {
this._terminalService.refreshActiveTab();
return Promise.resolve(null);
}
if (item === SelectDefaultShellWindowsTerminalAction.LABEL) {
this._terminalService.refreshActiveTab();
return this._terminalService.selectDefaultShell();
}
if (item === ConfigureTerminalSettingsAction.LABEL) {
const settingsAction = new ConfigureTerminalSettingsAction(ConfigureTerminalSettingsAction.ID, ConfigureTerminalSettingsAction.LABEL, this.preferencesService);
settingsAction.run();
this._terminalService.refreshActiveTab();
}
const indexMatches = terminalIndexRe.exec(item);
if (indexMatches) {
this._terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1);
return this._terminalService.showPanel(true);
}
const customType = this._contributions.terminalTypes.find(t => t.title === item);
if (customType) {
return this._commands.executeCommand(customType.command);
}
console.warn(`Unmatched terminal item: "${item}"`);
return Promise.resolve();
}
}
export class SwitchTerminalActionViewItem extends SelectActionViewItem {
public static readonly SEPARATOR = '─────────';
private _lastOptions: ISelectOptionItem[] = [];
constructor(
action: IAction,
@ITerminalService private readonly _terminalService: ITerminalService,
@IThemeService private readonly _themeService: IThemeService,
@ITerminalContributionService private readonly _contributions: ITerminalContributionService,
@IContextViewService contextViewService: IContextViewService,
) {
super(null, action, getTerminalSelectOpenItems(_terminalService, _contributions), _terminalService.activeTabIndex, contextViewService, { ariaLabel: localize('terminals', 'Open Terminals.'), optionsAsChildren: true });
this._register(_terminalService.onInstancesChanged(this._updateItems, this));
this._register(_terminalService.onActiveTabChanged(this._updateItems, this));
this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this));
this._register(_terminalService.onTabDisposed(this._updateItems, this));
this._register(_terminalService.onDidChangeConnectionState(this._updateItems, this));
this._register(attachSelectBoxStyler(this.selectBox, this._themeService));
}
render(container: HTMLElement): void {
super.render(container);
container.classList.add('switch-terminal');
this._register(attachStylerCallback(this._themeService, { selectBorder }, colors => {
container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : '';
}));
}
private _updateItems(): void {
const options = getTerminalSelectOpenItems(this._terminalService, this._contributions);
// only update options if they've changed
if (!equals(Object.values(options), Object.values(this._lastOptions))) {
this.setOptions(options, this._terminalService.activeTabIndex);
this._lastOptions = options;
}
}
}
function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] {
const items = terminalService.connectionState === TerminalConnectionState.Connected ?
terminalService.getTabLabels().map(label => <ISelectOptionItem>{ text: label }) :
[{ text: localize('terminalConnectingLabel', "Starting...") }];
items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true });
for (const contributed of contributions.terminalTypes) {
items.push({ text: contributed.title });
}
items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL });
items.push({ text: ConfigureTerminalSettingsAction.LABEL });
return items;
}
export class ClearTerminalAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.CLEAR;
public static readonly LABEL = localize('workbench.action.terminal.clear', "Clear");
constructor(
id: string, label: string,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(id, label);
}
async run() {
this._terminalService.doWithActiveInstance(t => {
t.clear();
t.focus();
});
}
}
export class TerminalLaunchHelpAction extends Action {
constructor(
@ -1441,6 +1111,379 @@ export function registerTerminalActions() {
accessor.get(ITerminalService).getActiveInstance()?.showEnvironmentInfoHover();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.SPLIT,
title: { value: localize('workbench.action.terminal.split', "Split Terminal"), original: 'Split Terminal' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
keybinding: [{
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5,
weight: KeybindingWeight.WorkbenchContrib,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH,
secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5]
},
when: KEYBINDING_CONTEXT_TERMINAL_FOCUS
}],
icon: Codicon.splitHorizontal,
menu: [{
id: MenuId.ViewTitle,
group: 'navigation',
order: 2,
when: ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID),
}]
});
}
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
await terminalService.doWithActiveInstance(async t => {
const cwd = await getCwdForSplit(terminalService.configHelper, t, accessor.get(IWorkspaceContextService).getWorkspace().folders, accessor.get(ICommandService));
if (cwd === undefined) {
return undefined;
}
terminalService.splitInstance(t, { cwd });
return terminalService.showPanel(true);
});
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.SPLIT,
title: localize('workbench.action.terminal.split.short', "Split")
},
group: ContextMenuGroup.Create
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.SPLIT_IN_ACTIVE_WORKSPACE,
title: { value: localize('workbench.action.terminal.splitInActiveWorkspace', "Split Terminal (In Active Workspace)"), original: 'Split Terminal (In Active Workspace)' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
});
}
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
await terminalService.doWithActiveInstance(async t => {
const cwd = await getCwdForSplit(terminalService.configHelper, t);
terminalService.splitInstance(t, { cwd });
await terminalService.showPanel(true);
});
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.SELECT_ALL,
title: { value: localize('workbench.action.terminal.selectAll', "Select All"), original: 'Select All' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
keybinding: [{
// Don't use ctrl+a by default as that would override the common go to start
// of prompt shell binding
primary: 0,
// Technically this doesn't need to be here as it will fall back to this
// behavior anyway when handed to xterm.js, having this handled by VS Code
// makes it easier for users to see how it works though.
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A },
weight: KeybindingWeight.WorkbenchContrib,
when: KEYBINDING_CONTEXT_TERMINAL_FOCUS
}],
menu: {
id: MenuId.TerminalContext,
group: ContextMenuGroup.Edit,
order: 3
}
});
}
run(accessor: ServicesAccessor) {
accessor.get(ITerminalService).getActiveInstance()?.selectAll();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.NEW,
title: { value: localize('workbench.action.terminal.new', "Create New Integrated Terminal"), original: 'Create New Integrated Terminal' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
icon: Codicon.plus,
menu: {
id: MenuId.ViewTitle,
group: 'navigation',
order: 1,
when: ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID)
}
});
}
async run(accessor: ServicesAccessor, event: unknown) {
const terminalService = accessor.get(ITerminalService);
const workspaceContextService = accessor.get(IWorkspaceContextService);
const commandService = accessor.get(ICommandService);
const folders = workspaceContextService.getWorkspace().folders;
if (event instanceof MouseEvent && (event.altKey || event.ctrlKey)) {
const activeInstance = terminalService.getActiveInstance();
if (activeInstance) {
const cwd = await getCwdForSplit(terminalService.configHelper, activeInstance);
terminalService.splitInstance(activeInstance, { cwd });
return;
}
}
if (terminalService.isProcessSupportRegistered) {
let instance: ITerminalInstance | undefined;
if (folders.length <= 1) {
// Allow terminal service to handle the path when there is only a
// single root
instance = terminalService.createTerminal(undefined);
} else {
const options: IPickOptions<IQuickPickItem> = {
placeHolder: localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal")
};
const workspace = await commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]);
if (!workspace) {
// Don't create the instance if the workspace picker was canceled
return;
}
instance = terminalService.createTerminal({ cwd: workspace.uri });
}
terminalService.setActiveInstance(instance);
}
await terminalService.showPanel(true);
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.NEW,
title: localize('workbench.action.terminal.new.short', "New Terminal")
},
group: ContextMenuGroup.Create
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.KILL,
title: { value: localize('workbench.action.terminal.kill', "Kill the Active Terminal Instance"), original: 'Kill the Active Terminal Instance' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
icon: Codicon.trash,
menu: {
id: MenuId.ViewTitle,
group: 'navigation',
order: 3,
when: ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID)
}
});
}
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
await terminalService.doWithActiveInstance(async t => {
t.dispose(true);
if (terminalService.terminalInstances.length > 0) {
await terminalService.showPanel(true);
}
});
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.KILL,
title: localize('workbench.action.terminal.kill.short', "Kill Terminal")
},
group: ContextMenuGroup.Kill
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.KILL,
title: { value: localize('workbench.action.terminal.clear', "Clear"), original: 'Clear' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
keybinding: [{
primary: 0,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K },
// Weight is higher than work workbench contributions so the keybinding remains
// highest priority when chords are registered afterwards
weight: KeybindingWeight.WorkbenchContrib + 1,
when: KEYBINDING_CONTEXT_TERMINAL_FOCUS
}],
menu: {
id: MenuId.TerminalContext,
group: ContextMenuGroup.Clear
}
});
}
run(accessor: ServicesAccessor) {
accessor.get(ITerminalService).doWithActiveInstance(t => {
t.clear();
t.focus();
});
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.SELECT_DEFAULT_SHELL,
title: { value: localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell"), original: 'Select Default Shell' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(ITerminalService).selectDefaultShell();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS,
title: { value: configureTerminalSettingsTitle, original: 'Configure Terminal Settings' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(IPreferencesService).openSettings(false, '@feature:terminal');
}
});
// Some commands depend on platform features
if (BrowserFeatures.clipboard.writeText) {
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.COPY_SELECTION,
title: { value: localize('workbench.action.terminal.copySelection', "Copy Selection"), original: 'Copy Selection' },
f1: true,
category,
// TODO: Why is copy still showing up when text isn't selected?
precondition: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED),
keybinding: [{
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C] },
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C },
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)
}]
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(ITerminalService).getActiveInstance()?.copySelection();
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.COPY_SELECTION,
title: localize('workbench.action.terminal.copySelection.short', "Copy")
},
group: ContextMenuGroup.Edit,
order: 1
});
}
if (BrowserFeatures.clipboard.readText) {
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.PASTE,
title: { value: localize('workbench.action.terminal.paste', "Paste into Active Terminal"), original: 'Paste into Active Terminal' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
keybinding: [{
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] },
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V },
weight: KeybindingWeight.WorkbenchContrib,
when: KEYBINDING_CONTEXT_TERMINAL_FOCUS
}],
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(ITerminalService).getActiveInstance()?.paste();
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.PASTE,
title: localize('workbench.action.terminal.paste.short', "Paste")
},
group: ContextMenuGroup.Edit,
order: 2
});
}
const switchTerminalTitle: ICommandActionTitle = { value: localize('workbench.action.terminal.switchTerminal', "Switch Terminal"), original: 'Switch Terminal' };
registerAction2(class extends Action2 {
constructor() {
super({
id: TERMINAL_COMMAND_ID.SWITCH_TERMINAL,
title: switchTerminalTitle,
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED,
keybinding: [{
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] },
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V },
weight: KeybindingWeight.WorkbenchContrib,
when: KEYBINDING_CONTEXT_TERMINAL_FOCUS
}],
});
}
async run(accessor: ServicesAccessor, item?: string) {
const terminalService = accessor.get(ITerminalService);
const terminalContributionService = accessor.get(ITerminalContributionService);
const commandService = accessor.get(ICommandService);
if (!item || !item.split) {
return Promise.resolve(null);
}
if (item === switchTerminalActionViewItemSeparator) {
terminalService.refreshActiveTab();
return Promise.resolve(null);
}
if (item === selectDefaultShellTitle) {
terminalService.refreshActiveTab();
return terminalService.selectDefaultShell();
}
if (item === configureTerminalSettingsTitle) {
await commandService.executeCommand(TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS);
terminalService.refreshActiveTab();
}
const indexMatches = terminalIndexRe.exec(item);
if (indexMatches) {
terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1);
return terminalService.showPanel(true);
}
const customType = terminalContributionService.terminalTypes.find(t => t.title === item);
if (customType) {
return commandService.executeCommand(customType.command);
}
console.warn(`Unmatched terminal item: "${item}"`);
return Promise.resolve();
}
});
MenuRegistry.appendMenuItem(MenuId.ViewTitle, {
command: {
id: TERMINAL_COMMAND_ID.SWITCH_TERMINAL,
title: switchTerminalTitle
},
group: 'navigation',
order: 0,
when: ContextKeyEqualsExpr.create('view', TERMINAL_VIEW_ID)
});
}
interface IRemoteTerminalPick extends IQuickPickItem {

View file

@ -6,41 +6,46 @@
import * as dom from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import * as platform from 'vs/base/common/platform';
import { Action, IAction, Separator, IActionViewItem } from 'vs/base/common/actions';
import { Action, IAction, IActionViewItem } from 'vs/base/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService, IColorTheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget';
import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionViewItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { configureTerminalSettingsTitle, selectDefaultShellTitle, switchTerminalActionViewItemSeparator } from 'vs/workbench/contrib/terminal/browser/terminalActions';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { URI } from 'vs/base/common/uri';
import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { DataTransfers } from 'vs/base/browser/dnd';
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
import { equals } from 'vs/base/common/arrays';
const FIND_FOCUS_CLASS = 'find-focused';
export class TerminalViewPane extends ViewPane {
private _menu: IMenu;
private _actions: IAction[] | undefined;
private _copyContextMenuAction: IAction | undefined;
private _contextMenuActions: IAction[] | undefined;
private _cancelContextMenu: boolean = false;
private _fontStyleElement: HTMLElement | undefined;
private _parentDomElement: HTMLElement | undefined;
private _terminalContainer: HTMLElement | undefined;
private _findWidget: TerminalFindWidget | undefined;
private _splitTerminalAction: IAction | undefined;
private _terminalsInitialized = false;
private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 };
@ -57,8 +62,10 @@ export class TerminalViewPane extends ViewPane {
@ITelemetryService telemetryService: ITelemetryService,
@INotificationService private readonly _notificationService: INotificationService,
@IOpenerService openerService: IOpenerService,
@IMenuService menuService: IMenuService,
) {
super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService);
this._menu = this._register(menuService.createMenu(MenuId.TerminalContext, contextKeyService));
this._terminalService.onDidRegisterProcessSupport(() => {
if (this._actions) {
for (const action of this._actions) {
@ -148,67 +155,10 @@ export class TerminalViewPane extends ViewPane {
this._bodyDimensions.width = width;
this._bodyDimensions.height = height;
this._terminalService.terminalTabs.forEach(t => t.layout(width, height));
// Update orientation of split button icon
if (this._splitTerminalAction) {
this._splitTerminalAction.class = this.orientation === Orientation.HORIZONTAL ? SplitTerminalAction.HORIZONTAL_CLASS : SplitTerminalAction.VERTICAL_CLASS;
}
}
public getActions(): IAction[] {
if (!this._actions) {
this._splitTerminalAction = this._instantiationService.createInstance(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL);
this._actions = [
this._instantiationService.createInstance(SwitchTerminalAction, SwitchTerminalAction.ID, SwitchTerminalAction.LABEL),
this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.SHORT_LABEL),
this._splitTerminalAction,
this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL)
];
for (const action of this._actions) {
if (!this._terminalService.isProcessSupportRegistered) {
action.enabled = false;
}
this._register(action);
}
}
return this._actions;
}
private _getContextMenuActions(): IAction[] {
if (!this._contextMenuActions || !this._copyContextMenuAction) {
this._copyContextMenuAction = this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.SHORT_LABEL);
const clipboardActions = [];
if (BrowserFeatures.clipboard.writeText) {
clipboardActions.push(this._copyContextMenuAction);
}
if (BrowserFeatures.clipboard.readText) {
clipboardActions.push(this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.SHORT_LABEL));
}
clipboardActions.push(this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL));
this._contextMenuActions = [
this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.SHORT_LABEL),
this._instantiationService.createInstance(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.SHORT_LABEL),
new Separator(),
...clipboardActions,
new Separator(),
this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL),
new Separator(),
this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL)
];
this._contextMenuActions.forEach(a => {
this._register(a);
});
}
const activeInstance = this._terminalService.getActiveInstance();
this._copyContextMenuAction.enabled = !!activeInstance && activeInstance.hasSelection();
return this._contextMenuActions;
}
public getActionViewItem(action: Action): IActionViewItem | undefined {
if (action.id === SwitchTerminalAction.ID) {
if (action.id === TERMINAL_COMMAND_ID.SWITCH_TERMINAL) {
return this._instantiationService.createInstance(SwitchTerminalActionViewItem, action);
}
@ -362,10 +312,15 @@ export class TerminalViewPane extends ViewPane {
private _openContextMenu(event: MouseEvent): void {
const standardEvent = new StandardMouseEvent(event);
const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy };
const actions: IAction[] = [];
const actionsDisposable = createAndFillInContextMenuActions(this._menu, undefined, actions);
this._contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this._getContextMenuActions(),
getActionsContext: () => this._parentDomElement
getActions: () => actions,
getActionsContext: () => this._parentDomElement,
onHide: () => actionsDisposable.dispose()
});
}
@ -394,3 +349,58 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
collector.addRule(`.monaco-workbench .pane-body.integrated-terminal .split-view-view:not(:first-child) { border-color: ${borderColor.toString()}; }`);
}
});
class SwitchTerminalActionViewItem extends SelectActionViewItem {
private _lastOptions: ISelectOptionItem[] = [];
constructor(
action: IAction,
@ITerminalService private readonly _terminalService: ITerminalService,
@IThemeService private readonly _themeService: IThemeService,
@ITerminalContributionService private readonly _contributions: ITerminalContributionService,
@IContextViewService contextViewService: IContextViewService,
) {
super(null, action, getTerminalSelectOpenItems(_terminalService, _contributions), _terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.'), optionsAsChildren: true });
this._register(_terminalService.onInstancesChanged(this._updateItems, this));
this._register(_terminalService.onActiveTabChanged(this._updateItems, this));
this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this));
this._register(_terminalService.onTabDisposed(this._updateItems, this));
this._register(_terminalService.onDidChangeConnectionState(this._updateItems, this));
this._register(attachSelectBoxStyler(this.selectBox, this._themeService));
}
render(container: HTMLElement): void {
super.render(container);
container.classList.add('switch-terminal');
this._register(attachStylerCallback(this._themeService, { selectBorder }, colors => {
container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : '';
}));
}
private _updateItems(): void {
const options = getTerminalSelectOpenItems(this._terminalService, this._contributions);
// only update options if they've changed
if (!equals(Object.values(options), Object.values(this._lastOptions))) {
this.setOptions(options, this._terminalService.activeTabIndex);
this._lastOptions = options;
}
}
}
function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] {
const items = terminalService.connectionState === TerminalConnectionState.Connected ?
terminalService.getTabLabels().map(label => <ISelectOptionItem>{ text: label }) :
[{ text: nls.localize('terminalConnectingLabel', "Starting...") }];
items.push({ text: switchTerminalActionViewItemSeparator, isDisabled: true });
for (const contributed of contributions.terminalTypes) {
items.push({ text: contributed.title });
}
items.push({ text: selectDefaultShellTitle });
items.push({ text: configureTerminalSettingsTitle });
return items;
}