diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 84ed8b00b64..3edd37683f0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -214,6 +214,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data)); request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows)); request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate)); + request.proxy.onRequestCwd(() => { + console.log('onRequestCwd', request.proxy.terminalId); + this._proxy.$acceptProcessRequestCwd(request.proxy.terminalId); + }); + request.proxy.onRequestInitialCwd(() => { + console.log('onRequestInitialCwd', request.proxy.terminalId); + this._proxy.$acceptProcessRequestInitialCwd(request.proxy.terminalId); + }); } public $sendProcessTitle(terminalId: number, title: string): void { @@ -232,4 +240,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._terminalProcesses[terminalId].emitExit(exitCode); delete this._terminalProcesses[terminalId]; } + + public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void { + this._terminalProcesses[terminalId].emitInitialCwd(initialCwd); + } + + public $sendProcessCwd(terminalId: number, cwd: string): void { + this._terminalProcesses[terminalId].emitCwd(cwd); + } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index c9bb77f1063..85e68f5080d 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -363,6 +363,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $sendProcessData(terminalId: number, data: string): void; $sendProcessPid(terminalId: number, pid: number): void; $sendProcessExit(terminalId: number, exitCode: number): void; + $sendProcessInitialCwd(terminalId: number, cwd: string): void; + $sendProcessCwd(terminalId: number, initialCwd: string): void; // Renderer $terminalRendererSetName(terminalId: number, name: string): void; @@ -938,6 +940,8 @@ export interface ExtHostTerminalServiceShape { $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; $acceptProcessShutdown(id: number, immediate: boolean): void; + $acceptProcessRequestInitialCwd(id: number): void; + $acceptProcessRequestCwd(id: number): void; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 99ba5c8878d..c99a43a3561 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -430,11 +430,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); - this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty')); - this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); - this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); - this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data)); - this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode)); + const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty')); + p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); + 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[id] = p; } @@ -457,6 +458,23 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._terminalProcesses[id].shutdown(immediate); } + // TODO: Also support initial cwd as it's evaluated on the ext host + public $acceptProcessRequestInitialCwd(id: number): void { + console.log('$acceptProcessRequestInitialCwd', id); + this._terminalProcesses[id].getInitialCwd().then(initialCwd => { + console.log('initialCwd', initialCwd); + this._proxy.$sendProcessInitialCwd(id, initialCwd); + }); + } + + public $acceptProcessRequestCwd(id: number): void { + console.log('$acceptProcessRequestCwd', id); + this._terminalProcesses[id].getCwd().then(cwd => { + console.log('cwd', cwd); + this._proxy.$sendProcessCwd(id, cwd); + }); + } + private _onProcessExit(id: number, exitCode: number): void { // Remove listeners this._terminalProcesses[id].dispose(); diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 155b7c68578..8f61e7761ce 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -417,11 +417,6 @@ export interface ITerminalInstance { */ readonly commandTracker: ITerminalCommandTracker; - /** - * The cwd that the terminal instance was initialized with. - */ - readonly initialCwd: string; - /** * Dispose the terminal instance, removing it from the panel/service and freeing up resources. * @@ -614,6 +609,7 @@ export interface ITerminalInstance { toggleEscapeSequenceLogging(): void; + getInitialCwd(): Promise; getCwd(): Promise; } @@ -630,7 +626,6 @@ export interface ITerminalProcessManager extends IDisposable { readonly processState: ProcessState; readonly ptyProcessReady: Promise; readonly shellProcessId: number; - readonly initialCwd: string; readonly onProcessReady: Event; readonly onProcessData: Event; @@ -642,6 +637,9 @@ export interface ITerminalProcessManager extends IDisposable { createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number); write(data: string): void; setDimensions(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; } export const enum ProcessState { @@ -671,10 +669,14 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { emitTitle(title: string): void; emitPid(pid: number): void; emitExit(exitCode: number): void; + emitInitialCwd(initialCwd: string): void; + emitCwd(cwd: string): void; onInput: Event; onResize: Event<{ cols: number, rows: number }>; onShutdown: Event; + onRequestInitialCwd: Event; + onRequestCwd: Event; } export interface ITerminalProcessExtHostRequest { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index c77fbe34407..31564bafef7 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -35,7 +35,7 @@ export const TERMINAL_PICKER_PREFIX = 'term '; function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { - case 'workspaceRoot': { + case 'workspaceRoot': // allow original behavior let pathPromise: Promise = Promise.resolve(''); if (folders.length > 1) { @@ -53,15 +53,12 @@ function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminal } return pathPromise; - } - case 'initial': { - return new Promise(resolve => { - resolve(instance.initialCwd); - }); - } - case 'inherited': { + case 'initial': + console.log('getCwdForSplit initial'); + return instance.getInitialCwd(); + case 'inherited': + console.log('getCwdForSplit inherited'); return instance.getCwd(); - } } } @@ -385,8 +382,9 @@ export class SplitTerminalAction extends Action { if (!instance) { return Promise.resolve(undefined); } - + console.log('SplitTerminalAction'); return getCwdForSplit(this._terminalService.configHelper, instance, this.workspaceContextService.getWorkspace().folders, this.commandService).then(cwd => { + console.log('SplitTerminalAction cwd', cwd); if (cwd || (cwd === '')) { this._terminalService.splitInstance(instance, { cwd }); return this._terminalService.showPanel(true); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 4adb53f2ac0..c0c72063d10 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -36,7 +36,7 @@ import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notif import { ILogService } from 'vs/platform/log/common/log'; import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { execFile, exec } from 'child_process'; +import { execFile } from 'child_process'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; import { TerminalProcessManager } from 'vs/workbench/parts/terminal/electron-browser/terminalProcessManager'; @@ -434,8 +434,8 @@ export class TerminalInstance implements ITerminalInstance { this._xterm.on('data', data => this._processManager!.write(data)); // TODO: How does the cwd work on detached processes? this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform); - this.processReady.then(() => { - this._linkHandler.processCwd = this._processManager!.initialCwd; + this.processReady.then(async () => { + this._linkHandler.processCwd = await this._processManager!.getInitialCwd(); }); } this._xterm.on('focus', () => this._onFocus.fire(this)); @@ -1314,28 +1314,18 @@ export class TerminalInstance implements ITerminalInstance { this._xterm.setOption('debug', this._xterm._core.debug); } - public get initialCwd(): string { - if (this._processManager) { - return this._processManager.initialCwd; + public getInitialCwd(): Promise { + if (!this._processManager) { + return Promise.resolve(''); } - return ''; + return this._processManager.getInitialCwd(); } public getCwd(): Promise { - if (!platform.isWindows) { - let pid = this.processId; - return new Promise(resolve => { - exec('lsof -p ' + pid + ' | grep cwd', (error, stdout, stderr) => { - if (stdout !== '') { - resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); - } - }); - }); - } else { - return new Promise(resolve => { - resolve(this.initialCwd); - }); + if (!this._processManager) { + return Promise.resolve(''); } + return this._processManager.getCwd(); } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index f838343e3ff..ed59e4a655a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -35,6 +35,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; public ptyProcessReady: Promise; public shellProcessId: number; + // TODO: This should be removed in favor of async getInitialCwd public initialCwd: string; private _process: ITerminalChildProcess | null = null; @@ -200,6 +201,14 @@ export class TerminalProcessManager implements ITerminalProcessManager { } } + public getInitialCwd(): Promise { + return this._process.getInitialCwd(); + } + + public getCwd(): Promise { + return this._process.getCwd(); + } + private _onExit(exitCode: number): void { this._process = null; diff --git a/src/vs/workbench/parts/terminal/node/terminal.ts b/src/vs/workbench/parts/terminal/node/terminal.ts index 83768c9025f..73b41c24e5d 100644 --- a/src/vs/workbench/parts/terminal/node/terminal.ts +++ b/src/vs/workbench/parts/terminal/node/terminal.ts @@ -10,7 +10,7 @@ import { readFile, fileExists } from 'vs/base/node/pfs'; import { Event } from 'vs/base/common/event'; /** - * An interface representing a raw terminal child process, this is a subset of the + * An interface representing a raw terminal child process, this contains a subset of the * child_process.ChildProcess node.js interface. */ export interface ITerminalChildProcess { @@ -28,6 +28,9 @@ export interface ITerminalChildProcess { shutdown(immediate: boolean): void; input(data: string): void; resize(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; } export function getDefaultShell(p: platform.Platform): string { diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index ac913edc443..16dfd0580b4 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -11,6 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ITerminalChildProcess } from 'vs/workbench/parts/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; +import { exec } from 'child_process'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _exitCode: number; @@ -20,6 +21,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _processStartupComplete: Promise; private _isDisposed: boolean = false; private _titleInterval: NodeJS.Timer | null = null; + private _initialCwd: string; private readonly _onProcessData = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } @@ -47,6 +49,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { shellName = 'xterm-256color'; } + this._initialCwd = cwd; const useConpty = windowsEnableConpty && process.platform === 'win32' && this._getWindowsBuildNumber() >= 18309; const options: pty.IPtyForkOptions = { name: shellName, @@ -187,4 +190,24 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { // exception in winpty. this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1)); } + + public getInitialCwd(): Promise { + return Promise.resolve(this._initialCwd); + } + + public getCwd(): Promise { + if (platform.isWindows) { + return new Promise(resolve => { + resolve(this._initialCwd); + }); + } + + return new Promise(resolve => { + exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => { + if (stdout !== '') { + resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); + } + }); + }); + } } diff --git a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts index ea06eac26f5..653e09b65dd 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts @@ -28,6 +28,13 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm public get onResize(): Event<{ cols: number, rows: number }> { return this._onResize.event; } private readonly _onShutdown = new Emitter(); public get onShutdown(): Event { return this._onShutdown.event; } + private readonly _onRequestInitialCwd = new Emitter(); + public get onRequestInitialCwd(): Event { return this._onRequestInitialCwd.event; } + private readonly _onRequestCwd = new Emitter(); + public get onRequestCwd(): Event { return this._onRequestCwd.event; } + + private _pendingInitialCwdRequests: ((value?: string | Thenable) => void)[] = []; + private _pendingCwdRequests: ((value?: string | Thenable) => void)[] = []; constructor( public terminalId: number, @@ -68,6 +75,18 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm this.dispose(); } + public emitInitialCwd(initialCwd: string): void { + while (this._pendingInitialCwdRequests.length > 0) { + this._pendingInitialCwdRequests.pop()(initialCwd); + } + } + + public emitCwd(cwd: string): void { + while (this._pendingCwdRequests.length > 0) { + this._pendingCwdRequests.pop()(cwd); + } + } + public shutdown(immediate: boolean): void { this._onShutdown.fire(immediate); } @@ -79,4 +98,18 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm public resize(cols: number, rows: number): void { this._onResize.fire({ cols, rows }); } + + public getInitialCwd(): Promise { + return new Promise(resolve => { + this._onRequestInitialCwd.fire(); + this._pendingInitialCwdRequests.push(resolve); + }); + } + + public getCwd(): Promise { + return new Promise(resolve => { + this._onRequestCwd.fire(); + this._pendingCwdRequests.push(resolve); + }); + } } \ No newline at end of file