Skip to content

Commit

Permalink
Merge pull request #120878 from microsoft/merogge/tabs-button
Browse files Browse the repository at this point in the history
+ button and context menu for terminal tabs
  • Loading branch information
Tyriar committed Apr 14, 2021
2 parents 084668c + 066dae2 commit 4ffd6a8
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 55 deletions.
1 change: 1 addition & 0 deletions src/vs/platform/actions/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class MenuId {
static readonly AccountsContext = new MenuId('AccountsContext');
static readonly PanelTitle = new MenuId('PanelTitle');
static readonly TerminalContext = new MenuId('TerminalContext');
static readonly TerminalTabsContext = new MenuId('TerminalTabsContext');

readonly id: number;
readonly _debugName: string;
Expand Down
3 changes: 2 additions & 1 deletion src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface IPtyHostAttachTarget {
workspaceId: string;
workspaceName: string;
isOrphan: boolean;
icon: string | undefined;
}

export type ITerminalsLayoutInfo = IRawTerminalsLayoutInfo<IPtyHostAttachTarget | null>;
Expand Down Expand Up @@ -251,7 +252,7 @@ export interface IShellLaunchConfig {
/**
* This is a terminal that attaches to an already running terminal.
*/
attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; };
attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; icon?: string; };

/**
* Whether the terminal process environment should be exactly as provided in
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/terminal/common/terminalProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface IProcessDetails {
workspaceId: string;
workspaceName: string;
isOrphan: boolean;
icon: string | undefined;
}

export type ITerminalTabLayoutInfoDto = IRawTerminalTabLayoutInfo<IProcessDetails>;
Expand Down
9 changes: 6 additions & 3 deletions src/vs/platform/terminal/node/ptyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class PtyService extends Disposable implements IPtyService {
if (process.onProcessResolvedShellLaunchConfig) {
process.onProcessResolvedShellLaunchConfig(event => this._onProcessResolvedShellLaunchConfig.fire({ id, event }));
}
const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, this._logService);
const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, this._logService, shellLaunchConfig.icon);
process.onProcessExit(() => {
persistentProcess.dispose();
this._ptys.delete(id);
Expand Down Expand Up @@ -217,7 +217,8 @@ export class PtyService extends Disposable implements IPtyService {
workspaceId: persistentProcess.workspaceId,
workspaceName: persistentProcess.workspaceName,
cwd,
isOrphan
isOrphan,
icon: persistentProcess.icon
};
}

Expand Down Expand Up @@ -267,6 +268,7 @@ export class PersistentTerminalProcess extends Disposable {

get pid(): number { return this._pid; }
get title(): string { return this._terminalProcess.currentTitle; }
get icon(): string | undefined { return this._icon; }

constructor(
private _persistentProcessId: number,
Expand All @@ -275,7 +277,8 @@ export class PersistentTerminalProcess extends Disposable {
public readonly workspaceName: string,
public readonly shouldPersistTerminal: boolean,
cols: number, rows: number,
private readonly _logService: ILogService
private readonly _logService: ILogService,
private readonly _icon?: string
) {
super();
this._recorder = new TerminalRecorder(cols, rows);
Expand Down
43 changes: 43 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/media/terminal.css
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,49 @@
padding: 0 22px 0 6px;
}

#terminal-tabs-plus-button {
min-height: 10;
flex-grow: 0;
text-align: center;
z-index: 10;
}

#terminal-tabs-plus-button.align-left {
padding-left: 10px;
text-align: left;
z-index: 10;
}

.tabs-container {
display: flex;
flex-direction: column;
}

.tabs-widget .terminal-tabs-entry {
text-align: center;
}

.tabs-widget .terminal-tabs-entry.has-text {
padding-left: 10px;
text-align: left;
}

.monaco-workbench .tabs-widget .codicon {
vertical-align: text-bottom;
}

.tabs-widget .monaco-tl-twistie.force-no-twistie {
display: none !important;
}

.tabs-widget .actions {
display: none;
}

.tabs-widget .actions .action-label {
padding: 2px;
}

.tabs-widget .monaco-list-row:hover .actions {
display: block;
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
title: termDto.title,
cwd: termDto.cwd,
workspaceId: termDto.workspaceId,
workspaceName: termDto.workspaceName
workspaceName: termDto.workspaceName,
icon: termDto.icon
};
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export interface ITerminalService {
*/
getAvailableProfiles(): ITerminalProfile[];

getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined;

setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
manageWorkspaceShellPermissions(): void;

Expand Down
60 changes: 55 additions & 5 deletions src/vs/workbench/contrib/terminal/browser/terminalActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/w
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_IS_OPEN, 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 { IRemoteTerminalAttachTarget, ITerminalConfigHelper, ITerminalProfile, 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_IS_OPEN, 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';
Expand All @@ -48,6 +48,12 @@ const enum ContextMenuGroup {
Kill = '4_kill'
}

export const enum ContextMenuTabsGroup {
Default = '1_create_default',
Profile = '2_create_profile',
Configure = '3_configure'
}

async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise<string | URI | undefined> {
switch (configHelper.config.splitCwd) {
case 'workspaceRoot':
Expand Down Expand Up @@ -135,13 +141,30 @@ export function registerTerminalActions() {
id: TERMINAL_COMMAND_ID.NEW_WITH_PROFILE,
title: { value: localize('workbench.action.terminal.newWithProfile', "Create New Integrated Terminal (With Profile)"), original: 'Create New Integrated Terminal (With Profile)' },
f1: true,
category
category,
description: {
description: 'workbench.action.terminal.newWithProfile',
args: [{
name: 'profile',
schema: {
type: 'object'
}
}]
},
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(ITerminalService).showProfileQuickPick('createInstance');
async run(accessor: ServicesAccessor, profile?: ITerminalProfile) {
const terminalService = accessor.get(ITerminalService);
if (profile) {
const instance = await terminalService.createTerminal(profile);
terminalService.setActiveInstance(instance);
return terminalService.showPanel(true);
} else {
await terminalService.showProfileQuickPick('createInstance');
}
}
});

registerAction2(class extends Action2 {
constructor() {
super({
Expand Down Expand Up @@ -629,6 +652,13 @@ export function registerTerminalActions() {
});
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalContext, {
command: {
id: TERMINAL_COMMAND_ID.RENAME,
title: localize('workbench.action.terminal.rename', "Rename")
},
group: ContextMenuGroup.Edit
});
registerAction2(class extends Action2 {
constructor() {
super({
Expand Down Expand Up @@ -1288,6 +1318,13 @@ export function registerTerminalActions() {
},
group: ContextMenuGroup.Create
});
MenuRegistry.appendMenuItem(MenuId.TerminalTabsContext, {
command: {
id: TERMINAL_COMMAND_ID.NEW,
title: localize('workbench.action.terminal.new.short', "New Terminal")
},
group: ContextMenuTabsGroup.Default
});
registerAction2(class extends Action2 {
constructor() {
super({
Expand Down Expand Up @@ -1365,6 +1402,13 @@ export function registerTerminalActions() {
await accessor.get(ITerminalService).showProfileQuickPick('setDefault');
}
});
MenuRegistry.appendMenuItem(MenuId.TerminalTabsContext, {
command: {
id: TERMINAL_COMMAND_ID.SELECT_DEFAULT_PROFILE,
title: { value: localize('workbench.action.terminal.selectDefaultProfile', "Select Default Profile"), original: 'Select Default Profile' }
},
group: ContextMenuTabsGroup.Configure
});
registerAction2(class extends Action2 {
constructor() {
super({
Expand All @@ -1379,7 +1423,13 @@ export function registerTerminalActions() {
await accessor.get(IPreferencesService).openSettings(false, '@feature:terminal');
}
});

MenuRegistry.appendMenuItem(MenuId.TerminalTabsContext, {
command: {
id: TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS,
title: localize(configureTerminalSettingsTitle, 'Configure Terminal Settings')
},
group: ContextMenuTabsGroup.Configure
});
// Some commands depend on platform features
if (BrowserFeatures.clipboard.writeText) {
registerAction2(class extends Action2 {
Expand Down
11 changes: 10 additions & 1 deletion src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public get commandTracker(): CommandTrackerAddon | undefined { return this._commandTrackerAddon; }
public get navigationMode(): INavigationMode | undefined { return this._navigationModeAddon; }
public get isDisconnected(): boolean { return this._processManager.isDisconnected; }
public get icon(): Codicon { return this.shellLaunchConfig.icon ? (iconRegistry.get(this.shellLaunchConfig.icon) || Codicon.terminal) : Codicon.terminal; }
public get icon(): Codicon { return this._getIcon(); }

private readonly _onExit = new Emitter<number | undefined>();
public get onExit(): Event<number | undefined> { return this._onExit.event; }
Expand Down Expand Up @@ -295,6 +295,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
});
}

private _getIcon(): Codicon {
if (this.shellLaunchConfig.icon) {
return iconRegistry.get(this.shellLaunchConfig.icon) || Codicon.terminal;
} else if (this.shellLaunchConfig?.attachPersistentProcess?.icon) {
return iconRegistry.get(this.shellLaunchConfig.attachPersistentProcess.icon) || Codicon.terminal;
}
return Codicon.terminal;
}

public addDisposable(disposable: IDisposable): void {
this._register(disposable);
}
Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/contrib/terminal/browser/terminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export class TerminalService implements ITerminalService {
if (!terminalInstance) {
// create tab and terminal
terminalInstance = this.createTerminal({ attachPersistentProcess: terminalLayout.terminal! });
tab = this._getTabForInstance(terminalInstance);
tab = this.getTabForInstance(terminalInstance);
if (tabLayout.isActive) {
activeTab = tab;
}
Expand Down Expand Up @@ -650,7 +650,7 @@ export class TerminalService implements ITerminalService {
public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance | null;
public splitInstance(instanceToSplit: ITerminalInstance, profile: ITerminalProfile): ITerminalInstance | null
public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile = {}): ITerminalInstance | null {
const tab = this._getTabForInstance(instanceToSplit);
const tab = this.getTabForInstance(instanceToSplit);
if (!tab) {
return null;
}
Expand Down Expand Up @@ -715,7 +715,7 @@ export class TerminalService implements ITerminalService {
}
}

private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined {
public getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined {
return this._terminalTabs.find(tab => tab.terminalInstances.indexOf(instance) !== -1);
}

Expand Down

0 comments on commit 4ffd6a8

Please sign in to comment.