From 0e11dc34f45fec3d9cd7a0d6994eb1c0fb5d3fd2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 29 Sep 2021 09:57:32 +0200 Subject: [PATCH] CLI on macOS with -w does not exit when running multiple times (fix #134030) --- src/vs/code/node/cli.ts | 10 ++++---- .../launch/electron-main/launchMainService.ts | 23 ++++++++++++++++--- .../platform/windows/electron-main/windows.ts | 1 + .../electron-main/windowsMainService.ts | 19 +++++++++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index eb00a95ffa1..c9235cf463c 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -195,7 +195,7 @@ export async function main(argv: string[]): Promise { } } - const isBigSur = isMacintosh && release() > '20.0.0'; + const isMacOSBigSurOrNewer = isMacintosh && release() > '20.0.0'; // If we are started with --wait create a random temporary file // and pass it over to the starting instance. We can use this file @@ -214,11 +214,11 @@ export async function main(argv: string[]): Promise { // - the launched process terminates (e.g. due to a crash) processCallbacks.push(async child => { let childExitPromise; - if (isBigSur) { + if (isMacOSBigSurOrNewer) { // On Big Sur, we resolve the following promise only when the child, // i.e. the open command, exited with a signal or error. Otherwise, we // wait for the marker file to be deleted or for the child to error. - childExitPromise = new Promise((resolve) => { + childExitPromise = new Promise(resolve => { // Only resolve this promise if the child (i.e. open) exited with an error child.on('exit', (code, signal) => { if (code !== 0 || signal) { @@ -365,7 +365,7 @@ export async function main(argv: string[]): Promise { } let child: ChildProcess; - if (!isBigSur) { + if (!isMacOSBigSurOrNewer) { // We spawn process.execPath directly child = spawn(process.execPath, argv.slice(2), options); } else { @@ -390,7 +390,7 @@ export async function main(argv: string[]): Promise { spawnArgs.push(`--${outputType}`, tmpName); // Listener to redirect content to stdout/stderr - processCallbacks.push(async (child: ChildProcess) => { + processCallbacks.push(async child => { try { const stream = outputType === 'stdout' ? process.stdout : process.stderr; diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index d60c78c0def..f4dfaed3bd6 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -123,7 +123,13 @@ export class LaunchMainService implements ILaunchMainService { // Special case extension development if (!!args.extensionDevelopmentPath) { - this.windowsMainService.openExtensionDevelopmentHostWindow(args.extensionDevelopmentPath, { context, cli: args, userEnv, waitMarkerFileURI, remoteAuthority }); + this.windowsMainService.openExtensionDevelopmentHostWindow(args.extensionDevelopmentPath, { + context, + cli: args, + userEnv, + waitMarkerFileURI, + remoteAuthority + }); } // Start without file/folder arguments @@ -173,11 +179,22 @@ export class LaunchMainService implements ILaunchMainService { else { const lastActive = this.windowsMainService.getLastActiveWindow(); if (lastActive) { - lastActive.focus(); + this.windowsMainService.openExistingWindow(lastActive, { + context, + cli: args, + userEnv, + remoteAuthority + }); usedWindows = [lastActive]; } else { - usedWindows = this.windowsMainService.open({ context, cli: args, forceEmpty: true, remoteAuthority }); + usedWindows = this.windowsMainService.open({ + context, + cli: args, + userEnv, + forceEmpty: true, + remoteAuthority + }); } } } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 6d480a57dc8..6944296ce35 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -204,6 +204,7 @@ export interface IWindowsMainService { open(openConfig: IOpenConfiguration): ICodeWindow[]; openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[]; + openExistingWindow(window: ICodeWindow, openConfig: IOpenConfiguration): void; openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[]; sendToFocused(channel: string, ...args: any[]): void; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 61f8b074a6e..2a369b1aaca 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -231,6 +231,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return this.open({ ...openConfig, cli, forceEmpty, forceNewWindow, forceReuseWindow, remoteAuthority }); } + openExistingWindow(window: ICodeWindow, openConfig: IOpenConfiguration): void { + + // Bring window to front + window.focus(); + + // Handle --wait + this.handleWaitMarkerFile(openConfig, [window]); + } + open(openConfig: IOpenConfiguration): ICodeWindow[] { this.logService.trace('windowsManager#open'); @@ -370,6 +379,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic this.workspacesHistoryMainService.addRecentlyOpened(recents); } + // Handle --wait + this.handleWaitMarkerFile(openConfig, usedWindows); + + return usedWindows; + } + + private handleWaitMarkerFile(openConfig: IOpenConfiguration, usedWindows: ICodeWindow[]): void { + // 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 or loaded to a different folder so that the waiting // process can continue. We do this by deleting the waitMarkerFilePath. @@ -385,8 +402,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } })(); } - - return usedWindows; } private doOpen(