Get virtual processes mostly working
This commit is contained in:
parent
2205cb69ed
commit
6f1d0fc7ec
5
src/vs/vscode.proposed.d.ts
vendored
5
src/vs/vscode.proposed.d.ts
vendored
|
@ -1292,6 +1292,11 @@ declare module 'vscode' {
|
|||
// This is called fire when window.onDidChangeTerminalDimensions fires as CustomExecution need
|
||||
// access to the "maximum" dimensions and don't want access to Terminal
|
||||
onDidChangeDimensions?(dimensions: TerminalDimensions): void;
|
||||
|
||||
/**
|
||||
* F
|
||||
*/
|
||||
onDidShutdownTerminal(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -46,6 +46,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||
this._toDispose.push(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
|
||||
this._toDispose.push(_terminalService.onInstanceMaximumDimensionsChanged(instance => this._onInstanceMaximumDimensionsChanged(instance)));
|
||||
this._toDispose.push(_terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
|
||||
this._toDispose.push(_terminalService.onInstanceRequestVirtualProcess(proxy => this._onTerminalRequestVirtualProcess(proxy)));
|
||||
this._toDispose.push(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
|
||||
this._toDispose.push(_terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title)));
|
||||
this._toDispose.push(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
|
||||
|
@ -77,6 +78,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||
}
|
||||
|
||||
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string, cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean, hideFromUser?: boolean, isVirtualProcess?: boolean): Promise<{ id: number, name: string }> {
|
||||
console.log('$createTerminal', arguments);
|
||||
const shellLaunchConfig: IShellLaunchConfig = {
|
||||
name,
|
||||
executable: shellPath,
|
||||
|
@ -86,7 +88,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||
ignoreConfigurationCwd: true,
|
||||
env,
|
||||
strictEnv,
|
||||
hideFromUser
|
||||
hideFromUser,
|
||||
isVirtualProcess
|
||||
};
|
||||
const terminal = this._terminalService.createTerminal(shellLaunchConfig);
|
||||
return Promise.resolve({
|
||||
|
@ -256,6 +259,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||
request.proxy.onRequestLatency(() => this._onRequestLatency(request.proxy.terminalId));
|
||||
}
|
||||
|
||||
private _onTerminalRequestVirtualProcess(proxy: ITerminalProcessExtHostProxy): void {
|
||||
console.log('_onTerminalRequestVirtualProcess', proxy);
|
||||
this._terminalProcesses[proxy.terminalId] = proxy;
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
|
||||
proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
|
||||
}
|
||||
|
||||
public $sendProcessTitle(terminalId: number, title: string): void {
|
||||
this._terminalProcesses[terminalId].emitTitle(title);
|
||||
}
|
||||
|
|
|
@ -524,10 +524,14 @@ export function createApiFactory(
|
|||
checkProposedApiEnabled(extension);
|
||||
return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension);
|
||||
},
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.TerminalVirtualProcessOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
if (typeof nameOrOptions === 'object') {
|
||||
nameOrOptions.hideFromUser = nameOrOptions.hideFromUser || (nameOrOptions.runInBackground && extension.enableProposedApi);
|
||||
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
|
||||
if ('virtualProcess' in nameOrOptions) {
|
||||
return extHostTerminalService.createVirtualProcessTerminal(nameOrOptions);
|
||||
} else {
|
||||
nameOrOptions.hideFromUser = nameOrOptions.hideFromUser || (nameOrOptions.runInBackground && extension.enableProposedApi);
|
||||
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
|
||||
}
|
||||
}
|
||||
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event';
|
|||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
|
@ -116,17 +116,20 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
|
|||
env?: { [key: string]: string | null },
|
||||
waitOnExit?: boolean,
|
||||
strictEnv?: boolean,
|
||||
hideFromUser?: boolean,
|
||||
isVirtualProcess?: boolean
|
||||
hideFromUser?: boolean
|
||||
): void {
|
||||
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isVirtualProcess).then(terminal => {
|
||||
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser).then(terminal => {
|
||||
this._name = terminal.name;
|
||||
this._runQueuedRequests(terminal.id);
|
||||
});
|
||||
}
|
||||
|
||||
public createVirtualProcess(): void {
|
||||
this.create(undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
|
||||
public createVirtualProcess(): Promise<void> {
|
||||
// TODO: Change $createTerminal to accept an object
|
||||
return this._proxy.$createTerminal(this._name, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true).then(terminal => {
|
||||
this._name = terminal.name;
|
||||
this._runQueuedRequests(terminal.id);
|
||||
});
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
|
@ -280,7 +283,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
private _proxy: MainThreadTerminalServiceShape;
|
||||
private _activeTerminal: ExtHostTerminal | undefined;
|
||||
private _terminals: ExtHostTerminal[] = [];
|
||||
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
|
||||
private _terminalProcesses: { [id: number]: ITerminalChildProcess } = {};
|
||||
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
|
||||
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
|
||||
|
||||
|
@ -318,11 +321,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
|
||||
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, options.name);
|
||||
if ((<any>options).isVirtualProcess) {
|
||||
terminal.createVirtualProcess();
|
||||
} else {
|
||||
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser);
|
||||
}
|
||||
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser);
|
||||
this._terminals.push(terminal);
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public createVirtualProcessTerminal(options: vscode.TerminalVirtualProcessOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, options.name);
|
||||
terminal.createVirtualProcess().then(() => {
|
||||
const id = terminal._id;
|
||||
console.log('virtual process id: ' + terminal._id);
|
||||
// TODO: The ID is ready now
|
||||
const p = new ExtHostVirtualProcess(options.virtualProcess);
|
||||
p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd));
|
||||
p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
|
||||
p.onProcessData(data => this._proxy.$sendProcessData(id, data));
|
||||
p.onProcessExit(exitCode => this._onProcessExit(id, exitCode));
|
||||
this._terminalProcesses[terminal._id] = p;
|
||||
});
|
||||
this._terminals.push(terminal);
|
||||
return terminal;
|
||||
}
|
||||
|
@ -607,9 +623,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
|||
}
|
||||
|
||||
private _onProcessExit(id: number, exitCode: number): void {
|
||||
// Remove listeners
|
||||
this._terminalProcesses[id].dispose();
|
||||
|
||||
// Remove process reference
|
||||
delete this._terminalProcesses[id];
|
||||
|
||||
|
@ -690,3 +703,59 @@ class ApiRequest {
|
|||
this._callback.apply(proxy, [id].concat(this._args));
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostVirtualProcess implements ITerminalChildProcess {
|
||||
private readonly _onProcessData = new Emitter<string>();
|
||||
public get onProcessData(): Event<string> { return this._onProcessData.event; }
|
||||
private readonly _onProcessExit = new Emitter<number>();
|
||||
public get onProcessExit(): Event<number> { return this._onProcessExit.event; }
|
||||
private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>();
|
||||
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
|
||||
private readonly _onProcessTitleChanged = new Emitter<string>();
|
||||
public get onProcessTitleChanged(): Event<string> { return this._onProcessTitleChanged.event; }
|
||||
|
||||
constructor(
|
||||
private readonly _virtualProcess: vscode.TerminalVirtualProcess
|
||||
) {
|
||||
// TODO: Events need to be buffered until the terminal id is set
|
||||
this._virtualProcess.write(e => this._onProcessData.fire(e));
|
||||
if (this._virtualProcess.exit) {
|
||||
this._virtualProcess.exit(e => this._onProcessExit.fire(e));
|
||||
}
|
||||
if (this._virtualProcess.overrideDimensions) {
|
||||
// TODO: Implement this
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
if (this._virtualProcess.onDidShutdownTerminal) {
|
||||
this._virtualProcess.onDidShutdownTerminal();
|
||||
}
|
||||
}
|
||||
|
||||
input(data: string): void {
|
||||
if (this._virtualProcess.onDidAcceptInput) {
|
||||
this._virtualProcess.onDidAcceptInput(data);
|
||||
}
|
||||
}
|
||||
|
||||
resize(cols: number, rows: number): void {
|
||||
if (this._virtualProcess.onDidChangeDimensions) {
|
||||
this._virtualProcess.onDidChangeDimensions({ columns: cols, rows });
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Are these returns correct?
|
||||
getInitialCwd(): Promise<string> {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
getCwd(): Promise<string> {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
getLatency(): Promise<number> {
|
||||
return Promise.resolve(0);
|
||||
}
|
||||
|
||||
}
|
|
@ -104,8 +104,8 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
|||
isScreenReaderModeEnabled: boolean
|
||||
): Promise<void> {
|
||||
if (shellLaunchConfig.isVirtualProcess) {
|
||||
// TODO: Hook up proxy
|
||||
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, undefined, cols, rows, this._configHelper);
|
||||
console.log('set terminal process ext host proxy', this._process);
|
||||
} else {
|
||||
const forceExtHostProcess = (this._configHelper.config as any).extHostProcess;
|
||||
if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') {
|
||||
|
|
|
@ -227,6 +227,7 @@ export interface ITerminalService {
|
|||
onInstanceDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceMaximumDimensionsChanged: Event<ITerminalInstance>;
|
||||
onInstanceRequestExtHostProcess: Event<ITerminalProcessExtHostRequest>;
|
||||
onInstanceRequestVirtualProcess: Event<ITerminalProcessExtHostProxy>;
|
||||
onInstancesChanged: Event<void>;
|
||||
onInstanceTitleChanged: Event<ITerminalInstance>;
|
||||
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
|
||||
|
@ -294,6 +295,7 @@ export interface ITerminalService {
|
|||
|
||||
extHostReady(remoteAuthority: string): void;
|
||||
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
|
||||
requestVirtualProcess(proxy: ITerminalProcessExtHostProxy): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,14 +51,21 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
|
|||
@IRemoteAgentService readonly remoteAgentService: IRemoteAgentService
|
||||
) {
|
||||
super();
|
||||
remoteAgentService.getEnvironment().then(env => {
|
||||
if (!env) {
|
||||
throw new Error('Could not fetch environment');
|
||||
|
||||
// Request a process if needed, if this is a virtual process this step can be skipped as
|
||||
// there is no real "process" and we know it's ready on the ext host already.
|
||||
if (shellLaunchConfig.isVirtualProcess) {
|
||||
this._terminalService.requestVirtualProcess(this);
|
||||
} else {
|
||||
remoteAgentService.getEnvironment().then(env => {
|
||||
if (!env) {
|
||||
throw new Error('Could not fetch environment');
|
||||
}
|
||||
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper.checkWorkspaceShellPermissions(env.os));
|
||||
});
|
||||
if (!hasReceivedResponse) {
|
||||
setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0);
|
||||
}
|
||||
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper.checkWorkspaceShellPermissions(env.os));
|
||||
});
|
||||
if (!hasReceivedResponse) {
|
||||
setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ export abstract class TerminalService implements ITerminalService {
|
|||
public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
|
||||
protected readonly _onInstanceRequestExtHostProcess = new Emitter<ITerminalProcessExtHostRequest>();
|
||||
public get onInstanceRequestExtHostProcess(): Event<ITerminalProcessExtHostRequest> { return this._onInstanceRequestExtHostProcess.event; }
|
||||
protected readonly _onInstanceRequestVirtualProcess = new Emitter<ITerminalProcessExtHostProxy>();
|
||||
public get onInstanceRequestVirtualProcess(): Event<ITerminalProcessExtHostProxy> { return this._onInstanceRequestVirtualProcess.event; }
|
||||
protected readonly _onInstanceDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
public get onInstanceDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceDimensionsChanged.event; }
|
||||
protected readonly _onInstanceMaximumDimensionsChanged = new Emitter<ITerminalInstance>();
|
||||
|
@ -142,6 +144,11 @@ export abstract class TerminalService implements ITerminalService {
|
|||
});
|
||||
}
|
||||
|
||||
public requestVirtualProcess(proxy: ITerminalProcessExtHostProxy): void {
|
||||
// Don't need to wait on extensions here as this can only be triggered by an extension
|
||||
this._onInstanceRequestVirtualProcess.fire(proxy);
|
||||
}
|
||||
|
||||
public extHostReady(remoteAuthority: string): void {
|
||||
this._extHostsReady[remoteAuthority] = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue