Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terminal editor lifecycle work #126383

Merged
merged 5 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Tyriar marked this conversation as resolved.
Show resolved Hide resolved
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