Skip to content

Commit

Permalink
Merge pull request #126383 from microsoft/merogge/terminalEditor_tyriar
Browse files Browse the repository at this point in the history
Terminal editor lifecycle work
  • Loading branch information
Tyriar authored Jun 15, 2021
2 parents 46877c9 + f569691 commit 52d8d8e
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 58 deletions.
2 changes: 1 addition & 1 deletion src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ export interface ITerminalChildProcess {
/**
* Detach the process from the UI and await reconnect.
*/
detach?(): void;
detach?(): Promise<void>;

/**
* Shutdown the terminal process.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalCol
import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands';
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IRemoteTerminalService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteTerminalService, ITerminalEditorService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
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';
Expand All @@ -42,9 +42,11 @@ import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor';
import { TerminalInputSerializer, TerminalEditor } from 'vs/workbench/contrib/terminal/browser/terminalEditor';
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings';
import { TerminalEditorService } from 'vs/workbench/contrib/terminal/browser/terminalEditorService';

// Register services
registerSingleton(ITerminalService, TerminalService, true);
registerSingleton(ITerminalEditorService, TerminalEditorService, true);
registerSingleton(IRemoteTerminalService, RemoteTerminalService);
registerSingleton(ITerminalInstanceService, TerminalInstanceService, true);

Expand Down
50 changes: 41 additions & 9 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { IEditableData } from 'vs/workbench/common/views';

export const ITerminalService = createDecorator<ITerminalService>('terminalService');
export const ITerminalEditorService = createDecorator<ITerminalEditorService>('terminalEditorService');
export const ITerminalInstanceService = createDecorator<ITerminalInstanceService>('terminalInstanceService');
export const IRemoteTerminalService = createDecorator<IRemoteTerminalService>('remoteTerminalService');

Expand Down Expand Up @@ -90,6 +91,29 @@ export const enum TerminalConnectionState {
Connected
}

export const enum TerminalTarget {
TerminalView = 'view',
Editor = 'editor'
}

export interface ICreateTerminalOptions {
/**
* The shell launch config or profile to launch with, when not specified the default terminal
* profile will be used.
*/
config?: IShellLaunchConfig | ITerminalProfile;
// TODO: Ensure this is supported for profiles
/**
* The current working directory to start with, this will override IShellLaunchConfig.cwd if
* specified.
*/
cwd?: string | URI;
/**
* Where to create the terminal, when not specified the default target will be used.
*/
target?: TerminalTarget;
}

export interface ITerminalService {
readonly _serviceBrand: undefined;

Expand Down Expand Up @@ -131,16 +155,10 @@ export interface ITerminalService {

/**
* Creates a terminal.
* @param shell The shell launch configuration to use.
* @param options The options to create the terminal with, when not specified the default
* profile will be used at the default target.
*/
createTerminal(shell?: IShellLaunchConfig, cwd?: string | URI): ITerminalInstance;

/**
* Creates a terminal.
* @param profile The profile to launch the terminal with.
*/
createTerminal(profile: ITerminalProfile): ITerminalInstance;

createTerminal(options?: ICreateTerminalOptions): ITerminalInstance;
createContributedTerminalProfile(extensionIdentifier: string, id: string, isSplitTerminal: boolean): Promise<void>;

/**
Expand Down Expand Up @@ -217,6 +235,18 @@ export interface ITerminalService {
safeDisposeTerminal(instance: ITerminalInstance): Promise<void>;
}

/**
* This service is responsible for integrating with the editor service and managing terminal
* editors.
*/
export interface ITerminalEditorService {
readonly _serviceBrand: undefined;

readonly terminalEditorInstances: ITerminalInstance[];

createEditor(instance: ITerminalInstance): Promise<void>;
}

export interface IRemoteTerminalService extends IOffProcessTerminalService {
createProcess(shellLaunchConfig: IShellLaunchConfig, configuration: ICompleteTerminalConfiguration, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, shouldPersist: boolean, configHelper: ITerminalConfigHelper): Promise<ITerminalChildProcess>;
}
Expand Down Expand Up @@ -295,6 +325,8 @@ export interface ITerminalInstance {
*/
processId: number | undefined;

target?: TerminalTarget;

/**
* The id of a persistent process. This is defined if this is a terminal created by a pty host
* that supports reconnection.
Expand Down
27 changes: 14 additions & 13 deletions src/vs/workbench/contrib/terminal/browser/terminalActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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 { isWindows, isLinux } from 'vs/base/common/platform';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { withNullAsUndefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
Expand All @@ -19,7 +19,7 @@ import { Action2, ICommandActionTitle, ILocalizedString, registerAction2 } from
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
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 { IListService } from 'vs/platform/list/browser/listService';
Expand All @@ -30,13 +30,11 @@ import { ILocalTerminalService, ITerminalProfile, TerminalSettingId, TitleEventS
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions';
import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalInstanceService, ITerminalService, TerminalTarget } 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_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
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_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TerminalCommandId, TERMINAL_ACTION_CATEGORY } from 'vs/workbench/contrib/terminal/common/terminal';
import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
Expand Down Expand Up @@ -180,7 +178,7 @@ export function registerTerminalActions() {
}

if (profile) {
instance = terminalService.createTerminal(profile, cwd);
instance = terminalService.createTerminal({ config: profile, cwd });
} else {
instance = await terminalService.showProfileQuickPick('createInstance', cwd);
}
Expand All @@ -204,10 +202,11 @@ export function registerTerminalActions() {
});
}
async run(accessor: ServicesAccessor) {
const editorService = accessor.get(IEditorService);
const instantiationService = accessor.get(IInstantiationService);
const input = TerminalEditorInput.copy(instantiationService);
await editorService.openEditor(input, { pinned: true, forceReload: true });
const terminalService = accessor.get(ITerminalService);
// TODO: Await openEditor
terminalService.createTerminal({
target: TerminalTarget.Editor
});
}
});

Expand Down Expand Up @@ -917,7 +916,9 @@ export function registerTerminalActions() {
}
const selected = await quickInputService.pick<IRemoteTerminalPick>(items, { canPickMany: false });
if (selected) {
const instance = terminalService.createTerminal({ attachPersistentProcess: selected.term });
const instance = terminalService.createTerminal({
config: { attachPersistentProcess: selected.term }
});
terminalService.setActiveInstance(instance);
terminalService.showPanel(true);
}
Expand Down Expand Up @@ -1798,7 +1799,7 @@ export function registerTerminalActions() {
if (quickSelectProfiles) {
const profile = quickSelectProfiles.find(profile => profile.profileName === profileSelection);
if (profile) {
const instance = terminalService.createTerminal(profile);
const instance = terminalService.createTerminal({ config: profile });
terminalService.setActiveInstance(instance);
} else {
console.warn(`No profile with name "${profileSelection}"`);
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/contrib/terminal/browser/terminalEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class TerminalInputSerializer implements IEditorInputSerializer {
}

public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): TerminalEditorInput {
return TerminalEditorInput.copy(instantiationService);
throw new Error('NYI');
// return TerminalEditorInput.copy(instantiationService);
}
}
13 changes: 6 additions & 7 deletions src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';

export class TerminalEditorInput extends EditorInput {

Expand All @@ -27,15 +26,15 @@ export class TerminalEditorInput extends EditorInput {
}

constructor(
@ITerminalService terminalService: ITerminalService
terminalInstance: ITerminalInstance
) {
super();
this._terminalInstance = terminalService.createTerminal();
this._terminalInstance = terminalInstance;
}

static copy(instantiationService: IInstantiationService): TerminalEditorInput {
return instantiationService.createInstance(TerminalEditorInput);
}
// static copy(instantiationService: IInstantiationService): TerminalEditorInput {
// return instantiationService.createInstance(TerminalEditorInput);
// }

override getName() {
return this.terminalInstance.title;
Expand Down
33 changes: 33 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ITerminalEditorService, ITerminalInstance, TerminalTarget } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

export class TerminalEditorService implements ITerminalEditorService {
declare _serviceBrand: undefined;

terminalEditorInstances: ITerminalInstance[] = [];

private _editorInputs: Map</*instanceId*/number, TerminalEditorInput> = new Map();

constructor(
@IEditorService private readonly _editorService: IEditorService
) {
// TODO: Multiplex instance events
}

async createEditor(instance: ITerminalInstance): Promise<void> {
instance.target = TerminalTarget.Editor;
const input = new TerminalEditorInput(instance);
this._editorInputs.set(instance.instanceId, input);
await this._editorService.openEditor(input, {
pinned: true,
forceReload: true
});
this.terminalEditorInstances.push(instance);
}
}
49 changes: 31 additions & 18 deletions src/vs/workbench/contrib/terminal/browser/terminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickPi
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILocalTerminalService, IOffProcessTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalSettingId, TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState, ITerminalProfileProvider } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState, ITerminalProfileProvider, ICreateTerminalOptions, TerminalTarget, ITerminalEditorService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IEditableData, IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
Expand Down Expand Up @@ -147,6 +147,7 @@ export class TerminalService implements ITerminalService {
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService,
@ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService,
@IExtensionService private readonly _extensionService: IExtensionService,
@INotificationService private readonly _notificationService: INotificationService,
@optional(ILocalTerminalService) localTerminalService: ILocalTerminalService
Expand Down Expand Up @@ -277,7 +278,9 @@ export class TerminalService implements ITerminalService {
terminalLayouts.forEach((terminalLayout) => {
if (!terminalInstance) {
// create group and terminal
terminalInstance = this.createTerminal({ attachPersistentProcess: terminalLayout.terminal! });
terminalInstance = this.createTerminal({
config: { attachPersistentProcess: terminalLayout.terminal! }
});
group = this.getGroupForInstance(terminalInstance);
if (groupLayout.isActive) {
activeGroup = group;
Expand Down Expand Up @@ -988,7 +991,7 @@ export class TerminalService implements ITerminalService {
// create split, only valid if there's an active instance
instance = this.splitInstance(activeInstance, value.profile, cwd);
} else {
instance = this.createTerminal(value.profile, cwd);
instance = this.createTerminal({ config: value.profile, cwd });
}
}

Expand Down Expand Up @@ -1100,13 +1103,14 @@ export class TerminalService implements ITerminalService {
return {};
}

createTerminal(shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance;
createTerminal(profile: ITerminalProfile, cwd?: string | URI): ITerminalInstance;
createTerminal(shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): ITerminalInstance {
const shellLaunchConfig = this._convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile);
// createTerminal(shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance;
// createTerminal(profile: ITerminalProfile, cwd?: string | URI): ITerminalInstance;
// createTerminal(shellLaunchConfigOrProfile: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): ITerminalInstance {
createTerminal(options?: ICreateTerminalOptions): ITerminalInstance {
const shellLaunchConfig = this._convertProfileToShellLaunchConfig(options?.config);

if (cwd) {
shellLaunchConfig.cwd = cwd;
if (options?.cwd) {
shellLaunchConfig.cwd = options.cwd;
}

if (!shellLaunchConfig.customPtyImplementation && !this.isProcessSupportRegistered) {
Expand All @@ -1131,17 +1135,26 @@ export class TerminalService implements ITerminalService {
}
}

const terminalGroup = this._instantiationService.createInstance(TerminalGroup, this._terminalContainer, shellLaunchConfig);
this._terminalGroups.push(terminalGroup);
terminalGroup.onPanelOrientationChanged((orientation) => this._onPanelOrientationChanged.fire(orientation));
let instance: ITerminalInstance;
if (options?.target === TerminalTarget.Editor) {
instance = this.createInstance(shellLaunchConfig);
this._terminalEditorService.createEditor(instance);
this._initInstanceListeners(instance);
this._onInstancesChanged.fire();
} else {
const terminalGroup = this._instantiationService.createInstance(TerminalGroup, this._terminalContainer, shellLaunchConfig);
this._terminalGroups.push(terminalGroup);
terminalGroup.onPanelOrientationChanged((orientation) => this._onPanelOrientationChanged.fire(orientation));

const instance = terminalGroup.terminalInstances[0];
instance = terminalGroup.terminalInstances[0];

terminalGroup.addDisposable(terminalGroup.onDisposed(this._onGroupDisposed.fire, this._onGroupDisposed));
terminalGroup.addDisposable(terminalGroup.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged));
this._initInstanceListeners(instance);
this._onInstancesChanged.fire();
this._onGroupsChanged.fire();
}

terminalGroup.addDisposable(terminalGroup.onDisposed(this._onGroupDisposed.fire, this._onGroupDisposed));
terminalGroup.addDisposable(terminalGroup.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged));
this._initInstanceListeners(instance);
this._onInstancesChanged.fire();
this._onGroupsChanged.fire();
if (this.terminalInstances.length === 1) {
// It's the first instance so it should be made active automatically, this must fire
// after onInstancesChanged so consumers can react to the instance being added first
Expand Down
Loading

0 comments on commit 52d8d8e

Please sign in to comment.