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

Add terminal location API #131028

Merged
merged 29 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a869f86
start #45407
meganrogge Aug 16, 2021
feb96c2
add editorOptions
meganrogge Aug 16, 2021
dc03c75
works for the basic case
meganrogge Aug 16, 2021
d52b5f3
parentTerminal is a real terminal instance
meganrogge Aug 16, 2021
cf10cde
very ugly, but it works
meganrogge Aug 16, 2021
aa458e0
clean up
meganrogge Aug 17, 2021
0142818
get rid of target
meganrogge Aug 17, 2021
1ce1f73
clean up
meganrogge Aug 17, 2021
b48448d
clean up
meganrogge Aug 17, 2021
fdc5cc2
remove some lines
meganrogge Aug 17, 2021
29276e8
Merge branch 'main' into merogge/split-api
meganrogge Aug 17, 2021
d79f5be
Merge branch 'main' into merogge/split-api
meganrogge Aug 17, 2021
0bc2f1f
Update src/vs/workbench/api/common/extHostTerminalService.ts
meganrogge Aug 17, 2021
0e36ae1
Update src/vs/workbench/api/browser/mainThreadTerminalService.ts
meganrogge Aug 17, 2021
f46876b
get rid of isSplitTerminal and forceSplit
meganrogge Aug 17, 2021
6593e3a
remove isSplitTerminal and target from profile ocreation options
meganrogge Aug 18, 2021
e4f9a07
bring back defaultLocation setting strings
meganrogge Aug 18, 2021
b6046df
splitting is broken with parentTerminal unable to be resolved in main…
meganrogge Aug 18, 2021
8dce8c2
delete console.logs
meganrogge Aug 18, 2021
d28dffd
clean up
meganrogge Aug 18, 2021
163bfe6
add todo
meganrogge Aug 18, 2021
dfa3fee
Merge branch 'main' into merogge/split-api
meganrogge Aug 19, 2021
b62b4de
use ExtHostTerminalIdentifier instead of ExtHostTerminal
meganrogge Aug 19, 2021
1e2febc
Update src/vs/workbench/contrib/terminal/browser/terminalService.ts
meganrogge Aug 19, 2021
00f6275
clean up
meganrogge Aug 19, 2021
d55713e
use instance id for terminal profiles
meganrogge Aug 19, 2021
3cee073
Merge branch 'main' into merogge/split-api
meganrogge Aug 19, 2021
21fd2ae
Merge branch 'main' into merogge/split-api
meganrogge Aug 20, 2021
25692e4
get profiles to work with splits
meganrogge Aug 20, 2021
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
10 changes: 7 additions & 3 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,13 +392,17 @@ export interface IShellLaunchConfig {
}

export interface ICreateContributedTerminalProfileOptions {
target?: TerminalLocation;
icon?: URI | string | { light: URI, dark: URI };
color?: string;
isSplitTerminal?: boolean;
splitActiveTerminal?: boolean;
}

export const enum TerminalLocation {
export enum TerminalLocation {
Panel = 0,
Editor = 1
}

export const enum TerminalLocationString {
TerminalView = 'view',
Editor = 'editor'
}
Expand Down
50 changes: 50 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,56 @@ declare module 'vscode' {
readonly state: TerminalState;
}

export interface TerminalOptions {
location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
}

export interface ExtensionTerminalOptions {
location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
}

export enum TerminalLocation {
Panel = 0,
Editor = 1,
}

export interface TerminalEditorLocationOptions {
/**
* A view column in which the {@link Terminal terminal} should be shown in the editor area.
* Use {@link ViewColumn.Active active} to open in the active editor group, other values are
* adjusted to be `Min(column, columnCount + 1)`, the
* {@link ViewColumn.Active active}-column is not adjusted. Use
* {@linkcode ViewColumn.Beside} to open the editor to the side of the currently active one.
*/
viewColumn: ViewColumn;
/**
* An optional flag that when `true` will stop the {@link Terminal} from taking focus.
*/
preserveFocus?: boolean;
}

export interface TerminalSplitLocationOptions {
/**
* The parent terminal to split this terminal beside. This works whether the parent terminal
* is in the panel or the editor area.
*/
parentTerminal: Terminal;
}

/**
* An event representing a change in a {@link Terminal.state terminal's state}.
*/
export interface TerminalStateChangeEvent {
/**
* The {@link Terminal} this event occurred on.
*/
readonly terminal: Terminal;
/**
* The {@link Terminal.state current state} of the {@link Terminal}.
*/
readonly state: TerminalState;
}

export namespace window {
/**
* An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed.
Expand Down
40 changes: 20 additions & 20 deletions src/vs/workbench/api/browser/mainThreadTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, TerminalLaunchConfig, ITerminalDimensionsDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, TerminalLaunchConfig, ITerminalDimensionsDto, ExtHostTerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { StopWatch } from 'vs/base/common/stopwatch';
Expand All @@ -20,6 +20,7 @@ import { IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, ITerminal
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { OperatingSystem, OS } from 'vs/base/common/platform';
import { TerminalEditorLocationOptions } from 'vscode';

@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
Expand Down Expand Up @@ -110,7 +111,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptDefaultProfile(...await Promise.all([defaultProfile, defaultAutomationProfile]));
}

private async _getTerminalInstance(id: TerminalIdentifier): Promise<ITerminalInstance | undefined> {
private async _getTerminalInstance(id: ExtHostTerminalIdentifier): Promise<ITerminalInstance | undefined> {
if (typeof id === 'string') {
return this._extHostTerminals.get(id);
}
Expand All @@ -137,27 +138,26 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
extHostTerminalId,
isFeatureTerminal: launchConfig.isFeatureTerminal,
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal,
useShellEnvironment: launchConfig.useShellEnvironment
useShellEnvironment: launchConfig.useShellEnvironment,
};
this._extHostTerminals.set(extHostTerminalId, new Promise(async r => {
let terminal: ITerminalInstance | undefined;
if (launchConfig.isSplitTerminal) {
const activeInstance = this._terminalService.getInstanceHost(launchConfig.target).activeInstance;
if (activeInstance) {
terminal = withNullAsUndefined(await this._terminalService.createTerminal({ instanceToSplit: activeInstance, config: shellLaunchConfig }));
}
}
if (!terminal) {
terminal = await this._terminalService.createTerminal({
config: shellLaunchConfig,
target: launchConfig.target
});
}
const terminal = await this._terminalService.createTerminal({
config: shellLaunchConfig,
location: await this._deserializeParentTerminal(launchConfig.location)
});
r(terminal);
}));
}

public async $show(id: TerminalIdentifier, preserveFocus: boolean): Promise<void> {
private async _deserializeParentTerminal(location?: TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: ExtHostTerminalIdentifier } | { splitActiveTerminal: boolean }): Promise<TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: ITerminalInstance } | { splitActiveTerminal: boolean } | undefined> {
if (typeof location === 'object' && 'parentTerminal' in location) {
const parentTerminal = await this._extHostTerminals.get(location.parentTerminal.toString());
return parentTerminal ? { parentTerminal } : undefined;
}
return location;
}

public async $show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): Promise<void> {
const terminalInstance = await this._getTerminalInstance(id);
if (terminalInstance) {
this._terminalService.setActiveInstance(terminalInstance);
Expand All @@ -169,19 +169,19 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}

public async $hide(id: TerminalIdentifier): Promise<void> {
public async $hide(id: ExtHostTerminalIdentifier): Promise<void> {
const instanceToHide = await this._getTerminalInstance(id);
const activeInstance = this._terminalService.activeInstance;
if (activeInstance && activeInstance.instanceId === instanceToHide?.instanceId && activeInstance.target !== TerminalLocation.Editor) {
this._terminalGroupService.hidePanel();
}
}

public async $dispose(id: TerminalIdentifier): Promise<void> {
public async $dispose(id: ExtHostTerminalIdentifier): Promise<void> {
(await this._getTerminalInstance(id))?.dispose();
}

public async $sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): Promise<void> {
public async $sendText(id: ExtHostTerminalIdentifier, text: string, addNewLine: boolean): Promise<void> {
const instance = await this._getTerminalInstance(id);
await instance?.sendText(text, addNewLine);
}
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
if ('location' in nameOrOptions) {
checkProposedApiEnabled(extension);
}
if ('pty' in nameOrOptions) {
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
}
Expand Down Expand Up @@ -1234,6 +1237,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskScope: extHostTypes.TaskScope,
TerminalLink: extHostTypes.TerminalLink,
TerminalLocation: extHostTypes.TerminalLocation,
TerminalProfile: extHostTypes.TerminalProfile,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
TextEdit: extHostTypes.TextEdit,
Expand Down
13 changes: 6 additions & 7 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ export interface MainThreadProgressShape extends IDisposable {
* All other terminals (that are not created on the extension host side) always
* use the numeric id.
*/
export type TerminalIdentifier = number | string;
export type ExtHostTerminalIdentifier = number | string;

export interface TerminalLaunchConfig {
name?: string;
Expand All @@ -485,16 +485,15 @@ export interface TerminalLaunchConfig {
isFeatureTerminal?: boolean;
isExtensionOwnedTerminal?: boolean;
useShellEnvironment?: boolean;
isSplitTerminal?: boolean;
target?: TerminalLocation;
location?: TerminalLocation | { viewColumn: number, preserveFocus?: boolean } | { parentTerminal: ExtHostTerminalIdentifier } | { splitActiveTerminal: boolean };
}

export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(extHostTerminalId: string, config: TerminalLaunchConfig): Promise<void>;
$dispose(id: TerminalIdentifier): void;
$hide(id: TerminalIdentifier): void;
$sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): void;
$show(id: TerminalIdentifier, preserveFocus: boolean): void;
$dispose(id: ExtHostTerminalIdentifier): void;
$hide(id: ExtHostTerminalIdentifier): void;
$sendText(id: ExtHostTerminalIdentifier, text: string, addNewLine: boolean): void;
$show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): void;
$startSendingDataEvents(): void;
$stopSendingDataEvents(): void;
$startLinkProvider(): void;
Expand Down
38 changes: 27 additions & 11 deletions src/vs/workbench/api/common/extHostTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, ExtHostTerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
Expand Down Expand Up @@ -51,8 +51,8 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
export interface ITerminalInternalOptions {
isFeatureTerminal?: boolean;
useShellEnvironment?: boolean;
isSplitTerminal?: boolean;
target?: TerminalLocation;
resolvedExtHostIdentifier?: ExtHostTerminalIdentifier;
splitActiveTerminal?: boolean;
}

export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
Expand All @@ -72,7 +72,7 @@ export class ExtHostTerminal {

constructor(
private _proxy: MainThreadTerminalServiceShape,
public _id: TerminalIdentifier,
public _id: ExtHostTerminalIdentifier,
private readonly _creationOptions: vscode.TerminalOptions | vscode.ExtensionTerminalOptions,
private _name?: string,
) {
Expand Down Expand Up @@ -147,12 +147,12 @@ export class ExtHostTerminal {
isFeatureTerminal: withNullAsUndefined(internalOptions?.isFeatureTerminal),
isExtensionOwnedTerminal: true,
useShellEnvironment: withNullAsUndefined(internalOptions?.useShellEnvironment),
isSplitTerminal: internalOptions?.isSplitTerminal,
target: internalOptions?.target
location: this._serializeParentTerminal(options.location, internalOptions?.resolvedExtHostIdentifier, internalOptions?.splitActiveTerminal)
});
}

public async createExtensionTerminal(isSplitTerminal?: boolean, target?: TerminalLocation, iconPath?: TerminalIcon, color?: ThemeColor): Promise<number> {

public async createExtensionTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, parentTerminal?: ExtHostTerminalIdentifier, iconPath?: TerminalIcon, color?: ThemeColor): Promise<number> {
if (typeof this._id !== 'string') {
throw new Error('Terminal has already been created');
}
Expand All @@ -161,8 +161,7 @@ export class ExtHostTerminal {
isExtensionCustomPtyTerminal: true,
icon: iconPath,
color: ThemeColor.isThemeColor(color) ? color.id : undefined,
isSplitTerminal,
target
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
location: this._serializeParentTerminal(location, parentTerminal)
});
// At this point, the id has been set via `$acceptTerminalOpened`
if (typeof this._id === 'string') {
Expand All @@ -171,6 +170,15 @@ export class ExtHostTerminal {
return this._id;
}

private _serializeParentTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, parentTerminal?: ExtHostTerminalIdentifier, splitActiveTerminal?: boolean): TerminalLocation | vscode.TerminalEditorLocationOptions | { parentTerminal: ExtHostTerminalIdentifier } | { splitActiveTerminal: boolean } | undefined {
if (typeof location === 'object' && 'parentTerminal' in location) {
return parentTerminal ? { parentTerminal } : undefined;
} else if (splitActiveTerminal) {
return { splitActiveTerminal: true };
}
return location;
}

private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
Expand Down Expand Up @@ -393,14 +401,21 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
const p = new ExtHostPseudoterminal(options.pty);
terminal.createExtensionTerminal(internalOptions?.isSplitTerminal, internalOptions?.target, asTerminalIcon(options.iconPath), asTerminalColor(options.color)).then(id => {
terminal.createExtensionTerminal(this._resolveLocation(options.location), internalOptions?.resolvedExtHostIdentifier, asTerminalIcon(options.iconPath), asTerminalColor(options.color)).then(id => {
const disposable = this._setupExtHostProcessListeners(id, p);
this._terminalProcessDisposables[id] = disposable;
});
this._terminals.push(terminal);
return terminal.value;
}

private _resolveLocation(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions): undefined | TerminalLocation | vscode.TerminalEditorLocationOptions {
if (typeof location === 'object' && 'parentTerminal' in location) {
return undefined;
}
return location;
}

public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void {
const terminal = this._getTerminalById(id);
if (!terminal) {
Expand Down Expand Up @@ -636,6 +651,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
if (!profile || !('options' in profile)) {
throw new Error(`No terminal profile options provided for id "${id}"`);
}

if ('pty' in profile.options) {
this.createExtensionTerminal(profile.options, options);
return;
Expand Down Expand Up @@ -739,7 +755,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
return index !== null ? array[index] : null;
}

private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: TerminalIdentifier): number | null {
private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: ExtHostTerminalIdentifier): number | null {
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/api/common/extHostTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,11 @@ export class TerminalLink implements vscode.TerminalLink {
}
}

export enum TerminalLocation {
Panel = 0,
Editor = 1,
}

export class TerminalProfile implements vscode.TerminalProfile {
constructor(
public options: vscode.TerminalOptions | vscode.ExtensionTerminalOptions
Expand Down
16 changes: 15 additions & 1 deletion src/vs/workbench/api/node/extHostTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
public createTerminalFromOptions(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
this._terminals.push(terminal);
terminal.create(options, internalOptions);
terminal.create(options, this._serializeParentTerminal(options, internalOptions));
return terminal.value;
}

private _serializeParentTerminal(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): ITerminalInternalOptions {
internalOptions = internalOptions ? internalOptions : {};
if (options.location && typeof options.location === 'object' && 'parentTerminal' in options.location) {
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
const parentTerminal = options.location.parentTerminal;
if (parentTerminal) {
const parentExtHostTerminal = this._terminals.find(t => t.value === parentTerminal);
if (parentExtHostTerminal) {
internalOptions.resolvedExtHostIdentifier = parentExtHostTerminal._id;
}
}
}
return internalOptions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
if (terminal.group === group) {
const originalInstance = terminal.terminal;
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
await originalInstance.waitForTitle();
result = await this.terminalService.createTerminal({ instanceToSplit: originalInstance, config: launchConfigs });
result = await this.terminalService.createTerminal({ location: { parentTerminal: originalInstance }, config: launchConfigs });
if (result) {
break;
}
Expand Down
Loading