From 120f9b88cdeb8ae3e722e82c346e15589301fd6f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 15 Jun 2021 04:34:42 -0700 Subject: [PATCH 1/4] Change createTerminal signature to use options object --- .../contrib/terminal/browser/terminal.ts | 35 ++++++++++++++----- .../terminal/browser/terminalActions.ts | 8 +++-- .../terminal/browser/terminalService.ts | 21 ++++++----- .../browser/testingOutputTerminalService.ts | 10 +++--- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index d527ae75a51f9..39ca82e024ab0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -90,6 +90,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; @@ -131,16 +154,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; /** diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index e6ebe76e10aa5..7535415414f6d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -180,7 +180,7 @@ export function registerTerminalActions() { } if (profile) { - instance = terminalService.createTerminal(profile, cwd); + instance = terminalService.createTerminal({ config: profile, cwd }); } else { instance = await terminalService.showProfileQuickPick('createInstance', cwd); } @@ -918,7 +918,9 @@ export function registerTerminalActions() { } const selected = await quickInputService.pick(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); } @@ -1799,7 +1801,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}"`); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 7184cad2b88ab..651aff5712cbf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -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 } 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'; @@ -277,7 +277,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; @@ -988,7 +990,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 }); } } @@ -1100,13 +1102,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) { diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts b/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts index e09599c30ebd4..3e2cbafbb0c63 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputTerminalService.ts @@ -101,10 +101,12 @@ export class TestingOutputTerminalService implements ITestingOutputTerminalServi const output = new TestOutputProcess(); this.showResultsInTerminal(this.terminalService.createTerminal({ - isFeatureTerminal: true, - icon: testingViewIcon, - customPtyImplementation: () => output, - name: getTitle(result), + config: { + isFeatureTerminal: true, + icon: testingViewIcon, + customPtyImplementation: () => output, + name: getTitle(result), + } }), output, result); } From 86b4336e37b4e82672fb4ca174df3a82f50f2d6c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 15 Jun 2021 04:44:53 -0700 Subject: [PATCH 2/4] Make detach return a Promise Part of #118276 --- src/vs/platform/terminal/common/terminal.ts | 2 +- .../workbench/contrib/terminal/electron-sandbox/localPty.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 8de007f5db142..493881fff71ac 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -478,7 +478,7 @@ export interface ITerminalChildProcess { /** * Detach the process from the UI and await reconnect. */ - detach?(): void; + detach?(): Promise; /** * Shutdown the terminal process. diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts index 529d9294fa395..cd090547430f3 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts @@ -44,8 +44,8 @@ export class LocalPty extends Disposable implements ITerminalChildProcess { start(): Promise { return this._localPtyService.start(this.id); } - detach(): void { - this._localPtyService.detachFromProcess(this.id); + detach(): Promise { + return this._localPtyService.detachFromProcess(this.id); } shutdown(immediate: boolean): void { this._localPtyService.shutdown(this.id, immediate); From 4475d8461e5a39254cd4a831137d684f39a47716 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 15 Jun 2021 05:03:39 -0700 Subject: [PATCH 3/4] Create editor terminal in terminal service --- .../contrib/terminal/browser/terminal.ts | 2 + .../terminal/browser/terminalActions.ts | 22 ++++++----- .../terminal/browser/terminalService.ts | 38 ++++++++++++++----- .../terminal/browser/terminalTabsList.ts | 4 +- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 39ca82e024ab0..5e52d74b2f7e8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -312,6 +312,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. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 7535415414f6d..f623c001d8419 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -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'; @@ -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'; @@ -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'; @@ -205,10 +203,14 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); - const editorService = accessor.get(IEditorService); - const instantiationService = accessor.get(IInstantiationService); - const input = TerminalEditorInput.copy(terminalService, instantiationService); - await editorService.openEditor(input, { pinned: true, forceReload: true }); + // TODO: Await openEditor + terminalService.createTerminal({ + target: TerminalTarget.Editor + }); + // const editorService = accessor.get(IEditorService); + // const instantiationService = accessor.get(IInstantiationService); + // const input = TerminalEditorInput.copy(terminalService, instantiationService); + // await editorService.openEditor(input, { pinned: true, forceReload: true }); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 651aff5712cbf..cdad0f97d719c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -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, ICreateTerminalOptions } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState, ITerminalProfileProvider, ICreateTerminalOptions, TerminalTarget } 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'; @@ -42,6 +42,8 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { registerTerminalDefaultProfileConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; export class TerminalService implements ITerminalService { declare _serviceBrand: undefined; @@ -134,6 +136,7 @@ export class TerminalService implements ITerminalService { constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, + @IEditorService private readonly _editorService: IEditorService, @ILabelService labelService: ILabelService, @ILifecycleService lifecycleService: ILifecycleService, @IDialogService private _dialogService: IDialogService, @@ -1134,17 +1137,32 @@ 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) { + const input = TerminalEditorInput.copy(this, this._instantiationService); + instance = input.terminalInstance; + instance.target = TerminalTarget.Editor; + // TODO: This is a promise - handle errors + this._editorService.openEditor(input, { + pinned: true, + forceReload: true + }); + // TODO: Push to terminal editors + 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 diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index f615a38679310..d1545684eb4a3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -9,7 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalInstanceService, ITerminalService, TerminalTarget } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -107,7 +107,7 @@ export class TerminalTabList extends WorkbenchList { this._terminalService.onDidChangeConnectionState(() => this.refresh()); this._themeService.onDidColorThemeChange(() => this.refresh()); this._terminalService.onActiveInstanceChanged(e => { - if (e) { + if (e && e.target !== TerminalTarget.Editor) { const i = this._terminalService.terminalInstances.indexOf(e); this.setSelection([i]); this.reveal(i); From a215c798d53e5a521ddfe4250bcce6641b5c4d1c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 15 Jun 2021 06:10:21 -0700 Subject: [PATCH 4/4] Move editor creation into new service --- .../terminal/browser/terminal.contribution.ts | 4 ++- .../contrib/terminal/browser/terminal.ts | 13 ++++++++ .../terminal/browser/terminalEditorService.ts | 33 +++++++++++++++++++ .../terminal/browser/terminalService.ts | 18 +++------- 4 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index bb19ebc1a072e..f034d9babd899 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -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'; @@ -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); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 5e52d74b2f7e8..9597a28ac2806 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -20,6 +20,7 @@ import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IEditableData } from 'vs/workbench/common/views'; export const ITerminalService = createDecorator('terminalService'); +export const ITerminalEditorService = createDecorator('terminalEditorService'); export const ITerminalInstanceService = createDecorator('terminalInstanceService'); export const IRemoteTerminalService = createDecorator('remoteTerminalService'); @@ -234,6 +235,18 @@ export interface ITerminalService { safeDisposeTerminal(instance: ITerminalInstance): Promise; } +/** + * 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; +} + export interface IRemoteTerminalService extends IOffProcessTerminalService { createProcess(shellLaunchConfig: IShellLaunchConfig, configuration: ICompleteTerminalConfiguration, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, shouldPersist: boolean, configHelper: ITerminalConfigHelper): Promise; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts new file mode 100644 index 0000000000000..f7c44b36add51 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts @@ -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 = new Map(); + + constructor( + @IEditorService private readonly _editorService: IEditorService + ) { + // TODO: Multiplex instance events + } + + async createEditor(instance: ITerminalInstance): Promise { + 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); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index cdad0f97d719c..ca37631f7516f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -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, ICreateTerminalOptions, TerminalTarget } 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'; @@ -42,8 +42,6 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { registerTerminalDefaultProfileConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; export class TerminalService implements ITerminalService { declare _serviceBrand: undefined; @@ -136,7 +134,6 @@ export class TerminalService implements ITerminalService { constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, - @IEditorService private readonly _editorService: IEditorService, @ILabelService labelService: ILabelService, @ILifecycleService lifecycleService: ILifecycleService, @IDialogService private _dialogService: IDialogService, @@ -150,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 @@ -1139,15 +1137,8 @@ export class TerminalService implements ITerminalService { let instance: ITerminalInstance; if (options?.target === TerminalTarget.Editor) { - const input = TerminalEditorInput.copy(this, this._instantiationService); - instance = input.terminalInstance; - instance.target = TerminalTarget.Editor; - // TODO: This is a promise - handle errors - this._editorService.openEditor(input, { - pinned: true, - forceReload: true - }); - // TODO: Push to terminal editors + instance = this.createInstance(shellLaunchConfig); + this._terminalEditorService.createEditor(instance); this._initInstanceListeners(instance); this._onInstancesChanged.fire(); } else { @@ -1163,6 +1154,7 @@ export class TerminalService implements ITerminalService { 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