提交 7f043c26 编写于 作者: D Daniel Imms

Pass terminal shell launch config through

上级 c8d58f42
......@@ -5,9 +5,9 @@
'use strict';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy } from 'vs/workbench/parts/terminal/common/terminal';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } 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 { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
......@@ -31,7 +31,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}));
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)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
// Set initial ext host state
this.terminalService.terminalInstances.forEach(t => {
......@@ -100,14 +100,21 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
private _onTerminalRequestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void {
private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {
console.log('mainThreadTerminalService#_onTerminalRequestExtHostProcess', arguments);
this._terminalProcesses[proxy.terminalId] = proxy;
this._proxy.$createProcess(proxy.terminalId, null, 0, 0);
this._terminalProcesses[request.proxy.terminalId] = request.proxy;
const shellLaunchConfigDto: ShellLaunchConfigDto = {
name: request.shellLaunchConfig.name,
executable: request.shellLaunchConfig.executable,
args: request.shellLaunchConfig.args,
cwd: request.shellLaunchConfig.cwd,
env: request.shellLaunchConfig.env
};
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.cols, request.rows);
// TODO: Dispose of this properly when the terminal/process dies
this._toDispose.push(proxy.onInput(data => this._proxy.$acceptTerminalProcessInput(proxy.terminalId, data)));
this._toDispose.push(proxy.onResize((cols, rows) => this._proxy.$acceptTerminalProcessResize(proxy.terminalId, cols, rows)));
this._toDispose.push(proxy.onShutdown(() => this._proxy.$acceptTerminalProcessShutdown(proxy.terminalId)));
this._toDispose.push(request.proxy.onInput(data => this._proxy.$acceptTerminalProcessInput(request.proxy.terminalId, data)));
this._toDispose.push(request.proxy.onResize((cols, rows) => this._proxy.$acceptTerminalProcessResize(request.proxy.terminalId, cols, rows)));
this._toDispose.push(request.proxy.onShutdown(() => this._proxy.$acceptTerminalProcessShutdown(request.proxy.terminalId)));
}
public $sendProcessTitle(terminalId: number, title: string): void {
......
......@@ -115,7 +115,7 @@ export function createApiFactory(
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
......
......@@ -7,10 +7,13 @@
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
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';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtHostTerminal implements vscode.Terminal {
......@@ -116,7 +119,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public get terminals(): ExtHostTerminal[] { return this._terminals; }
constructor(mainContext: IMainContext) {
constructor(
mainContext: IMainContext,
private _extHostConfiguration: ExtHostConfiguration,
private _logService: ILogService
) {
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
this._onDidOpenTerminal = new Emitter<vscode.Terminal>();
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
......@@ -176,28 +183,61 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void {
shellLaunchConfig = {
env: {},
executable: 'bash'
};
// TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally
// they would be merged into a single implementation.
const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated');
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux';
const shellConfigValue: string = terminalConfig.get(`shell.${platformKey}`);
const shellArgsConfigValue: string = terminalConfig.get(`shellArgs.${platformKey}`);
shellLaunchConfig.executable = shellConfigValue;
shellLaunchConfig.args = shellArgsConfigValue;
}
this._logService.info('$createProcess', id, shellLaunchConfig, cols, rows);
// const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
// this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
// // Resolve env vars from config and shell
// const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
// shellLaunchConfig.env = envFromShell;
// // Merge process env with the env from config
// const parentEnv = { ...process.env };
// terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
// // Continue env initialization, merging in the env from the launch
// // config and adding keys that are needed to create the process
// const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, this.initialCwd, locale, cols, rows);
// const cwd = Uri.parse(paths.dirname(require.toUrl('../node/terminalProcess'))).fsPath;
// const options = { env, cwd };
// 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);
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, '/home/daniel', locale, 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._logService.debug(`Terminal process launching on ext host`, options);
this._terminalProcesses[id] = cp.fork(bootstrapUri, ['--type=terminal'], options);
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
......@@ -207,9 +247,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
case 'data': this._proxy.$sendProcessData(id, <string>message.content); break;
}
});
const terminal = this._getTerminalById(id);
console.log('$createProcess terminal: ' + terminal.name);
}
public $acceptTerminalProcessInput(id: number, data: string): void {
......
......@@ -165,7 +165,7 @@ export interface ITerminalService {
onInstanceCreated: Event<ITerminalInstance>;
onInstanceDisposed: Event<ITerminalInstance>;
onInstanceProcessIdReady: Event<ITerminalInstance>;
onInstanceRequestExtHostProcess: Event<ITerminalProcessExtHostProxy>;
onInstanceRequestExtHostProcess: Event<ITerminalProcessExtHostRequest>;
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): void;
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): void;
}
export const enum Direction {
......@@ -533,3 +533,10 @@ export interface ITerminalProcessExtHostProxy {
onResize(listener: (cols: number, rows: number) => void): IDisposable;
onShutdown(listener: () => void): IDisposable;
}
export interface ITerminalProcessExtHostRequest {
proxy: ITerminalProcessExtHostProxy;
shellLaunchConfig: IShellLaunchConfig;
cols: number;
rows: number;
}
\ No newline at end of file
......@@ -9,7 +9,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy } from 'vs/workbench/parts/terminal/common/terminal';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
......@@ -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<ITerminalProcessExtHostProxy> = new Emitter<ITerminalProcessExtHostProxy>();
public get onInstanceRequestExtHostProcess(): Event<ITerminalProcessExtHostProxy> { return this._onInstanceRequestExtHostProcess.event; }
protected readonly _onInstanceRequestExtHostProcess: Emitter<ITerminalProcessExtHostRequest> = new Emitter<ITerminalProcessExtHostRequest>();
public get onInstanceRequestExtHostProcess(): Event<ITerminalProcessExtHostRequest> { 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): void;
public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): void;
private _restoreTabs(): void {
if (!this.configHelper.config.experimentalRestore) {
......
......@@ -109,6 +109,7 @@ export class TerminalInstance implements ITerminalInstance {
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILogService private _logService: ILogService
) {
// TODO: Delete me
this._shellLaunchConfig.extensionHostOwned = true;
this._disposables = [];
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import * as path from 'path';
import * as paths from 'vs/base/common/paths';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import Uri from 'vs/base/common/uri';
......@@ -59,6 +59,12 @@ export class TerminalProcessManager implements ITerminalProcessManager {
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILogService private _logService: ILogService
) {
this.ptyProcessReady = new TPromise<void>(c => {
this.onProcessReady(() => {
this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`);
c(void 0);
});
});
}
public dispose(): void {
......@@ -85,41 +91,35 @@ export class TerminalProcessManager implements ITerminalProcessManager {
cols: number,
rows: number
): void {
this.ptyProcessReady = new TPromise<void>(c => {
this.onProcessReady(() => {
this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`);
c(void 0);
});
});
const locale = this._configHelper.config.setLocaleVariables ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
}
const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
// Resolve env vars from config and shell
const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, this.initialCwd, locale, cols, rows);
const cwd = Uri.parse(path.dirname(require.toUrl('../node/terminalProcess'))).fsPath;
const options = { env, cwd };
this._logService.debug(`Terminal process launching`, options);
if (shellLaunchConfig.extensionHostOwned) {
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId);
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, cols, rows);
} else {
const locale = this._configHelper.config.setLocaleVariables ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
}
const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
// Resolve env vars from config and shell
const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, this.initialCwd, locale, cols, rows);
const cwd = Uri.parse(paths.dirname(require.toUrl('../node/terminalProcess'))).fsPath;
const options = { env, cwd };
this._logService.debug(`Terminal process launching`, options);
this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
}
this.processState = ProcessState.LAUNCHING;
......
......@@ -95,13 +95,12 @@ export class TerminalService extends AbstractTerminalService implements ITermina
return this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, undefined, shellLaunchConfig, true);
}
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy): void {
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): 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);
this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, cols, rows });
}, 100);
});
}
......
......@@ -79,7 +79,6 @@ export function createTerminalEnv(parentEnv: IStringDictionary<string>, shell: I
return env;
}
// TODO:should be protected/non-static
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: IStringDictionary<string>, lastActiveWorkspaceRoot: IWorkspaceFolder): IStringDictionary<string> {
Object.keys(env).forEach((key) => {
if (typeof env[key] === 'string') {
......
......@@ -5,7 +5,7 @@
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 { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { IDisposable } from '../../../../base/common/lifecycle';
export class TerminalProcessExtHostProxy extends EventEmitter implements ITerminalChildProcess, ITerminalProcessExtHostProxy {
......@@ -14,12 +14,15 @@ export class TerminalProcessExtHostProxy extends EventEmitter implements ITermin
constructor(
public terminalId: number,
shellLaunchConfig: IShellLaunchConfig,
cols: number,
rows: number
@ITerminalService private _terminalService: ITerminalService
) {
super();
// TODO: Return TPromise<boolean> indicating success? Teardown if failure?
this._terminalService.requestExtHostProcess(this);
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, cols, rows);
}
public emitData(data: string): void {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册