Squashed commit of the following:

commit 89de2bd6ed
Author: meganrogge <megrogge@gmail.com>
Date:   Tue Jun 15 11:43:13 2021 -0700

    dispose of instance on close of editor

commit 583f348737
Author: meganrogge <megrogge@gmail.com>
Date:   Tue Jun 15 11:21:02 2021 -0700

    get unique terminal instances to work
    Co-authored-by: Daniel Imms <daimms@microsoft.com>

commit 7fa7440d6e
Author: meganrogge <megrogge@gmail.com>
Date:   Tue Jun 15 09:44:29 2021 -0700

    call attachToElement in setInput

commit 5416f7555c
Author: Benjamin Pasero <benjamin.pasero@microsoft.com>
Date:   Tue Jun 15 15:49:49 2021 +0200

    :chore: - ignore unhandled promise cancel rejections

    //cc @joaomoreno @alexdima @sbatten

commit 55c6ee0872
Author: Benjamin Pasero <benjamin.pasero@microsoft.com>
Date:   Tue Jun 15 15:45:10 2021 +0200

    .

commit 25fe69f920
Author: Benjamin Pasero <benjamin.pasero@microsoft.com>
Date:   Tue Jun 15 15:40:20 2021 +0200

    fix emmet test integration path

commit f4bc7d3c32
Author: Martin Aeschlimann <martinae@microsoft.com>
Date:   Tue Jun 15 15:34:54 2021 +0200

    Use custom hover for all status bar entries

commit 5b79dcf94a
Author: Martin Aeschlimann <martinae@microsoft.com>
Date:   Tue Jun 15 15:17:12 2021 +0200

    MarkdownString for StatusBarItem.tooltip. For #126258

commit 0a0c8f3d2a
Author: João Moreno <joao.moreno@microsoft.com>
Date:   Tue Jun 15 15:29:36 2021 +0200

    Revert "switch to generic pool"

    This reverts commit 6495c052ab.

commit 19f9cd42ea
Author: Johannes Rieken <johannes.rieken@gmail.com>
Date:   Tue Jun 15 15:24:09 2021 +0200

    fix https://github.com/microsoft/vscode/issues/125942

commit c6945f1c52
Author: João Moreno <joao.moreno@microsoft.com>
Date:   Tue Jun 15 15:15:41 2021 +0200

    fix integration browser tests

commit e99f7918b8
Author: Sandeep Somavarapu <sasomava@microsoft.com>
Date:   Mon Jun 14 23:29:27 2021 +0200

    - move writing user configuration to main process
    - safe guard read from writing in parallel from other windows

commit 4a840ce386
Author: Megan Rogge <megan.rogge@microsoft.com>
Date:   Tue Jun 15 07:31:23 2021 -0500

    finalize terminal iconPath (#125783)

commit 5745873a72
Author: Alex Ross <alros@microsoft.com>
Date:   Tue Jun 15 14:08:59 2021 +0200

    Fix timing issue where port detection can fail to start

commit d47661d453
Author: João Moreno <joao.moreno@microsoft.com>
Date:   Tue Jun 15 14:07:35 2021 +0200

    skip the entire notebook test suite so Code isn't even launched

commit 41c7c251ca
Author: Alex Ross <alros@microsoft.com>
Date:   Tue Jun 15 13:20:11 2021 +0200

    Fix task close=false
    Fixes #126352

commit 5d70c90530
Author: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com>
Date:   Tue Jun 15 13:08:59 2021 +0200

    💄 clean-up workspace contribution

commit 537b1f0b9e
Author: meganrogge <megrogge@gmail.com>
Date:   Tue Jun 15 07:00:12 2021 -0700

    add icon manager

commit e97953a703
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 06:57:18 2021 -0700

    Add move to editor command

    Part of #126247

commit 56501c57f5
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 06:50:21 2021 -0700

    Disable serialization by returning undefined

commit 52d8d8e77a
Merge: 46877c9abc f5696915ba
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 06:43:37 2021 -0700

    Merge pull request #126383 from microsoft/merogge/terminalEditor_tyriar

    Terminal editor lifecycle work

commit f5696915ba
Merge: a215c798d5 46877c9abc
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 06:26:45 2021 -0700

    Merge remote-tracking branch 'origin/merogge/terminalEditor' into merogge/terminalEditor_tyriar

commit 46877c9abc
Author: meganrogge <megrogge@gmail.com>
Date:   Tue Jun 15 06:16:17 2021 -0700

    fix serialization error

commit a215c798d5
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 06:10:21 2021 -0700

    Move editor creation into new service

commit 4475d8461e
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 05:03:39 2021 -0700

    Create editor terminal in terminal service

commit 86b4336e37
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:44:53 2021 -0700

    Make detach return a Promise

    Part of #118276

commit 120f9b88cd
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:34:42 2021 -0700

    Change createTerminal signature to use options object

commit 0baba5e51e
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:14:27 2021 -0700

    Rename editor to just 'Terminal'

    Other editors that don't actually edit don't have 'Editor' in the name either (eg.
    Running Extensions), so it will be an internal term.

commit 8bb96ced86
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:11:08 2021 -0700

    Inline terminal editor input ID

commit c2304c2330
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:07:57 2021 -0700

    Remove unneeded !

commit 8367f1875a
Merge: c29d362ec2 9b4f499aca
Author: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date:   Tue Jun 15 04:05:12 2021 -0700

    Merge remote-tracking branch 'origin/main' into merogge/terminalEditor

commit c29d362ec2
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 19:23:52 2021 -0700

    use terminal instance.resource

commit fb50a7fe7a
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 17:13:51 2021 -0700

    revert some css changes

commit 50b6b81f20
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 15:53:47 2021 -0700

    add copy and try a few other things

commit 0c226cd099
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 13:13:28 2021 -0700

    clean up

commit e4c8b94b29
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 12:03:05 2021 -0700

    remove unnecessary changes

commit 31bcda454a
Author: meganrogge <megrogge@gmail.com>
Date:   Mon Jun 14 11:45:41 2021 -0700

    add basics for #126244
This commit is contained in:
meganrogge 2021-06-15 11:55:37 -07:00
parent 79d338bbbe
commit 22f216d7bf
No known key found for this signature in database
GPG key ID: 3155C8B2F0428C81
16 changed files with 432 additions and 56 deletions

View file

@ -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.

View file

@ -17,41 +17,49 @@
.monaco-workbench .pane-body.integrated-terminal .terminal-outer-container,
.monaco-workbench .pane-body.integrated-terminal .terminal-groups-container,
.monaco-workbench .pane-body.integrated-terminal .terminal-group,
.monaco-workbench .pane-body.integrated-terminal .terminal-split-pane {
.monaco-workbench .pane-body.integrated-terminal .terminal-split-pane,
.monaco-workbench .editor-instance .terminal-split-pane,
.monaco-workbench .editor-instance .terminal-outer-container {
height: 100%;
}
.monaco-workbench .editor-instance .terminal-wrapper,
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper {
display: none;
margin: 0 10px;
bottom: 2px;
}
.monaco-workbench .editor-instance .terminal-wrapper.active,
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper.active {
display: block;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper.active {
position: absolute;
top: 0;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .terminal-wrapper {
.monaco-workbench .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .terminal-wrapper {
margin-left: 20px;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .terminal-wrapper {
.monaco-workbench .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .terminal-wrapper {
margin-right: 20px;
}
.monaco-workbench .pane-body.integrated-terminal .xterm a:not(.xterm-invalid-link) {
.monaco-workbench .xterm a:not(.xterm-invalid-link) {
/* To support message box sizing */
position: relative;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper > div {
.monaco-workbench .terminal-wrapper > div {
height: 100%;
}
.monaco-workbench .pane-body.integrated-terminal .xterm-viewport {
.monaco-workbench .xterm-viewport {
box-sizing: border-box;
margin-right: -10px;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm-viewport {
.monaco-workbench .monaco-split-view2.horizontal .split-view-view:last-child .xterm-viewport {
margin-right: -20px;
}

View file

@ -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';
@ -37,9 +37,16 @@ import { isIOS, isWindows } from 'vs/base/common/platform';
import { setupTerminalMenus } from 'vs/workbench/contrib/terminal/browser/terminalMenus';
import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration';
import { EditorExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
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);
@ -62,6 +69,18 @@ CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPick
registerTerminalPlatformConfiguration();
registerTerminalConfiguration();
Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).registerEditorInputSerializer(TerminalEditorInput.ID, TerminalInputSerializer);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
EditorDescriptor.create(
TerminalEditor,
TerminalEditor.ID,
terminalStrings.terminal
),
[
new SyncDescriptor(TerminalEditorInput)
]
);
// Register views
const VIEW_CONTAINER = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
id: TERMINAL_VIEW_ID,

View file

@ -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');
@ -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;
@ -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>;
/**
@ -163,6 +181,7 @@ export interface ITerminalService {
*/
moveGroup(source: ITerminalInstance, target: ITerminalInstance): void;
moveInstance(source: ITerminalInstance, target: ITerminalInstance, side: 'before' | 'after'): void;
moveToEditor(source: ITerminalInstance): void;
/**
* Perform an action with the active terminal instance, if the terminal does
@ -215,6 +234,20 @@ export interface ITerminalService {
setEditable(instance: ITerminalInstance, data: IEditableData | null): Promise<void>;
instanceIsSplit(instance: ITerminalInstance): boolean;
safeDisposeTerminal(instance: ITerminalInstance): Promise<void>;
getPlatformKey(): Promise<string>;
}
/**
* 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 {
@ -295,6 +328,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.
@ -564,6 +599,11 @@ export interface ITerminalInstance {
*/
attachToElement(container: HTMLElement): Promise<void> | void;
/**
* Detaches the terminal instance from the terminal editor DOM element.
*/
detachFromElement(): void;
/**
* Configure the dimensions of the terminal instance.
*

View file

@ -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';
@ -30,9 +30,9 @@ 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 { 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 { IHistoryService } from 'vs/workbench/services/history/common/history';
@ -178,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);
}
@ -191,6 +191,39 @@ export function registerTerminalActions() {
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TerminalCommandId.CreateTerminalEditor,
title: { value: localize('workbench.action.terminal.createTerminalEditor', "Create Terminal Editor"), original: 'Create Terminal Editor' },
f1: true,
category,
precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED
});
}
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
// TODO: Await openEditor
terminalService.createTerminal({
target: TerminalTarget.Editor
});
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TerminalCommandId.MoveToEditor,
title: { value: localize('workbench.action.terminal.moveToEditor', "Move From Terminal View to Editor"), original: 'Move From Terminal View to Editor' },
f1: true,
category
});
}
async run(accessor: ServicesAccessor) {
const terminalService = accessor.get(ITerminalService);
terminalService.doWithActiveInstance(instance => terminalService.moveToEditor(instance));
}
});
registerAction2(class extends Action2 {
constructor() {
@ -898,7 +931,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);
}
@ -1779,7 +1814,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}"`);

View file

@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Dimension } from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IEditorInputSerializer, IEditorOpenContext } from 'vs/workbench/common/editor';
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
export class TerminalEditor extends EditorPane {
public static readonly ID = 'terminalEditor';
private _parentElement: HTMLElement | undefined;
private _editorInput?: TerminalEditorInput = undefined;
private _lastDimension?: Dimension;
override async setInput(newInput: TerminalEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken) {
this._editorInput?.terminalInstance?.detachFromElement();
this._editorInput = newInput;
await super.setInput(newInput, options, context, token);
this._editorInput.terminalInstance.attachToElement(this._parentElement!);
if (this._lastDimension) {
this.layout(this._lastDimension);
}
this._editorInput.terminalInstance.setVisible(true);
}
// eslint-disable-next-line @typescript-eslint/naming-convention
protected createEditor(parent: HTMLElement): void {
this._parentElement = parent;
}
layout(dimension: Dimension): void {
if (this._editorInput?.terminalInstance) {
this._editorInput.terminalInstance.layout(dimension);
}
this._lastDimension = dimension;
}
override setVisible(visible: boolean, group?: IEditorGroup): void {
super.setVisible(visible, group);
if (!this._editorInput?.terminalInstance) {
return;
}
this._editorInput.terminalInstance.setVisible(visible);
}
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
) {
super(TerminalEditor.ID, telemetryService, themeService, storageService);
}
}
export class TerminalInputSerializer implements IEditorInputSerializer {
public canSerialize(editorInput: TerminalEditorInput): boolean {
return false;
}
public serialize(editorInput: TerminalEditorInput): string | undefined {
return undefined;
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): TerminalEditorInput {
// TODO: Attach to instanceId via pty service
throw new Error('NYI');
}
}

View file

@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
export class TerminalEditorIconManager implements IDisposable {
private _styleElement: HTMLStyleElement | undefined;
constructor(
@IConfigurationService _configurationService: IConfigurationService,
@ITerminalService private readonly _terminalService: ITerminalService,
) {
_configurationService.onDidChangeConfiguration(async e => {
if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + _terminalService.getPlatformKey()) ||
e.affectsConfiguration(TerminalSettingPrefix.Profiles + _terminalService.getPlatformKey())) {
this.updateStyleSheet();
}
});
}
dispose() {
this._styleElement?.remove();
this._styleElement = undefined;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
// private get styleElement(): HTMLStyleElement {
// if (!this._styleElement) {
// this._styleElement = dom.createStyleSheet();
// this._styleElement.className = 'terminal-editor-icon';
// }
// return this._styleElement;
// }
async updateStyleSheet() {
// const cssRules: string[] = [];
// await this._lifecycleService.when(LifecyclePhase.Starting);
for (const instance of this._terminalService.terminalInstances) {
const icon = instance.icon;
if (!icon) {
continue;
}
// const cssRules: string[] = [];
// if (icon instanceof Codicon) {
// const terminalSelector = dom.$(`.tab .${instance.title}-name-file-icon`);
// console.log(`${terminalSelector}`);
// cssRules.push(`.monaco-workbench ${terminalSelector} ${icon.classNames} { content: ""; }`);
}
// const iconClasses = getUriClasses(instance, this._themeService.getColorTheme().type);
// let uri = undefined;
// if (icon instanceof URI) {
// uri = icon;
// } else if (icon instanceof Object && 'light' in icon && 'dark' in icon) {
// uri = this._themeService.getColorTheme().type === ColorScheme.LIGHT ? icon.light : icon.dark;
// }
// if (uri instanceof URI && iconClasses && iconClasses.length > 1) {
// const terminalSelector = `.${instance.title}-name-file-icon::before`;
// console.log(terminalSelector);
// try {
// cssRules.push(`.monaco-workbench .${iconClasses[0]} ${terminalSelector} { content: ""; background-image: ${dom.asCSSUrl(uri)}; }`);
// } catch {
// // noop
// }
// }
}
// this.styleElement.textContent = cssRules.join('\n');
// }
}

View file

@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
export class TerminalEditorInput extends EditorInput {
static readonly ID = 'workbench.editors.terminal';
override get typeId(): string {
return TerminalEditorInput.ID;
}
private readonly _terminalInstance: ITerminalInstance;
get terminalInstance(): ITerminalInstance {
return this._terminalInstance;
}
get resource(): URI {
return this.terminalInstance.resource;
}
constructor(
terminalInstance: ITerminalInstance
) {
super();
this._terminalInstance = terminalInstance;
this._terminalInstance.onTitleChanged(() => this._onDidChangeLabel.fire());
}
override getName() {
return this.terminalInstance.title;
}
override dispose() {
this.terminalInstance.dispose();
super.dispose();
}
}

View file

@ -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);
}
}

View file

@ -639,6 +639,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._container.appendChild(this._wrapperElement);
}
detachFromElement(): void {
this._wrapperElement?.parentNode?.removeChild(this._wrapperElement);
this._wrapperElement = undefined;
this._container = undefined;
}
attachToElement(container: HTMLElement): Promise<void> | void {
// The container did not change, do nothing
if (this._container === container) {

View file

@ -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';
@ -42,6 +42,7 @@ 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 { TerminalEditorIconManager } from 'vs/workbench/contrib/terminal/browser/terminalEditorIconManager';
export class TerminalService implements ITerminalService {
declare _serviceBrand: undefined;
@ -73,6 +74,8 @@ export class TerminalService implements ITerminalService {
private _editable: { instance: ITerminalInstance, data: IEditableData } | undefined;
private _iconManager: TerminalEditorIconManager;
public get activeGroupIndex(): number { return this._activeGroupIndex; }
public get terminalGroups(): ITerminalGroup[] { return this._terminalGroups; }
public get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); }
@ -147,6 +150,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
@ -163,6 +167,10 @@ export class TerminalService implements ITerminalService {
this._terminalAltBufferActiveContextKey = KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE.bindTo(this._contextKeyService);
this._configHelper = _instantiationService.createInstance(TerminalConfigHelper);
this._iconManager = this._instantiationService.createInstance(TerminalEditorIconManager);
this.onInstanceIconChanged(() => this._iconManager.updateStyleSheet());
this.onInstanceCreated(() => this._iconManager.updateStyleSheet());
// the below avoids having to poll routinely.
// we update detected profiles when an instance is created so that,
// for example, we detect if you've installed a pwsh
@ -180,8 +188,8 @@ export class TerminalService implements ITerminalService {
lifecycleService.onWillShutdown(e => this._onWillShutdown(e));
this._configurationService.onDidChangeConfiguration(async e => {
if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + this._getPlatformKey()) ||
e.affectsConfiguration(TerminalSettingPrefix.Profiles + this._getPlatformKey()) ||
if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + this.getPlatformKey()) ||
e.affectsConfiguration(TerminalSettingPrefix.Profiles + this.getPlatformKey()) ||
e.affectsConfiguration(TerminalSettingId.UseWslProfiles)) {
this._refreshAvailableProfiles();
}
@ -277,7 +285,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;
@ -377,7 +387,7 @@ export class TerminalService implements ITerminalService {
if (!this._primaryOffProcessTerminalService) {
return this._availableProfiles || [];
}
const platform = await this._getPlatformKey();
const platform = await this.getPlatformKey();
return this._primaryOffProcessTerminalService?.getProfiles(this._configurationService.getValue(`${TerminalSettingPrefix.Profiles}${platform}`), this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${platform}`), includeDetectedProfiles);
}
@ -759,6 +769,15 @@ export class TerminalService implements ITerminalService {
targetGroup.moveInstance(source, index);
}
moveToEditor(source: ITerminalInstance): void {
const sourceGroup = this.getGroupForInstance(source);
if (!sourceGroup) {
return;
}
sourceGroup.removeInstance(source);
this._terminalEditorService.createEditor(source);
}
protected _initInstanceListeners(instance: ITerminalInstance): void {
instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed));
instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged));
@ -899,7 +918,7 @@ export class TerminalService implements ITerminalService {
private async _getPlatformKey(): Promise<string> {
async getPlatformKey(): Promise<string> {
const env = await this._remoteAgentService.getEnvironment();
if (env) {
return env.os === OperatingSystem.Windows ? 'windows' : (env.os === OperatingSystem.Macintosh ? 'osx' : 'linux');
@ -910,7 +929,7 @@ export class TerminalService implements ITerminalService {
async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise<ITerminalInstance | undefined> {
let keyMods: IKeyMods | undefined;
const profiles = await this._detectProfiles(true);
const platformKey = await this._getPlatformKey();
const platformKey = await this.getPlatformKey();
const options: IPickOptions<IProfileQuickPickItem> = {
placeHolder: type === 'createInstance' ? nls.localize('terminal.integrated.selectProfileToCreate', "Select the terminal profile to create") : nls.localize('terminal.integrated.chooseDefaultProfile', "Select your default terminal profile"),
@ -988,7 +1007,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 +1119,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) {
@ -1131,17 +1151,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

View file

@ -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<ITerminalInstance> {
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);

View file

@ -397,6 +397,7 @@ export const enum TerminalCommandId {
Relaunch = 'workbench.action.terminal.relaunch',
FocusPreviousPane = 'workbench.action.terminal.focusPreviousPane',
ShowTabs = 'workbench.action.terminal.showTabs',
CreateTerminalEditor = 'workbench.action.createTerminalEditor',
FocusTabs = 'workbench.action.terminal.focusTabs',
FocusNextPane = 'workbench.action.terminal.focusNextPane',
ResizePaneLeft = 'workbench.action.terminal.resizePaneLeft',
@ -448,7 +449,8 @@ export const enum TerminalCommandId {
ShowEnvironmentInformation = 'workbench.action.terminal.showEnvironmentInformation',
SearchWorkspace = 'workbench.action.terminal.searchWorkspace',
AttachToRemoteTerminal = 'workbench.action.terminal.attachToSession',
DetachProcess = 'workbench.action.terminal.detachProcess'
DetachProcess = 'workbench.action.terminal.detachProcess',
MoveToEditor = 'workbench.action.terminal.moveToEditor'
}
export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [

View file

@ -17,6 +17,7 @@ export function formatMessageForTerminal(message: string, excludeLeadingNewLine:
* An object holding strings shared by multiple parts of the terminal
*/
export const terminalStrings = {
terminal: localize('terminal.editor.input', "Terminal"),
focus: {
value: localize('workbench.action.terminal.focus', "Focus Terminal"),
original: 'Focus Terminal'

View file

@ -44,8 +44,8 @@ export class LocalPty extends Disposable implements ITerminalChildProcess {
start(): Promise<ITerminalLaunchError | undefined> {
return this._localPtyService.start(this.id);
}
detach(): void {
this._localPtyService.detachFromProcess(this.id);
detach(): Promise<void> {
return this._localPtyService.detachFromProcess(this.id);
}
shutdown(immediate: boolean): void {
this._localPtyService.shutdown(this.id, immediate);

View file

@ -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);
}