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

vscode: Support location in TerminalOptions #54

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export interface TerminalServiceMain {
* Create new Terminal with Terminal options.
* @param options - object with parameters to create new terminal.
*/
$createTerminal(id: string, options: theia.TerminalOptions, isPseudoTerminal?: boolean): Promise<string>;
$createTerminal(id: string, options: theia.TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise<string>;

/**
* Send text to the terminal by id.
Expand Down
24 changes: 21 additions & 3 deletions packages/plugin-ext/src/main/browser/terminal-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

import { interfaces } from '@theia/core/shared/inversify';
import { ApplicationShell, WidgetOpenerOptions } from '@theia/core/lib/browser';
import { TerminalOptions } from '@theia/plugin';
import { CancellationToken } from '@theia/core/shared/vscode-languageserver-protocol';
import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalEditorLocationOptions, TerminalOptions } from '@theia/plugin';
import { TerminalLocation, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
import { TerminalServiceMain, TerminalServiceExt, MAIN_RPC_CONTEXT } from '../../common/plugin-api-rpc';
import { RPCProtocol } from '../../common/rpc-protocol';
Expand Down Expand Up @@ -122,7 +122,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
terminal.resize(cols, rows);
}

async $createTerminal(id: string, options: TerminalOptions, isPseudoTerminal?: boolean): Promise<string> {
async $createTerminal(id: string, options: TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise<string> {
try {
const terminal = await this.terminals.newTerminal({
id,
Expand All @@ -136,6 +136,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
useServerTitle: false,
attributes: options.attributes,
hideFromUser: options.hideFromUser,
location: this.getTerminalLocation(options, parentId),
isPseudoTerminal
});
if (options.message) {
Expand All @@ -148,6 +149,23 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
}
}

protected getTerminalLocation(options: TerminalOptions, parentId?: string): TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: string; } | undefined {
if (typeof options.location === 'number' && Object.values(TerminalLocation).includes(options.location)) {
return options.location;
} else if (options.location && typeof options.location === 'object') {
if ('parentTerminal' in options.location) {
if (!parentId) {
throw new Error('parentTerminal is set but no parentId is provided');
}
return { 'parentTerminal': parentId };
} else {
return options.location;
}
}

return undefined;
}

$sendText(id: string, text: string, addNewLine?: boolean): void {
const terminal = this.terminals.getById(id);
if (terminal) {
Expand Down
4 changes: 3 additions & 1 deletion packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ import {
TextDocumentChangeReason,
InputBoxValidationSeverity,
TerminalLink,
TerminalLocation,
InlayHint,
InlayHintKind,
InlayHintLabelPart,
Expand Down Expand Up @@ -1135,7 +1136,8 @@ export function createAPIFactory(
ExtensionKind,
InlineCompletionItem,
InlineCompletionList,
InlineCompletionTriggerKind
InlineCompletionTriggerKind,
TerminalLocation
};
};
}
Expand Down
17 changes: 16 additions & 1 deletion packages/plugin-ext/src/plugin/terminal-ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,22 @@ export class TerminalServiceExtImpl implements TerminalServiceExt {
shellArgs: shellArgs
};
}
this.proxy.$createTerminal(id, options, !!pseudoTerminal);

let parentId;

if (options.location && typeof options.location === 'object' && 'parentTerminal' in options.location) {
const parentTerminal = options.location.parentTerminal;
if (parentTerminal instanceof TerminalExtImpl) {
for (const [k, v] of this._terminals) {
if (v === parentTerminal) {
parentId = k;
break;
}
}
}
}

this.proxy.$createTerminal(id, options, parentId, !!pseudoTerminal);

let creationOptions: theia.TerminalOptions | theia.ExtensionTerminalOptions = options;
// make sure to pass ExtensionTerminalOptions as creation options
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,11 @@ export class TerminalLink {
}
}

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

@es5ClassCompat
export class FileDecoration {

Expand Down
54 changes: 54 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3001,6 +3001,11 @@ export module '@theia/plugin' {
*/
message?: string;

/**
* The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal.
*/
location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;

/**
* Terminal attributes. Can be useful to apply some implementation specific information.
*/
Expand Down Expand Up @@ -3067,6 +3072,11 @@ export module '@theia/plugin' {
* control it.
*/
pty: Pseudoterminal;

/**
* The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal.
*/
location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
}

/**
Expand Down Expand Up @@ -3207,6 +3217,50 @@ export module '@theia/plugin' {
constructor(startIndex: number, length: number, tooltip?: string);
}

/**
* The location of the {@link Terminal}.
*/
export enum TerminalLocation {
/**
* In the terminal view
*/
Panel = 1,
/**
* In the editor area
*/
Editor = 2,
}

/**
* Assumes a {@link TerminalLocation} of editor and allows specifying a {@link ViewColumn} and
* {@link TerminalEditorLocationOptions.preserveFocus preserveFocus } property
*/
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;
}

/**
* Uses the parent {@link Terminal}'s location for the terminal
*/
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;
}

/**
* A file decoration represents metadata that can be rendered with a file.
*/
Expand Down
73 changes: 72 additions & 1 deletion packages/terminal/src/browser/base/terminal-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,79 @@ export interface TerminalExitStatus {
readonly code: number | undefined;
}

export type TerminalLocationOptions = TerminalLocation | TerminalEditorLocation | TerminalSplitLocation;

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

export interface TerminalEditorLocation {
readonly viewColumn: number;
readonly preserveFocus?: boolean;
}

export interface TerminalSplitLocation {
readonly parentTerminal: string;
}

export enum ViewColumn {
/**
* A *symbolic* editor column representing the currently active column. This value
* can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value
* of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Active`.
*/
Active = -1,
/**
* A *symbolic* editor column representing the column to the side of the active one. This value
* can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value
* of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Beside`.
*/
Beside = -2,
/**
* The first editor column.
*/
One = 1,
/**
* The second editor column.
*/
Two = 2,
/**
* The third editor column.
*/
Three = 3,
/**
* The fourth editor column.
*/
Four = 4,
/**
* The fifth editor column.
*/
Five = 5,
/**
* The sixth editor column.
*/
Six = 6,
/**
* The seventh editor column.
*/
Seven = 7,
/**
* The eighth editor column.
*/
Eight = 8,
/**
* The ninth editor column.
*/
Nine = 9
}

/**
* Terminal UI widget.
*/
export abstract class TerminalWidget extends BaseWidget {

abstract processId: Promise<number>;

/**
* Get the current executable and arguments.
*/
Expand All @@ -54,6 +120,9 @@ export abstract class TerminalWidget extends BaseWidget {
/** Terminal widget can be hidden from users until explicitly shown once. */
abstract readonly hiddenFromUser: boolean;

/** The position of the terminal widget. */
abstract readonly location: TerminalLocationOptions;

/** The last CWD assigned to the terminal, useful when attempting getCwdURI on a task terminal fails */
lastCwd: URI;

Expand Down Expand Up @@ -211,4 +280,6 @@ export interface TerminalWidgetOptions {
* When enabled the terminal will run the process as normal but not be surfaced to the user until `Terminal.show` is called.
*/
readonly hideFromUser?: boolean;

readonly location?: TerminalLocationOptions;
}
40 changes: 33 additions & 7 deletions packages/terminal/src/browser/terminal-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/li
import { TERMINAL_WIDGET_FACTORY_ID, TerminalWidgetFactoryOptions, TerminalWidgetImpl } from './terminal-widget-impl';
import { TerminalKeybindingContexts } from './terminal-keybinding-contexts';
import { TerminalService } from './base/terminal-service';
import { TerminalWidgetOptions, TerminalWidget } from './base/terminal-widget';
import { TerminalWidgetOptions, TerminalWidget, TerminalLocation, ViewColumn } from './base/terminal-widget';
import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import { ShellTerminalServerProxy } from '../common/shell-terminal-protocol';
import URI from '@theia/core/lib/common/uri';
Expand Down Expand Up @@ -644,20 +644,46 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu

// TODO: reuse WidgetOpenHandler.open
open(widget: TerminalWidget, options?: WidgetOpenerOptions): void {
const area = widget.location === TerminalLocation.Editor ? 'main' : 'bottom';
const widgetOptions: ApplicationShell.WidgetOptions = { area: area, ...options?.widgetOptions };
let preserveFocus = false;

if (typeof widget.location === 'object') {
if ('parentTerminal' in widget.location) {
widgetOptions.ref = this.getById(widget.location.parentTerminal);
widgetOptions.mode = 'split-right';
} else if ('viewColumn' in widget.location) {
preserveFocus = widget.location.preserveFocus ?? false;
switch (widget.location.viewColumn) {
case ViewColumn.Active:
widgetOptions.ref = this.shell.currentWidget;
widgetOptions.mode = 'tab-after';
break;
case ViewColumn.Beside:
widgetOptions.ref = this.shell.currentWidget;
widgetOptions.mode = 'split-right';
break;
default:
widgetOptions.area = 'main';
const mainAreaTerminals = this.shell.getWidgets('main').filter(w => w instanceof TerminalWidget && w.isVisible);
const column = Math.min(widget.location.viewColumn, mainAreaTerminals.length);
widgetOptions.mode = widget.location.viewColumn <= mainAreaTerminals.length ? 'split-left' : 'split-right';
widgetOptions.ref = mainAreaTerminals[column - 1];
}
}
}

const op: WidgetOpenerOptions = {
mode: 'activate',
...options,
widgetOptions: {
area: 'bottom',
...(options && options.widgetOptions)
}
widgetOptions: widgetOptions
};
if (!widget.isAttached) {
this.shell.addWidget(widget, op.widgetOptions);
}
if (op.mode === 'activate') {
if (op.mode === 'activate' && !preserveFocus) {
this.shell.activateWidget(widget.id);
} else if (op.mode === 'reveal') {
} else if (op.mode === 'reveal' || preserveFocus) {
this.shell.revealWidget(widget.id);
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/terminal/src/browser/terminal-widget-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { ShellTerminalServerProxy, IShellTerminalPreferences } from '../common/s
import { terminalsPath } from '../common/terminal-protocol';
import { IBaseTerminalServer, TerminalProcessInfo } from '../common/base-terminal-protocol';
import { TerminalWatcher } from '../common/terminal-watcher';
import { TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus } from './base/terminal-widget';
import { TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus, TerminalLocationOptions, TerminalLocation } from './base/terminal-widget';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { TerminalPreferences, TerminalRendererType, isTerminalRendererType, DEFAULT_TERMINAL_RENDERER_TYPE, CursorStyle } from './terminal-preferences';
import URI from '@theia/core/lib/common/uri';
Expand Down Expand Up @@ -53,6 +53,7 @@ export interface TerminalContribution {
export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget {
readonly isExtractable: boolean = true;
secondaryWindow: Window | undefined;
location: TerminalLocationOptions;

static LABEL = nls.localizeByDefault('Terminal');

Expand Down Expand Up @@ -128,6 +129,8 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
));
}

this.location = this.options.location || TerminalLocation.Panel;

this.title.closable = true;
this.addClass('terminal-container');

Expand Down