diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 14128133e0626737266cf359ef70903a7e9d31f6..621dcdbd40e2a4ebc86731c752773f70ed2e0908 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -12,7 +12,6 @@ import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { once } from 'vs/base/common/event'; import { OpenContext } from 'vs/platform/windows/common/windows'; import { IWindowsMainService, ICodeWindow } from "vs/platform/windows/electron-main/windows"; @@ -115,16 +114,7 @@ export class LaunchService implements ILaunchService { // If the other instance is waiting to be killed, we hook up a window listener if one window // is being used and only then resolve the startup promise which will kill this second instance if (args.wait && usedWindows.length === 1 && usedWindows[0]) { - const windowId = usedWindows[0].id; - - return new TPromise((c, e) => { - const onceWindowClose = once(this.windowsService.onWindowClose); - onceWindowClose(id => { - if (id === windowId) { - c(null); - } - }); - }); + return this.windowsService.waitForWindowClose(usedWindows[0].id); } return TPromise.as(null); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1692cdca4a0e4cb7ff2a4891250775dae3500365..7b02760c0fcf13670b0833d538c48462f650fcb1 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -315,6 +315,13 @@ export class WindowsManager implements IWindowsMainService { this._onPathsOpen.fire(windowsToOpen); } + // If we got started with --wait from the CLI, we need to signal to the outside when the window + // used for the edit operation is closed so that the waiting process can continue. We do this by + // deleting the waitMarkerFilePath. + if (openConfig.context === OpenContext.CLI && openConfig.cli.wait && openConfig.cli.waitMarkerFilePath && usedWindows.length === 1 && usedWindows[0]) { + this.waitForWindowClose(usedWindows[0].id).done(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0)); + } + return usedWindows; } @@ -1044,6 +1051,17 @@ export class WindowsManager implements IWindowsMainService { this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); } + public waitForWindowClose(windowId: number): TPromise { + return new TPromise(c => { + const toDispose = this.onWindowClose(id => { + if (id === windowId) { + toDispose.dispose(); + c(null); + } + }); + }); + } + public sendToFocused(channel: string, ...args: any[]): void { const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index f8e6b9d7c3f0848a33f6e1576f567e7dc2e283d7..d0f0416e6ffc20dc710964912b64100ee20c5f82 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -11,6 +11,10 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; +import * as fs from 'fs'; +import * as paths from 'path'; +import * as os from 'os'; + function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension']; } @@ -49,9 +53,34 @@ export function main(argv: string[]): TPromise { env['ELECTRON_ENABLE_LOGGING'] = '1'; } + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + let waitMarkerFilePath: string; + if (args.wait) { + let waitMarkerError: Error; + const randomTmpFile = paths.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + try { + fs.writeFileSync(randomTmpFile, ''); + waitMarkerFilePath = randomTmpFile; + argv.push('--waitMarkerFilePath', waitMarkerFilePath); + } catch (error) { + waitMarkerError = error; + } + + if (args.verbose) { + if (waitMarkerError) { + console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`); + } else { + console.log(`Marker file for --wait created: ${waitMarkerFilePath}`); + } + } + } + const options = { detached: true, - env, + env }; if (!args.verbose) { @@ -65,9 +94,27 @@ export function main(argv: string[]): TPromise { child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); } - if (args.wait || args.verbose) { + if (args.verbose) { return new TPromise(c => child.once('exit', () => c(null))); } + + if (args.wait && waitMarkerFilePath) { + return new TPromise(c => { + + // Complete when process exits + child.once('exit', () => c(null)); + + // Complete when wait marker file is deleted + const interval = setInterval(() => { + fs.exists(waitMarkerFilePath, exists => { + if (!exists) { + clearInterval(interval); + c(null); + } + }); + }, 1000); + }); + } } return TPromise.as(null); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index b7aee17d58400ca9314cdba0117737478046957f..679643e9353b8b4edb9e9321da4708a78e64ab16 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -11,6 +11,7 @@ export interface ParsedArgs { help?: boolean; version?: boolean; wait?: boolean; + waitMarkerFilePath?: string; diff?: boolean; goto?: boolean; 'new-window'?: boolean; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index c31a706d3a66819dddc3e6269bdbb13733ed2462..2acc9ec8d0aec71f30a51d03c448da4fa31a11db 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -55,6 +55,7 @@ export interface IWindowsMainService { pickFolder(options?: { buttonLabel: string; title: string; }): TPromise; focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow; getLastActiveWindow(): ICodeWindow; + waitForWindowClose(windowId: number): TPromise; findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): ICodeWindow; openNewWindow(context: OpenContext): void; sendToFocused(channel: string, ...args: any[]): void;