提交 70b22b8c 编写于 作者: D Daniel Imms

Get terminal process running on ext host

上级 4323d933
......@@ -5,7 +5,7 @@
'use strict';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
......@@ -14,23 +14,24 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
private _proxy: ExtHostTerminalServiceShape;
private _toDispose: IDisposable[];
private _toDispose: IDisposable[] = [];
private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {};
constructor(
extHostContext: IExtHostContext,
@ITerminalService private terminalService: ITerminalService
) {
console.log('MainThreadTerminalService#ctor');
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._toDispose = [];
this._toDispose.push(terminalService.onInstanceCreated((terminalInstance) => {
// Delay this message so the TerminalInstance constructor has a chance to finish and
// return the ID normally to the extension host. The ID that is passed here will be used
// to register non-extension API terminals in the extension host.
setTimeout(() => this._onTerminalOpened(terminalInstance), 100);
}));
this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess((terminalInstance) => this._onTerminalRequestExtHostProcess(terminalInstance)));
this._toDispose.push(terminalService.onInstanceDisposed(terminalInstance => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady(terminalInstance => this._onTerminalProcessIdReady(terminalInstance)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(proxy => this._onTerminalRequestExtHostProcess(proxy)));
// Set initial ext host state
this.terminalService.terminalInstances.forEach(t => {
......@@ -99,8 +100,27 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
private _onTerminalRequestExtHostProcess(terminalInstance: ITerminalInstance): void {
private _onTerminalRequestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void {
console.log('mainThreadTerminalService#_onTerminalRequestExtHostProcess', arguments);
this._proxy.$createProcess(null, 0, 0);
this._terminalProcesses[proxy.terminalId] = proxy;
this._proxy.$createProcess(proxy.terminalId, null, 0, 0);
// TODO: Dispose of this properly when the terminal/process dies
this._toDispose.push(proxy.onInput(data => this._onTerminalProcessWrite(proxy.terminalId, data)));
}
public $sendProcessTitle(terminalId: number, title: string): void {
this._terminalProcesses[terminalId].emitTitle(title);
}
public $sendProcessData(terminalId: number, data: string): void {
this._terminalProcesses[terminalId].emitData(data);
}
public $sendProcessPid(terminalId: number, pid: number): void {
this._terminalProcesses[terminalId].emitPid(pid);
}
private _onTerminalProcessWrite(terminalId: number, data: string): void {
this._proxy.$acceptTerminalProcessWrite(terminalId, data);
}
}
......@@ -321,6 +321,10 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessPid(terminalId: number, pid: number): void;
}
export interface MyQuickPickItems extends IPickOpenEntry {
......@@ -743,7 +747,8 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$createProcess(shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$acceptTerminalProcessWrite(id: number, data: string): void;
}
export interface ExtHostSCMShape {
......
......@@ -5,8 +5,12 @@
'use strict';
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as path from 'path';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
export class ExtHostTerminal implements vscode.Terminal {
......@@ -81,6 +85,7 @@ export class ExtHostTerminal implements vscode.Terminal {
}
public _setProcessId(processId: number): void {
console.log('extHostTerminalService#_setProcessId', processId);
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
}
......@@ -106,7 +111,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private readonly _onDidCloseTerminal: Emitter<vscode.Terminal>;
private readonly _onDidOpenTerminal: Emitter<vscode.Terminal>;
private _proxy: MainThreadTerminalServiceShape;
private _terminals: ExtHostTerminal[];
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: cp.ChildProcess } = {};
public get terminals(): ExtHostTerminal[] { return this._terminals; }
......@@ -114,7 +120,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
this._onDidOpenTerminal = new Emitter<vscode.Terminal>();
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
this._terminals = [];
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
......@@ -150,6 +155,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public $acceptTerminalOpened(id: number, name: string): void {
console.log('terminal opened: ' + id);
let index = this._getTerminalIndexById(id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
......@@ -163,13 +169,64 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public $acceptTerminalProcessId(id: number, processId: number): void {
let terminal = this._getTerminalById(id);
console.log('ExtHostTerminalService#$acceptTerminalProcessId ' + id + ' ' + processId);
if (terminal) {
terminal._setProcessId(processId);
}
}
public $createProcess(shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void {
console.log('$createProcess');
public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void {
shellLaunchConfig = {
env: {},
executable: 'bash'
};
// TODO: Launch process
// TODO: Associate the process with the terminal object/id
// TODO: terminal has incorrect name/options, fix up
const parentEnv = { ...process.env };
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, '/home/daniel', undefined, cols, rows);
// TODO: Use Uri?
let cwd = path.dirname(require.toUrl('../../parts/terminal/node/terminalProcess')).replace('file://', '');
console.log(cwd);
const options = { env, cwd, execArgv: [] };
let bootstrapUri = require.toUrl('bootstrap').replace('file://', '') + '.js';
console.log(bootstrapUri);
// cwd = '/home/daniel/dev/Microsoft/vscode/out/vs/workbench/parts/terminal/node';
// bootstrapUri = '/home/daniel/dev/Microsoft/vscode/out/bootstrap';
// env['AMD_ENTRYPOINT'] = 'vs/workbench/parts/terminal/node/terminalProcess';
this._terminalProcesses[id] = cp.fork(bootstrapUri, ['--type=terminal'], options);
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
switch (message.type) {
case 'pid':
this._proxy.$sendProcessPid(id, <number>message.content);
break;
case 'title':
this._proxy.$sendProcessTitle(id, <string>message.content);
break;
case 'data':
this._proxy.$sendProcessData(id, <string>message.content);
break;
}
// console.log('message type: ' + d.type + ', content: ' + d.content)
});
// const processPath = require.toUrl('../../parts/terminal/node/terminalProcess').replace('file://', '');
// const process2 = cp.fork(processPath, [], options);
// process2.on('data', d => console.log('data ' + d));
// process2.on('exit', d => console.log('exit ' + d));
// process2.on('error', d => console.log('error ' + d));
const terminal = this._getTerminalById(id);
console.log('$createProcess terminal: ' + terminal.name);
}
public $acceptTerminalProcessWrite(id: number, data: string): void {
this._terminalProcesses[id].send({ event: 'input', data });
}
private _getTerminalById(id: number): ExtHostTerminal {
......@@ -180,6 +237,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _getTerminalIndexById(id: number): number {
let index: number = null;
this._terminals.some((terminal, i) => {
// TODO: This shouldn't be cas
let thisId = (<any>terminal)._id;
if (thisId === id) {
index = i;
......
......@@ -165,7 +165,7 @@ export interface ITerminalService {
onInstanceCreated: Event<ITerminalInstance>;
onInstanceDisposed: Event<ITerminalInstance>;
onInstanceProcessIdReady: Event<ITerminalInstance>;
onInstanceRequestExtHostProcess: Event<ITerminalInstance>;
onInstanceRequestExtHostProcess: Event<ITerminalProcessExtHostProxy>;
onInstancesChanged: Event<void>;
onInstanceTitleChanged: Event<string>;
terminalInstances: ITerminalInstance[];
......@@ -207,7 +207,7 @@ export interface ITerminalService {
selectDefaultWindowsShell(): TPromise<string>;
setWorkspaceShellAllowed(isAllowed: boolean): void;
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): TPromise<void>;
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void;
}
export const enum Direction {
......@@ -524,7 +524,10 @@ export enum ProcessState {
export interface ITerminalProcessExtHostProxy {
readonly terminalId: number;
emitData(data: string): void;
emitTitle(title: string): void;
emitPid(pid: number): void;
}
\ No newline at end of file
onInput(listener: (data: string) => void): IDisposable;
}
......@@ -39,8 +39,8 @@ export abstract class TerminalService implements ITerminalService {
public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
protected readonly _onInstanceProcessIdReady: Emitter<ITerminalInstance> = new Emitter<ITerminalInstance>();
public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
protected readonly _onInstanceRequestExtHostProcess: Emitter<ITerminalInstance> = new Emitter<ITerminalInstance>();
public get onInstanceRequestExtHostProcess(): Event<ITerminalInstance> { return this._onInstanceRequestExtHostProcess.event; }
protected readonly _onInstanceRequestExtHostProcess: Emitter<ITerminalProcessExtHostProxy> = new Emitter<ITerminalProcessExtHostProxy>();
public get onInstanceRequestExtHostProcess(): Event<ITerminalProcessExtHostProxy> { return this._onInstanceRequestExtHostProcess.event; }
protected readonly _onInstancesChanged: Emitter<void> = new Emitter<void>();
public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
protected readonly _onInstanceTitleChanged: Emitter<string> = new Emitter<string>();
......@@ -75,7 +75,7 @@ export abstract class TerminalService implements ITerminalService {
public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance;
public abstract selectDefaultWindowsShell(): TPromise<string>;
public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): TPromise<void>;
public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void;
private _restoreTabs(): void {
if (!this.configHelper.config.experimentalRestore) {
......
......@@ -587,7 +587,7 @@ export class TerminalInstance implements ITerminalInstance {
}
protected _createProcess(): void {
this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._configHelper);
this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper);
this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this));
this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode));
this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows);
......
......@@ -51,6 +51,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
public get onProcessExit(): Event<number> { return this._onProcessExit.event; }
constructor(
private _terminalId: number,
private _configHelper: ITerminalConfigHelper,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IHistoryService private readonly _historyService: IHistoryService,
......@@ -117,7 +118,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
const options = { env, cwd };
this._logService.debug(`Terminal process launching`, options);
if (shellLaunchConfig.extensionHostOwned) {
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy);
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId);
} else {
this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
}
......
......@@ -27,6 +27,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { ipcRenderer as ipc } from 'electron';
import { IOpenFileRequest } from 'vs/platform/windows/common/windows';
import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
import { IExtensionService } from '../../../services/extensions/common/extensions';
export class TerminalService extends AbstractTerminalService implements ITerminalService {
private _configHelper: TerminalConfigHelper;
......@@ -47,7 +48,8 @@ export class TerminalService extends AbstractTerminalService implements ITermina
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IQuickOpenService private readonly _quickOpenService: IQuickOpenService,
@INotificationService private readonly _notificationService: INotificationService,
@IDialogService private readonly _dialogService: IDialogService
@IDialogService private readonly _dialogService: IDialogService,
@IExtensionService private readonly _extensionService: IExtensionService
) {
super(contextKeyService, panelService, partService, lifecycleService, storageService);
......@@ -93,17 +95,15 @@ export class TerminalService extends AbstractTerminalService implements ITermina
return this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, undefined, shellLaunchConfig, true);
}
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): TPromise<void> {
let i = 0;
setTimeout(() => {
proxy.emitPid(-1);
proxy.emitTitle('test title');
proxy.emitData(`test ${i++}\r\n`);
}, 0);
setInterval(() => {
proxy.emitData(`test ${i++}\r\n`);
}, 1000);
return TPromise.as(void 0);
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void {
// Ensure extension host is ready before requesting a process
this._extensionService.whenInstalledExtensionsRegistered().then(() => {
// TODO: MainThreadTerminalService is not ready at this point, fix this
setTimeout(() => {
console.log('request');
this._onInstanceRequestExtHostProcess.fire(proxy);
}, 100);
});
}
public focusFindWidget(): TPromise<void> {
......
......@@ -6,17 +6,19 @@
import { ITerminalChildProcess, IMessageToTerminalProcess, IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
import { EventEmitter } from 'events';
import { ITerminalService, ITerminalProcessExtHostProxy } from 'vs/workbench/parts/terminal/common/terminal';
import { IDisposable } from '../../../../base/common/lifecycle';
export class TerminalProcessExtHostProxy extends EventEmitter implements ITerminalChildProcess, ITerminalProcessExtHostProxy {
public connected: boolean;
constructor(
public terminalId: number,
@ITerminalService private _terminalService: ITerminalService
) {
super();
this._terminalService.requestExtHostProcess(this).then(() => {
});
// TODO: Return TPromise<boolean> indicating success? Teardown if failure?
this._terminalService.requestExtHostProcess(this);
}
public emitData(data: string): void {
......@@ -30,7 +32,21 @@ export class TerminalProcessExtHostProxy extends EventEmitter implements ITermin
}
public send(message: IMessageToTerminalProcess): boolean {
console.log('TerminalProcessExtHostBridge#send', arguments);
console.log('TerminalProcessExtHostProxy#send');
if (message.event === 'input') {
console.log('emit input', message.data);
this.emit('input', message.data);
}
return true;
}
public onInput(listener: (data: string) => void): IDisposable {
console.log('TerminalProcessExtHostProxy#onInput', arguments);
// TODO: Dispose of me
this.on('input', data => {
console.log('TerminalProcessExtHostProxy#onInput - listener');
listener(data);
});
return null;
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册