未验证 提交 130499db 编写于 作者: D Daniel Imms 提交者: GitHub

Merge pull request #78529 from microsoft/tyriar/69865_remove_renderers

Remove terminal renderers
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { window, Terminal, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget } from 'vscode';
import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget } from 'vscode';
import { doesNotThrow, equal, ok, deepEqual } from 'assert';
suite('window namespace tests', () => {
......@@ -81,24 +81,6 @@ suite('window namespace tests', () => {
});
const terminal = window.createTerminal('b');
});
test('Terminal.sendText should fire Terminal.onInput', (done) => {
const reg1 = window.onDidOpenTerminal(terminal => {
reg1.dispose();
const reg2 = renderer.onDidAcceptInput(data => {
equal(data, 'bar');
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {
reg3.dispose();
done();
});
terminal.dispose();
});
terminal.sendText('bar', false);
});
const renderer = window.createTerminalRenderer('foo');
});
// test('onDidChangeActiveTerminal should fire when new terminals are created', (done) => {
// const reg1 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => {
// equal(active, terminal);
......@@ -246,59 +228,7 @@ suite('window namespace tests', () => {
});
});
suite('Terminal renderers (deprecated)', () => {
test('should fire onDidOpenTerminal and onDidCloseTerminal from createTerminalRenderer terminal', (done) => {
const reg1 = window.onDidOpenTerminal(term => {
equal(term.name, 'c');
reg1.dispose();
const reg2 = window.onDidCloseTerminal(() => {
reg2.dispose();
done();
});
term.dispose();
});
window.createTerminalRenderer('c');
});
test('should get maximum dimensions set when shown', (done) => {
let terminal: Terminal;
const reg1 = window.onDidOpenTerminal(term => {
reg1.dispose();
term.show();
terminal = term;
});
const renderer = window.createTerminalRenderer('foo');
const reg2 = renderer.onDidChangeMaximumDimensions(dimensions => {
ok(dimensions.columns > 0);
ok(dimensions.rows > 0);
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {
reg3.dispose();
done();
});
terminal.dispose();
});
});
test('should fire Terminal.onData on write', (done) => {
const reg1 = window.onDidOpenTerminal(terminal => {
reg1.dispose();
const reg2 = terminal.onDidWriteData(data => {
equal(data, 'bar');
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {
reg3.dispose();
done();
});
terminal.dispose();
});
renderer.write('bar');
});
const renderer = window.createTerminalRenderer('foo');
});
});
suite('Virtual process terminals', () => {
suite('Extension pty terminals', () => {
test('should fire onDidOpenTerminal and onDidCloseTerminal', (done) => {
const reg1 = window.onDidOpenTerminal(term => {
equal(term.name, 'c');
......@@ -344,7 +274,31 @@ suite('window namespace tests', () => {
const terminal = window.createTerminal({ name: 'foo', pty });
});
test('should fire provide dimensions on start as the terminal has been shown', (done) => {
test('should not provide dimensions on start as the terminal has not been shown yet', (done) => {
const reg1 = window.onDidOpenTerminal(term => {
equal(terminal, term);
reg1.dispose();
});
const pty: Pseudoterminal = {
onDidWrite: new EventEmitter<string>().event,
open: (dimensions) => {
equal(dimensions, undefined);
const reg3 = window.onDidCloseTerminal(() => {
reg3.dispose();
done();
});
// Show a terminal and wait a brief period before dispose, this will cause
// the panel to init it's dimenisons and be provided to following terminals.
// The following test depends on this.
terminal.show();
setTimeout(() => terminal.dispose(), 200);
},
close: () => {}
};
const terminal = window.createTerminal({ name: 'foo', pty });
});
test('should provide dimensions on start as the terminal has been shown', (done) => {
const reg1 = window.onDidOpenTerminal(term => {
equal(terminal, term);
reg1.dispose();
......@@ -352,6 +306,8 @@ suite('window namespace tests', () => {
const pty: Pseudoterminal = {
onDidWrite: new EventEmitter<string>().event,
open: (dimensions) => {
// This test depends on Terminal.show being called some time before such
// that the panel dimensions are initialized and cached.
ok(dimensions!.columns > 0);
ok(dimensions!.rows > 0);
const reg3 = window.onDidCloseTerminal(() => {
......
......@@ -810,134 +810,13 @@ declare module 'vscode' {
readonly rows: number;
}
/**
* Represents a terminal without a process where all interaction and output in the terminal is
* controlled by an extension. This is similar to an output window but has the same VT sequence
* compatibility as the regular terminal.
*
* Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is
* created with all its APIs available for use by extensions. When using the Terminal object
* of a TerminalRenderer it acts just like normal only the extension that created the
* TerminalRenderer essentially acts as a process. For example when an
* [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire
* when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when
* [Terminal.sendText](#Terminal.sendText) is triggered that will fire the
* [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event.
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*
* **Example:** Create a terminal renderer, show it and write hello world in red
* ```typescript
* const renderer = window.createTerminalRenderer('foo');
* renderer.terminal.then(t => t.show());
* renderer.write('\x1b[31mHello world\x1b[0m');
* ```
*/
export interface TerminalRenderer {
/**
* The name of the terminal, this will appear in the terminal selector.
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*/
name: string;
/**
* The dimensions of the terminal, the rows and columns of the terminal can only be set to
* a value smaller than the maximum value, if this is undefined the terminal will auto fit
* to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions).
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*
* **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows
* ```typescript
* terminalRenderer.dimensions = {
* cols: 20,
* rows: 10
* };
* ```
*/
dimensions: TerminalDimensions | undefined;
/**
* The maximum dimensions of the terminal, this will be undefined immediately after a
* terminal renderer is created and also until the terminal becomes visible in the UI.
* Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions)
* to get notified when this value changes.
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*/
readonly maximumDimensions: TerminalDimensions | undefined;
/**
* The corresponding [Terminal](#Terminal) for this TerminalRenderer.
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*/
readonly terminal: Terminal;
/**
* Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends
* text to the underlying _process_, this will write the text to the terminal itself.
*
* @param text The text to write.
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*
* **Example:** Write red text to the terminal
* ```typescript
* terminalRenderer.write('\x1b[31mHello world\x1b[0m');
* ```
*
* **Example:** Move the cursor to the 10th row and 20th column and write an asterisk
* ```typescript
* terminalRenderer.write('\x1b[10;20H*');
* ```
*/
write(text: string): void;
/**
* An event which fires on keystrokes in the terminal or when an extension calls
* [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their
* corresponding VT sequence representation.
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*
* **Example:** Simulate interaction with the terminal from an outside extension or a
* workbench command such as `workbench.action.terminal.runSelectedText`
* ```typescript
* const terminalRenderer = window.createTerminalRenderer('test');
* terminalRenderer.onDidAcceptInput(data => {
* console.log(data); // 'Hello world'
* });
* terminalRenderer.terminal.sendText('Hello world');
* ```
*/
readonly onDidAcceptInput: Event<string>;
/**
* An event which fires when the [maximum dimensions](#TerminalRenderer.maximumDimensions) of
* the terminal renderer change.
*
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*/
readonly onDidChangeMaximumDimensions: Event<TerminalDimensions>;
}
export namespace window {
/**
* Create a [TerminalRenderer](#TerminalRenderer).
*
* @param name The name of the terminal renderer, this shows up in the terminal selector.
* @deprecated Use [ExtensionTerminalOptions](#ExtensionTerminalOptions) instead.
*/
export function createTerminalRenderer(name: string): TerminalRenderer;
}
//#endregion
//#region Extension terminals
export namespace window {
/**
* Creates a [Terminal](#Terminal) where an extension controls the teerminal.
* Creates a [Terminal](#Terminal) where an extension controls the terminal.
*
* @param options An [ExtensionTerminalOptions](#ExtensionTerminalOptions) object describing
* the characteristics of the new terminal.
......@@ -1170,30 +1049,12 @@ declare module 'vscode' {
//#endregion
//#region CustomExecution
/**
* Class used to execute an extension callback as a task.
*/
export class CustomExecution {
/**
* @param callback The callback that will be called when the extension callback task is executed.
*/
constructor(callback: (terminalRenderer: TerminalRenderer, cancellationToken: CancellationToken, thisArg?: any) => Thenable<number>);
/**
* The callback used to execute the task.
* @param terminalRenderer Used by the task to render output and receive input.
* @param cancellationToken Cancellation used to signal a cancel request to the executing task.
* @returns The callback should return '0' for success and a non-zero value for failure.
*/
callback: (terminalRenderer: TerminalRenderer, cancellationToken: CancellationToken, thisArg?: any) => Thenable<number>;
}
/**
* Class used to execute an extension callback as a task.
*/
export class CustomExecution2 {
/**
* @param process The [Pseudotrminal](#Pseudoterminal) to be used by the task to display output.
* @param process The [Pseudoterminal](#Pseudoterminal) to be used by the task to display output.
* @param callback The callback that will be called when the task is started by a user.
*/
constructor(callback: (thisArg?: any) => Thenable<Pseudoterminal>);
......@@ -1222,12 +1083,12 @@ declare module 'vscode' {
* or '$eslint'. Problem matchers can be contributed by an extension using
* the `problemMatchers` extension point.
*/
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]);
/**
* The task's execution engine
*/
execution2?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2;
execution2?: ProcessExecution | ShellExecution | CustomExecution2;
}
//#endregion
......
......@@ -29,7 +29,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecution2DTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
} from 'vs/workbench/api/common/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
......@@ -131,7 +131,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ProcessExecutionDTO {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
......@@ -199,7 +199,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is ShellExecutionDTO {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
......@@ -230,28 +230,8 @@ namespace ShellExecutionDTO {
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecutionDTO {
const candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
}
export function from(value: CommandConfiguration): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
export function to(value: CustomExecutionDTO): CommandConfiguration {
return {
runtime: RuntimeType.CustomExecution,
presentation: undefined
};
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO): value is CustomExecution2DTO {
const candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
}
......@@ -371,8 +351,6 @@ namespace TaskDTO {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
} else if (CustomExecutionDTO.is(task.execution)) {
command = CustomExecutionDTO.to(task.execution);
} else if (CustomExecution2DTO.is(task.execution)) {
command = CustomExecution2DTO.to(task.execution);
}
......
......@@ -22,7 +22,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
private readonly _terminalOnDidWriteDataListeners = new Map<number, IDisposable>();
private readonly _terminalOnDidAcceptInputListeners = new Map<number, IDisposable>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
constructor(
......@@ -103,11 +102,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
});
}
public $createTerminalRenderer(name: string): Promise<number> {
const instance = this._terminalService.createTerminalRenderer(name);
return Promise.resolve(instance.id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
......@@ -130,44 +124,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
public $terminalRendererWrite(terminalId: number, text: string): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.write(text);
}
}
public $terminalRendererSetName(terminalId: number, name: string): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setTitle(name, false);
}
}
public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.setDimensions(dimensions);
}
}
public $terminalRendererRegisterOnInputListener(terminalId: number): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (!terminalInstance) {
return;
}
// Listener already registered
if (this._terminalOnDidAcceptInputListeners.has(terminalId)) {
return;
}
// Register
const listener = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data));
this._terminalOnDidAcceptInputListeners.set(terminalId, listener);
terminalInstance.addDisposable(listener);
}
public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
const terminalInstance = this._terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
......@@ -231,10 +187,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptWorkspacePermissionsChanged(isAllowed);
}
private _onTerminalRendererInput(terminalId: number, data: string): void {
this._proxy.$acceptTerminalRendererInput(terminalId, data);
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
}
......
......@@ -395,7 +395,6 @@ export interface TerminalLaunchConfig {
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(config: TerminalLaunchConfig): Promise<{ id: number, name: string }>;
$createTerminalRenderer(name: string): Promise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
......@@ -414,12 +413,6 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessCwd(terminalId: number, initialCwd: string): void;
$sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void;
$sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void;
// Renderer
$terminalRendererSetName(terminalId: number, name: string): void;
$terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void;
$terminalRendererWrite(terminalId: number, text: string): void;
$terminalRendererRegisterOnInputListener(terminalId: number): void;
}
export interface TransferQuickPickItems extends quickInput.IQuickPickItem {
......@@ -1163,7 +1156,6 @@ export interface ExtHostTerminalServiceShape {
/** @deprecated */
$acceptTerminalProcessData(id: number, data: string): void;
$acceptTerminalProcessData2(id: number, data: string): void;
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
......
......@@ -1753,26 +1753,6 @@ export enum TaskScope {
Workspace = 2
}
export class CustomExecution implements vscode.CustomExecution {
private _callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>;
constructor(callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = callback;
}
public computeId(): string {
return 'customExecution' + generateUuid();
}
public set callback(value: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = value;
}
public get callback(): (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number> {
return this._callback;
}
}
export class CustomExecution2 implements vscode.CustomExecution2 {
private _callback: () => Thenable<vscode.Pseudoterminal>;
constructor(callback: () => Thenable<vscode.Pseudoterminal>) {
......@@ -1804,7 +1784,7 @@ export class Task implements vscode.Task2 {
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined;
private _execution: ProcessExecution | ShellExecution | CustomExecution2 | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
......@@ -1813,8 +1793,8 @@ export class Task implements vscode.Task2 {
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution2, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
this.definition = definition;
let problemMatchers: string | string[];
......@@ -1879,7 +1859,7 @@ export class Task implements vscode.Task2 {
type: Task.ShellType,
id: this._execution.computeId()
};
} else if (this._execution instanceof CustomExecution) {
} else if (this._execution instanceof CustomExecution2) {
this._definition = {
type: Task.ExtensionCallbackType,
id: this._execution.computeId()
......@@ -1926,18 +1906,18 @@ export class Task implements vscode.Task2 {
}
get execution(): ProcessExecution | ShellExecution | undefined {
return ((this._execution instanceof CustomExecution) || (this._execution instanceof CustomExecution2)) ? undefined : this._execution;
return (this._execution instanceof CustomExecution2) ? undefined : this._execution;
}
set execution(value: ProcessExecution | ShellExecution | undefined) {
this.execution2 = value;
}
get execution2(): ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined {
get execution2(): ProcessExecution | ShellExecution | CustomExecution2 | undefined {
return this._execution;
}
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | CustomExecution2 | undefined) {
set execution2(value: ProcessExecution | ShellExecution | CustomExecution2 | undefined) {
if (value === null) {
value = undefined;
}
......
......@@ -66,10 +66,6 @@ export interface ShellExecutionDTO {
options?: ShellExecutionOptionsDTO;
}
export interface CustomExecutionDTO {
customExecution: 'customExecution';
}
export interface CustomExecution2DTO {
customExecution: 'customExecution2';
}
......@@ -88,7 +84,7 @@ export interface TaskHandleDTO {
export interface TaskDTO {
_id: string;
name?: string;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecution2DTO | undefined;
definition: TaskDefinitionDTO;
isBackground?: boolean;
source: TaskSourceDTO;
......@@ -129,4 +125,4 @@ export interface TaskSystemInfoDTO {
scheme: string;
authority: string;
platform: string;
}
\ No newline at end of file
}
......@@ -542,9 +542,6 @@ export function createApiFactory(
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
createTerminalRenderer(name: string): vscode.TerminalRenderer {
return extHostTerminalService.createTerminalRenderer(name);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
},
......@@ -850,7 +847,6 @@ export function createApiFactory(
EventEmitter: Emitter,
ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext,
ExtensionKind: extHostTypes.ExtensionKind,
CustomExecution: extHostTypes.CustomExecution,
CustomExecution2: extHostTypes.CustomExecution2,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
......
......@@ -21,17 +21,15 @@ import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO,
CustomExecutionDTO,
CustomExecution2DTO,
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../common/shared/tasks';
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
namespace TaskDefinitionDTO {
......@@ -80,7 +78,7 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
if (value) {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
......@@ -125,7 +123,7 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
if (value) {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
......@@ -162,25 +160,8 @@ namespace ShellExecutionDTO {
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecutionDTO {
if (value) {
let candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
} else {
return false;
}
}
export function from(value: vscode.CustomExecution): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
}
namespace CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
if (value) {
let candidate = value as CustomExecution2DTO;
return candidate && candidate.customExecution === 'customExecution2';
......@@ -229,13 +210,11 @@ namespace TaskDTO {
if (value === undefined || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | CustomExecution2DTO | undefined;
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution) {
execution = CustomExecutionDTO.from(<types.CustomExecution>(<vscode.Task2>value).execution2);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
}
......@@ -373,110 +352,6 @@ interface HandlerData {
extension: IExtensionDescription;
}
class CustomExecutionData implements IDisposable {
private static waitForDimensionsTimeoutInMs: number = 5000;
private _cancellationSource?: CancellationTokenSource;
private readonly _onTaskExecutionComplete: Emitter<CustomExecutionData> = new Emitter<CustomExecutionData>();
private readonly _disposables = new DisposableStore();
private terminal?: vscode.Terminal;
private terminalId?: number;
public result: number | undefined;
constructor(
private readonly customExecution: vscode.CustomExecution,
private readonly terminalService: ExtHostTerminalService) {
}
public dispose(): void {
this._cancellationSource = undefined;
this._disposables.dispose();
}
public get onTaskExecutionComplete(): Event<CustomExecutionData> {
return this._onTaskExecutionComplete.event;
}
private onDidCloseTerminal(terminal: vscode.Terminal): void {
if ((this.terminal === terminal) && this._cancellationSource) {
this._cancellationSource.cancel();
}
}
private onDidOpenTerminal(terminal: vscode.Terminal): void {
if (!(terminal instanceof ExtHostTerminal)) {
throw new Error('How could this not be a extension host terminal?');
}
if (this.terminalId && terminal._id === this.terminalId) {
this.startCallback(this.terminalId);
}
}
public async startCallback(terminalId: number): Promise<void> {
this.terminalId = terminalId;
// If we have already started the extension task callback, then
// do not start it again.
// It is completely valid for multiple terminals to be opened
// before the one for our task.
if (this._cancellationSource) {
return undefined;
}
const callbackTerminals: vscode.Terminal[] = this.terminalService.terminals.filter((terminal) => terminal._id === terminalId);
if (!callbackTerminals || callbackTerminals.length === 0) {
this._disposables.add(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
return;
}
if (callbackTerminals.length !== 1) {
throw new Error(`Expected to only have one terminal at this point`);
}
this.terminal = callbackTerminals[0];
const terminalRenderer: vscode.TerminalRenderer = await this.terminalService.resolveTerminalRenderer(terminalId);
// If we don't have the maximum dimensions yet, then we need to wait for them (but not indefinitely).
// Custom executions will expect the dimensions to be set properly before they are launched.
// BUT, due to the API contract VSCode has for terminals and dimensions, they are still responsible for
// handling cases where they are not set.
if (!terminalRenderer.maximumDimensions) {
const dimensionTimeout: Promise<void> = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, CustomExecutionData.waitForDimensionsTimeoutInMs);
});
let dimensionsRegistration: IDisposable | undefined;
const dimensionsPromise: Promise<void> = new Promise((resolve) => {
dimensionsRegistration = terminalRenderer.onDidChangeMaximumDimensions((newDimensions) => {
resolve();
});
});
await Promise.race([dimensionTimeout, dimensionsPromise]);
if (dimensionsRegistration) {
dimensionsRegistration.dispose();
}
}
this._cancellationSource = new CancellationTokenSource();
this._disposables.add(this._cancellationSource);
this._disposables.add(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this)));
// Regardless of how the task completes, we are done with this custom execution task.
this.customExecution.callback(terminalRenderer, this._cancellationSource.token).then(
(success) => {
this.result = success;
this._onTaskExecutionComplete.fire(this);
}, (rejected) => {
this._onTaskExecutionComplete.fire(this);
});
}
}
export class ExtHostTask implements ExtHostTaskShape {
private _proxy: MainThreadTaskShape;
......@@ -487,8 +362,6 @@ export class ExtHostTask implements ExtHostTaskShape {
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private _taskExecutions: Map<string, TaskExecutionImpl>;
private _providedCustomExecutions: Map<string, CustomExecutionData>;
private _activeCustomExecutions: Map<string, CustomExecutionData>;
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
......@@ -512,8 +385,6 @@ export class ExtHostTask implements ExtHostTaskShape {
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
this._taskExecutions = new Map<string, TaskExecutionImpl>();
this._providedCustomExecutions = new Map<string, CustomExecutionData>();
this._activeCustomExecutions = new Map<string, CustomExecutionData>();
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
}
......@@ -591,25 +462,6 @@ export class ExtHostTask implements ExtHostTaskShape {
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
}
// Once a terminal is spun up for the custom execution task this event will be fired.
// At that point, we need to actually start the callback, but
// only if it hasn't already begun.
const extensionCallback: CustomExecutionData | undefined = this._providedCustomExecutions.get(execution.id);
if (extensionCallback) {
if (this._activeCustomExecutions.get(execution.id) !== undefined) {
throw new Error('We should not be trying to start the same custom task executions twice.');
}
this._activeCustomExecutions.set(execution.id, extensionCallback);
const taskExecutionComplete: IDisposable = extensionCallback.onTaskExecutionComplete(() => {
this.customExecutionComplete(execution);
taskExecutionComplete.dispose();
});
extensionCallback.startCallback(terminalId);
}
this._onDidExecuteTask.fire({
execution: await this.getTaskExecution(execution)
});
......@@ -665,7 +517,6 @@ export class ExtHostTask implements ExtHostTaskShape {
// For custom execution tasks, we need to store the execution objects locally
// since we obviously cannot send callback functions through the proxy.
// So, clear out any existing ones.
this._providedCustomExecutions.clear();
this._providedCustomExecutions2.clear();
// Set up a list of task ID promises that we can wait on
......@@ -689,14 +540,11 @@ export class ExtHostTask implements ExtHostTaskShape {
if (taskDTO) {
taskDTOs.push(taskDTO);
if (CustomExecutionDTO.is(taskDTO.execution)) {
if (CustomExecution2DTO.is(taskDTO.execution)) {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
taskIdPromises.push(this.addCustomExecution(taskDTO, <vscode.Task2>task));
} else if (CustomExecution2DTO.is(taskDTO.execution)) {
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
}
}
}
......@@ -745,10 +593,6 @@ export class ExtHostTask implements ExtHostTaskShape {
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
}
if (CustomExecutionDTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution(resolvedTaskDTO, <vscode.Task2>resolvedTask);
}
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask);
}
......@@ -801,11 +645,6 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._handleCounter++;
}
private async addCustomExecution(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
}
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
const taskId = await this._proxy.$createTaskId(taskDTO);
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
......@@ -834,12 +673,6 @@ export class ExtHostTask implements ExtHostTaskShape {
}
private customExecutionComplete(execution: TaskExecutionDTO): void {
const extensionCallback: CustomExecutionData | undefined = this._activeCustomExecutions.get(execution.id);
if (extensionCallback) {
this._activeCustomExecutions.delete(execution.id);
this._proxy.$customExecutionComplete(execution.id, extensionCallback.result);
extensionCallback.dispose();
}
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
if (extensionCallback2) {
this._activeCustomExecutions2.delete(execution.id);
......
......@@ -24,8 +24,6 @@ import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/term
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
import { IDisposable } from 'vs/base/common/lifecycle';
const RENDERER_NO_PROCESS_ID = -1;
export class BaseExtHostTerminal {
public _id: number | undefined;
protected _idPromise: Promise<number>;
......@@ -104,17 +102,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
constructor(
proxy: MainThreadTerminalServiceShape,
private _name?: string,
id?: number,
pid?: number
id?: number
) {
super(proxy, id);
this._pidPromise = new Promise<number>(c => {
if (pid === RENDERER_NO_PROCESS_ID) {
c(undefined);
} else {
this._pidPromiseComplete = c;
}
});
this._pidPromise = new Promise<number>(c => this._pidPromiseComplete = c);
}
public async create(
......@@ -205,92 +196,11 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
}
}
export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vscode.TerminalRenderer {
public get name(): string { return this._name; }
public set name(newName: string) {
this._name = newName;
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererSetName, [this._name]);
}
private readonly _onInput = new Emitter<string>();
public get onDidAcceptInput(): Event<string> {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererRegisterOnInputListener, [this._id]);
// Tell the main side to start sending data if it's not already
// this._proxy.$terminalRendererRegisterOnDataListener(this._id);
return this._onInput && this._onInput.event;
}
private _dimensions: vscode.TerminalDimensions | undefined;
public get dimensions(): vscode.TerminalDimensions | undefined { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions | undefined) {
this._checkDisposed();
this._dimensions = dimensions;
this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]);
}
private _maximumDimensions: vscode.TerminalDimensions | undefined;
public get maximumDimensions(): vscode.TerminalDimensions | undefined {
if (!this._maximumDimensions) {
return undefined;
}
return {
rows: this._maximumDimensions.rows,
columns: this._maximumDimensions.columns
};
}
private readonly _onDidChangeMaximumDimensions: Emitter<vscode.TerminalDimensions> = new Emitter<vscode.TerminalDimensions>();
public get onDidChangeMaximumDimensions(): Event<vscode.TerminalDimensions> {
return this._onDidChangeMaximumDimensions && this._onDidChangeMaximumDimensions.event;
}
public get terminal(): ExtHostTerminal {
return this._terminal;
}
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _terminal: ExtHostTerminal,
id?: number
) {
super(proxy, id);
if (!id) {
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
}
}
public write(data: string): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererWrite, [data]);
}
public _fireOnInput(data: string): void {
this._onInput.fire(data);
}
public _setMaximumDimensions(columns: number, rows: number): void {
if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) {
return;
}
const newValue = { columns, rows };
this._maximumDimensions = newValue;
this._onDidChangeMaximumDimensions.fire(newValue);
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal | undefined;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
private _variableResolver: ExtHostVariableResolverService | undefined;
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
......@@ -360,17 +270,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._setupExtHostProcessListeners(id, p);
}
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminal = new ExtHostTerminal(this._proxy, name);
terminal._setProcessId(undefined);
this._terminals.push(terminal);
const renderer = new ExtHostTerminalRenderer(this._proxy, name, terminal);
this._terminalRenderers.push(renderer);
return renderer;
}
public getDefaultShell(configProvider: ExtHostConfigProvider): string {
const fetchSetting = (key: string) => {
const setting = configProvider
......@@ -401,24 +300,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, this._lastActiveWorkspace, this._variableResolver, this._logService);
}
public async resolveTerminalRenderer(id: number): Promise<vscode.TerminalRenderer> {
// Check to see if the extension host already knows about this terminal.
for (const terminalRenderer of this._terminalRenderers) {
if (terminalRenderer._id === id) {
return terminalRenderer;
}
}
const terminal = this._getTerminalById(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal renderer for terminal id ${id}`);
}
const renderer = new ExtHostTerminalRenderer(this._proxy, terminal.name, terminal, terminal._id);
this._terminalRenderers.push(renderer);
return renderer;
}
public async $acceptActiveTerminalChanged(id: number | null): Promise<void> {
const original = this._activeTerminal;
if (id === null) {
......@@ -468,25 +349,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
await this._getTerminalByIdEventually(id);
if (this._terminalProcesses[id]) {
// Virtual processes only - when virtual process resize fires it means that the
// Extension pty terminal only - when virtual process resize fires it means that the
// terminal's maximum dimensions changed
this._terminalProcesses[id].resize(cols, rows);
} else {
// Terminal renderer
this._getTerminalByIdEventually(id).then(() => {
// When a terminal's dimensions change, a renderer's _maximum_ dimensions change
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
});
}
}
public $acceptTerminalRendererInput(id: number, data: string): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._fireOnInput(data);
}
}
......@@ -516,8 +381,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return;
}
const renderer = this._getTerminalRendererById(id);
const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined);
const terminal = new ExtHostTerminal(this._proxy, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
terminal.isOpen = true;
......@@ -785,16 +649,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return this._getTerminalObjectById(this._terminals, id);
}
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer | null {
return this._getTerminalObjectById(this._terminalRenderers, id);
}
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T | null {
private _getTerminalObjectById<T extends ExtHostTerminal>(array: T[], id: number): T | null {
const index = this._getTerminalObjectIndexById(array, id);
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number | null {
private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: number): number | null {
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;
......@@ -855,9 +715,7 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
}
shutdown(): void {
if (this._pty.close) {
this._pty.close();
}
this._pty.close();
}
input(data: string): void {
......@@ -899,9 +757,7 @@ class ExtHostPseudoterminal implements ITerminalChildProcess {
this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : e));
}
if (this._pty.open) {
this._pty.open(initialDimensions);
}
this._pty.open(initialDimensions ? initialDimensions : undefined);
}
}
......
......@@ -285,7 +285,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
return new Promise<void>((resolve) => {
activeTerminal.terminal.rendererExit(result);
// activeTerminal.terminal.rendererExit(result);
resolve();
});
}
......@@ -472,7 +472,7 @@ export class TerminalTaskSystem implements ITaskSystem {
const resolvedVariables = this.resolveVariablesFromSet(systemInfo, workspaceFolder, task, variables);
return resolvedVariables.then((resolvedVariables) => {
const isCustomExecution = (task.command.runtime === RuntimeType.CustomExecution) || (task.command.runtime === RuntimeType.CustomExecution2);
const isCustomExecution = (task.command.runtime === RuntimeType.CustomExecution2);
if (resolvedVariables && (task.command !== undefined) && task.command.runtime && (isCustomExecution || (task.command.name !== undefined))) {
this.currentTask.resolvedVariables = resolvedVariables;
return this.executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder);
......@@ -556,9 +556,7 @@ export class TerminalTaskSystem implements ITaskSystem {
let processStartedSignaled = false;
terminal.processReady.then(() => {
if (!processStartedSignaled) {
if (task.command.runtime !== RuntimeType.CustomExecution) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!));
}
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!));
processStartedSignaled = true;
}
}, (_error) => {
......@@ -608,9 +606,7 @@ export class TerminalTaskSystem implements ITaskSystem {
processStartedSignaled = true;
}
if (task.command.runtime !== RuntimeType.CustomExecution) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode));
}
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode));
for (let i = 0; i < eventCounter; i++) {
let event = TaskEvent.create(TaskEventKind.Inactive, task);
......@@ -635,9 +631,7 @@ export class TerminalTaskSystem implements ITaskSystem {
let processStartedSignaled = false;
terminal.processReady.then(() => {
if (!processStartedSignaled) {
if (task.command.runtime !== RuntimeType.CustomExecution) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!));
}
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!));
processStartedSignaled = true;
}
}, (_error) => {
......@@ -690,9 +684,8 @@ export class TerminalTaskSystem implements ITaskSystem {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!));
processStartedSignaled = true;
}
if (task.command.runtime !== RuntimeType.CustomExecution) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode));
}
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task));
resolve({ exitCode });
......@@ -845,7 +838,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
}
} else {
let commandExecutable = ((task.command.runtime !== RuntimeType.CustomExecution) && (task.command.runtime !== RuntimeType.CustomExecution2)) ? CommandString.value(command) : undefined;
let commandExecutable = (task.command.runtime !== RuntimeType.CustomExecution2) ? CommandString.value(command) : undefined;
let executable = !isShellCommand
? this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}')
: commandExecutable;
......@@ -917,14 +910,7 @@ export class TerminalTaskSystem implements ITaskSystem {
let args: CommandString[] | undefined;
let launchConfigs: IShellLaunchConfig | undefined;
if (task.command.runtime === RuntimeType.CustomExecution) {
this.currentTask.shellLaunchConfig = launchConfigs = {
isRendererOnly: true,
waitOnExit,
name: this.createTerminalName(task, workspaceFolder),
initialText: task.command.presentation && task.command.presentation.echo ? `\x1b[1m> Executing task: ${task._label} <\x1b[0m\n` : undefined
};
} else if (task.command.runtime === RuntimeType.CustomExecution2) {
if (task.command.runtime === RuntimeType.CustomExecution2) {
this.currentTask.shellLaunchConfig = launchConfigs = {
isExtensionTerminal: true,
waitOnExit,
......@@ -1145,7 +1131,7 @@ export class TerminalTaskSystem implements ITaskSystem {
private collectCommandVariables(variables: Set<string>, command: CommandConfiguration, task: CustomTask | ContributedTask): void {
// The custom execution should have everything it needs already as it provided
// the callback.
if ((command.runtime === RuntimeType.CustomExecution) || (command.runtime === RuntimeType.CustomExecution2)) {
if (command.runtime === RuntimeType.CustomExecution2) {
return;
}
......
......@@ -273,8 +273,7 @@ export namespace PresentationOptions {
export enum RuntimeType {
Shell = 1,
Process = 2,
CustomExecution = 3,
CustomExecution2 = 4
CustomExecution2 = 3
}
export namespace RuntimeType {
......@@ -284,8 +283,6 @@ export namespace RuntimeType {
return RuntimeType.Shell;
case 'process':
return RuntimeType.Process;
case 'customExecution':
return RuntimeType.CustomExecution;
case 'customExecution2':
return RuntimeType.CustomExecution2;
default:
......@@ -663,10 +660,6 @@ export class CustomTask extends CommonTask {
type = 'process';
break;
case RuntimeType.CustomExecution:
type = 'customExecution';
break;
case RuntimeType.CustomExecution2:
type = 'customExecution2';
break;
......
......@@ -280,7 +280,7 @@ export class ProcessTaskSystem implements ITaskSystem {
this.childProcessEnded();
watchingProblemMatcher.done();
watchingProblemMatcher.dispose();
if (processStartedSignaled && task.command.runtime !== RuntimeType.CustomExecution) {
if (processStartedSignaled) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode!));
}
toDispose = dispose(toDispose!);
......@@ -336,7 +336,7 @@ export class ProcessTaskSystem implements ITaskSystem {
startStopProblemMatcher.done();
startStopProblemMatcher.dispose();
this.checkTerminated(task, success);
if (processStartedSignaled && task.command.runtime !== RuntimeType.CustomExecution) {
if (processStartedSignaled) {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode!));
}
this._onDidStateChange.fire(inactiveEvent);
......
......@@ -172,7 +172,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private static _lastKnownGridDimensions: IGridDimensions | undefined;
private static _idCounter = 1;
private _processManager: ITerminalProcessManager | undefined;
private _processManager!: ITerminalProcessManager;
private _pressAnyKeyToCloseListener: IDisposable | undefined;
private _id: number;
......@@ -220,10 +220,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public get maxCols(): number { return this._cols; }
public get maxRows(): number { return this._rows; }
// TODO: Ideally processId would be merged into processReady
public get processId(): number | undefined { return this._processManager ? this._processManager.shellProcessId : undefined; }
public get processId(): number | undefined { return this._processManager.shellProcessId; }
// TODO: How does this work with detached processes?
// TODO: Should this be an event as it can fire twice?
public get processReady(): Promise<void> { return this._processManager ? this._processManager.ptyProcessReady : Promise.resolve(undefined); }
public get processReady(): Promise<void> { return this._processManager.ptyProcessReady; }
public get title(): string { return this._title; }
public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; }
public get isTitleSetByProcess(): boolean { return !!this._messageTitleDisposable; }
......@@ -245,8 +245,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public get onData(): Event<string> { return this._onData.event; }
private readonly _onLineData = new Emitter<string>();
public get onLineData(): Event<string> { return this._onLineData.event; }
private readonly _onRendererInput = new Emitter<string>();
public get onRendererInput(): Event<string> { return this._onRendererInput.event; }
private readonly _onRequestExtHostProcess = new Emitter<ITerminalInstance>();
public get onRequestExtHostProcess(): Event<ITerminalInstance> { return this._onRequestExtHostProcess.event; }
private readonly _onDimensionsChanged = new Emitter<void>();
......@@ -294,11 +292,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig);
this._initDimensions();
if (!this.shellLaunchConfig.isRendererOnly) {
this._createProcess();
} else {
this.setTitle(this._shellLaunchConfig.name, false);
}
this._createProcess();
this._xtermReadyPromise = this._createXterm();
this._xtermReadyPromise.then(() => {
......@@ -489,41 +483,29 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._xterm.onLineFeed(() => this._onLineFeed());
this._xterm.onKey(e => this._onKey(e.key, e.domEvent));
if (this._processManager) {
this._processManager.onProcessData(data => this._onProcessData(data));
this._xterm.onData(data => this._processManager!.write(data));
// TODO: How does the cwd work on detached processes?
this.processReady.then(async () => {
if (this._linkHandler) {
this._linkHandler.processCwd = await this._processManager!.getInitialCwd();
}
});
// Init winpty compat and link handler after process creation as they rely on the
// underlying process OS
this._processManager.onProcessReady(() => {
if (!this._processManager) {
return;
}
if (this._processManager.os === platform.OperatingSystem.Windows) {
xterm.setOption('windowsMode', true);
// Force line data to be sent when the cursor is moved, the main purpose for
// this is because ConPTY will often not do a line feed but instead move the
// cursor, in which case we still want to send the current line's data to tasks.
xterm.addCsiHandler('H', () => {
this._onCursorMove();
return false;
});
}
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, this._processManager, this._configHelper);
});
} else if (this.shellLaunchConfig.isRendererOnly) {
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, undefined, this._configHelper);
}
// Register listener to trigger the onInput ext API if the terminal is a renderer only
if (this._shellLaunchConfig.isRendererOnly) {
this._xterm.onData(data => this._sendRendererInput(data));
}
this._processManager.onProcessData(data => this._onProcessData(data));
this._xterm.onData(data => this._processManager.write(data));
// TODO: How does the cwd work on detached processes?
this.processReady.then(async () => {
if (this._linkHandler) {
this._linkHandler.processCwd = await this._processManager.getInitialCwd();
}
});
// Init winpty compat and link handler after process creation as they rely on the
// underlying process OS
this._processManager.onProcessReady(() => {
if (this._processManager.os === platform.OperatingSystem.Windows) {
xterm.setOption('windowsMode', true);
// Force line data to be sent when the cursor is moved, the main purpose for
// this is because ConPTY will often not do a line feed but instead move the
// cursor, in which case we still want to send the current line's data to tasks.
xterm.addCsiHandler('H', () => {
this._onCursorMove();
return false;
});
}
this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, this._processManager, this._configHelper);
});
this._commandTrackerAddon = new CommandTrackerAddon();
this._xterm.loadAddon(this._commandTrackerAddon);
......@@ -662,18 +644,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._wrapperElement.appendChild(this._xtermElement);
this._container.appendChild(this._wrapperElement);
if (this._processManager) {
const widgetManager = new TerminalWidgetManager(this._wrapperElement);
this._widgetManager = widgetManager;
this._processManager.onProcessReady(() => {
if (this._linkHandler) {
this._linkHandler.setWidgetManager(widgetManager);
}
});
} else if (this._shellLaunchConfig.isRendererOnly) {
this._widgetManager = new TerminalWidgetManager(this._wrapperElement);
this._linkHandler!.setWidgetManager(this._widgetManager);
}
const widgetManager = new TerminalWidgetManager(this._wrapperElement);
this._widgetManager = widgetManager;
this._processManager.onProcessReady(() => {
if (this._linkHandler) {
this._linkHandler.setWidgetManager(widgetManager);
}
});
const computedStyle = window.getComputedStyle(this._container);
const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10);
......@@ -836,14 +813,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._pressAnyKeyToCloseListener = undefined;
}
if (this._processManager) {
this._processManager.dispose(immediate);
} else {
// In cases where there is no associated process (for example executing an extension callback task)
// consumers still expect on onExit event to be fired. An example of this is terminating the extension callback
// task.
this._onExit.fire(0);
}
this._processManager.dispose(immediate);
if (!this._isDisposed) {
this._isDisposed = true;
......@@ -852,16 +822,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
super.dispose();
}
public rendererExit(exitCode: number): void {
// The use of this API is for cases where there is no backing process behind a terminal
// instance (e.g. a custom execution task).
if (!this.shellLaunchConfig.isRendererOnly) {
throw new Error('rendererExit is only expected to be called on a renderer only terminal');
}
return this._onProcessExit(exitCode);
}
public forceRedraw(): void {
if (!this._xterm) {
return;
......@@ -910,10 +870,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return;
}
this._xterm.write(text);
if (this._shellLaunchConfig.isRendererOnly) {
// Fire onData API in the extension host
this._onData.fire(text);
}
});
}
......@@ -924,17 +880,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
text += '\r';
}
if (this._shellLaunchConfig.isRendererOnly) {
// If the terminal is a renderer only, fire the onInput ext API
this._sendRendererInput(text);
} else {
// If the terminal has a process, send it to the process
if (this._processManager) {
this._processManager.ptyProcessReady.then(() => {
this._processManager!.write(text);
});
}
}
// Send it to the process
this._processManager.ptyProcessReady.then(() => this._processManager.write(text));
}
public setVisible(visible: boolean): void {
......@@ -1031,7 +978,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
if (platform.isWindows) {
this._processManager.ptyProcessReady.then(() => {
if (this._processManager!.remoteAuthority) {
if (this._processManager.remoteAuthority) {
return;
}
this._xtermReadyPromise.then(xterm => {
......@@ -1045,7 +992,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// Create the process asynchronously to allow the terminal's container
// to be created so dimensions are accurate
setTimeout(() => {
this._processManager!.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._isScreenReaderOptimized());
this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._isScreenReaderOptimized());
}, 0);
}
......@@ -1083,7 +1030,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidPathDirectory', 'The terminal shell path "{0}" is a directory', this._shellLaunchConfig.executable);
} else if (exitCode === SHELL_CWD_INVALID_EXIT_CODE && this._shellLaunchConfig.cwd) {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithInvalidCWD', 'The terminal shell CWD "{0}" does not exist', this._shellLaunchConfig.cwd.toString());
} else if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
} else if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
let args = '';
if (typeof this._shellLaunchConfig.args === 'string') {
args = ` ${this._shellLaunchConfig.args}`;
......@@ -1105,11 +1052,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
this._logService.debug(`Terminal process exit (id: ${this.id})${this._processManager ? ' state ' + this._processManager.processState : ''}`);
this._logService.debug(`Terminal process exit (id: ${this.id}) state ${this._processManager.processState}`);
// Only trigger wait on exit when the exit was *not* triggered by the
// user (via the `workbench.action.terminal.kill` command).
if (this._shellLaunchConfig.waitOnExit && (!this._processManager || this._processManager.processState !== ProcessState.KILLED_BY_USER)) {
if (this._shellLaunchConfig.waitOnExit && this._processManager.processState !== ProcessState.KILLED_BY_USER) {
this._xtermReadyPromise.then(xterm => {
if (exitCodeMessage) {
xterm.writeln(exitCodeMessage);
......@@ -1129,7 +1076,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
} else {
this.dispose();
if (exitCodeMessage) {
if (this._processManager && this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
if (this._processManager.processState === ProcessState.KILLED_DURING_LAUNCH) {
this._notificationService.error(exitCodeMessage);
} else {
if (this._configHelper.config.showExitAlert) {
......@@ -1165,10 +1112,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
// Kill and clear up the process, making the process manager ready for a new process
if (this._processManager) {
this._processManager.dispose();
this._processManager = undefined;
}
this._processManager.dispose();
if (this._xterm) {
// Ensure new processes' output starts at start of new line
......@@ -1192,30 +1136,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// Launch the process unless this is only a renderer.
// In the renderer only cases, we still need to set the title correctly.
const oldTitle = this._title;
if (!this._shellLaunchConfig.isRendererOnly) {
this._createProcess();
} else if (this._shellLaunchConfig.name) {
this.setTitle(this._shellLaunchConfig.name, false);
}
this._createProcess();
if (oldTitle !== this._title) {
this.setTitle(this._title, true);
}
if (this._processManager) {
// The "!" operator is required here because _processManager is set to undefiend earlier
// and TS does not know that createProcess sets it.
this._processManager!.onProcessData(data => this._onProcessData(data));
}
}
private _sendRendererInput(input: string): void {
if (this._processManager) {
throw new Error('onRendererInput attempted to be used on a regular terminal');
}
// For terminal renderers onData fires on keystrokes and when sendText is called.
this._onRendererInput.fire(input);
this._processManager.onProcessData(data => this._onProcessData(data));
}
private _onLineFeed(): void {
......@@ -1400,9 +1327,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
if (this._processManager) {
this._processManager.ptyProcessReady.then(() => this._processManager!.setDimensions(cols, rows));
}
this._processManager.ptyProcessReady.then(() => this._processManager.setDimensions(cols, rows));
}
public setTitle(title: string | undefined, eventFromProcess: boolean): void {
......@@ -1497,16 +1422,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
public getInitialCwd(): Promise<string> {
if (!this._processManager) {
return Promise.resolve('');
}
return this._processManager.getInitialCwd();
}
public getCwd(): Promise<string> {
if (!this._processManager) {
return Promise.resolve('');
}
return this._processManager.getCwd();
}
}
......
......@@ -187,11 +187,6 @@ export interface IShellLaunchConfig {
*/
initialText?: string;
/**
* @deprecated use `isExtensionTerminal`
*/
isRendererOnly?: boolean;
/**
* Whether an extension is controlling the terminal via a `vscode.Pseudoterminal`.
*/
......@@ -244,12 +239,6 @@ export interface ITerminalService {
*/
createTerminal(shell?: IShellLaunchConfig): ITerminalInstance;
/**
* Creates a terminal renderer.
* @param name The name of the terminal.
*/
createTerminalRenderer(name: string): ITerminalInstance;
/**
* Creates a raw terminal instance, this should not be used outside of the terminal part.
*/
......@@ -422,13 +411,6 @@ export interface ITerminalInstance {
*/
onData: Event<string>;
/**
* Attach a listener to the "renderer" input event, this event fires for terminal renderers on
* keystrokes and when the Terminal.sendText extension API is used.
* @param listener The listener function.
*/
onRendererInput: Event<string>;
/**
* Attach a listener to listen for new lines added to this terminal instance.
*
......@@ -497,14 +479,6 @@ export interface ITerminalInstance {
*/
dispose(immediate?: boolean): void;
/**
* Indicates that a consumer of a renderer only terminal is finished with it.
*
* @param exitCode The exit code of the terminal. Zero indicates success, non-zero indicates
* failure.
*/
rendererExit(exitCode: number): void;
/**
* Forces the terminal to redraw its viewport.
*/
......
......@@ -126,10 +126,6 @@ export abstract class TerminalService implements ITerminalService {
public abstract createInstance(terminalFocusContextKey: IContextKey<boolean>, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance;
public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
public createTerminalRenderer(name: string): ITerminalInstance {
return this.createTerminal({ name, isRendererOnly: true });
}
public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance {
const activeInstance = this.getActiveInstance();
return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册