diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f5ff00b1049..d7a25cd2488 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -452,10 +452,22 @@ declare module 'vscode' { */ readonly args: string[]; + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + readonly env?: { [key: string]: string }; + + /** + * The working directory for the debug adapter. + */ + readonly cwd?: string; + /** * Create a new debug adapter specification. */ - constructor(command: string, args?: string[]); + constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string); } export class DebugAdapterServer { @@ -467,10 +479,15 @@ declare module 'vscode' { */ readonly port: number; + /** + * The host. + */ + readonly host?: string; + /** * Create a new debug adapter specification. */ - constructor(port: number); + constructor(port: number, host?: string); } export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 68ffe2d93a6..e0d68bbae18 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -347,7 +347,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { mythis._debugServiceProxy.$acceptDAMessage(handle, message); } - }(adapter.port); + }(adapter); break; case 'executable': @@ -363,7 +363,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { mythis._debugServiceProxy.$acceptDAMessage(handle, message); } - }(config.type, adapter); + }(adapter, config.type); break; default: diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index ee51dbde3af..7eee39b2e32 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1892,19 +1892,25 @@ export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable { readonly type = 'executable'; readonly command: string; readonly args: string[]; + readonly env?: { [key: string]: string }; + readonly cwd?: string; - constructor(command: string, args?: string[]) { + constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string) { this.command = command; this.args = args; + this.env = env; + this.cwd = cwd; } } export class DebugAdapterServer implements vscode.DebugAdapterServer { readonly type = 'server'; readonly port: number; + readonly host: string; - constructor(port: number) { + constructor(port: number, host?: string) { this.port = port; + this.host = host; } } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index c06771cb047..7362998f8f6 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -460,11 +460,14 @@ export interface IAdapterExecutable { readonly type: 'executable'; readonly command: string; readonly args: string[]; + readonly cwd?: string; + readonly env?: { [key: string]: string }; } export interface IAdapterServer { readonly type: 'server'; readonly port: number; + readonly host?: string; } export type IAdapterDescriptor = IAdapterExecutable | IAdapterServer; diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 54b758b9b92..cb6808251cf 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -17,7 +17,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IOutputService } from 'vs/workbench/parts/output/common/output'; -import { IDebugAdapter, IAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugAdapter, IAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IAdapterServer } from 'vs/workbench/parts/debug/common/debug'; /** * Abstract implementation of the low level API for a debug adapter. @@ -245,14 +245,14 @@ export class SocketDebugAdapter extends StreamDebugAdapter { private socket: net.Socket; - constructor(private port: number, private host = '127.0.0.1') { + constructor(private adapterServer: IAdapterServer) { super(); } startSession(): TPromise { return new TPromise((resolve, reject) => { let connected = false; - this.socket = net.createConnection(this.port, this.host, () => { + this.socket = net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', () => { this.connect(this.socket, this.socket); resolve(null); connected = true; @@ -294,19 +294,19 @@ export class DebugAdapter extends StreamDebugAdapter { private serverProcess: cp.ChildProcess; - constructor(private debugType: string, private adapterExecutable: IAdapterExecutable, private outputService?: IOutputService) { + constructor(private adapterExecutable: IAdapterExecutable, private debugType: string, private outputService?: IOutputService) { super(); } startSession(): TPromise { - return new TPromise((c, e) => { + return new TPromise((resolve, reject) => { // verify executables if (this.adapterExecutable.command) { if (paths.isAbsolute(this.adapterExecutable.command)) { if (!fs.existsSync(this.adapterExecutable.command)) { - e(new Error(nls.localize('debugAdapterBinNotFound', "Debug adapter executable '{0}' does not exist.", this.adapterExecutable.command))); + reject(new Error(nls.localize('debugAdapterBinNotFound', "Debug adapter executable '{0}' does not exist.", this.adapterExecutable.command))); } } else { // relative path @@ -316,28 +316,43 @@ export class DebugAdapter extends StreamDebugAdapter { } } } else { - e(new Error(nls.localize({ key: 'debugAdapterCannotDetermineExecutable', comment: ['Adapter executable file not found'] }, + reject(new Error(nls.localize({ key: 'debugAdapterCannotDetermineExecutable', comment: ['Adapter executable file not found'] }, "Cannot determine executable for debug adapter '{0}'.", this.debugType))); } if (this.adapterExecutable.command === 'node') { if (Array.isArray(this.adapterExecutable.args) && this.adapterExecutable.args.length > 0) { - const isElectron = process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']; - const child = cp.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), { + const isElectron = !!process.env['ELECTRON_RUN_AS_NODE'] || !!process.versions['electron']; + const options: cp.ForkOptions = { + env: this.adapterExecutable.env + ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.env) + : process.env, execArgv: isElectron ? ['-e', 'delete process.env.ELECTRON_RUN_AS_NODE;require(process.argv[1])'] : [], silent: true - }); + }; + if (this.adapterExecutable.cwd) { + options.cwd = this.adapterExecutable.cwd; + } + const child = cp.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), options); if (!child.pid) { - e(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); + reject(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); } this.serverProcess = child; - c(null); + resolve(null); } else { - e(new Error(nls.localize('unableToLaunchDebugAdapterNoArgs', "Unable to launch debug adapter."))); + reject(new Error(nls.localize('unableToLaunchDebugAdapterNoArgs', "Unable to launch debug adapter."))); } } else { - this.serverProcess = cp.spawn(this.adapterExecutable.command, this.adapterExecutable.args); - c(null); + const options: cp.SpawnOptions = { + env: this.adapterExecutable.env + ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.env) + : process.env + }; + if (this.adapterExecutable.cwd) { + options.cwd = this.adapterExecutable.cwd; + } + this.serverProcess = cp.spawn(this.adapterExecutable.command, this.adapterExecutable.args, options); + resolve(null); } }).then(_ => { this.serverProcess.on('error', err => { diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 5b1fb78dce2..af5e24b50be 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -46,9 +46,9 @@ export class Debugger implements IDebugger { return this.getAdapterDescriptor(session, root, config).then(adapterDescriptor => { switch (adapterDescriptor.type) { case 'server': - return new SocketDebugAdapter(adapterDescriptor.port); + return new SocketDebugAdapter(adapterDescriptor); case 'executable': - return new DebugAdapter(this.type, adapterDescriptor, outputService); + return new DebugAdapter(adapterDescriptor, this.type, outputService); default: return undefined; }