From ae7c6465f0d7cdd0aca97302a3543b77aef030db Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 11 Mar 2016 11:12:31 +0100 Subject: [PATCH] Support -w command also when code is already running --- src/vs/workbench/electron-main/env.ts | 8 ++-- src/vs/workbench/electron-main/main.ts | 53 +++++++++++++++++------ src/vs/workbench/electron-main/menus.ts | 4 +- src/vs/workbench/electron-main/windows.ts | 33 +++++++++----- 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/electron-main/env.ts b/src/vs/workbench/electron-main/env.ts index 1d0ffccdadb..1e20a9ece5d 100644 --- a/src/vs/workbench/electron-main/env.ts +++ b/src/vs/workbench/electron-main/env.ts @@ -133,8 +133,6 @@ export interface ICommandLineArguments { pathArguments?: string[]; - workers?: number; - enablePerformance?: boolean; firstrun?: boolean; @@ -146,6 +144,8 @@ export interface ICommandLineArguments { diffMode?: boolean; locale?: string; + + waitForWindowClose?: boolean; } function parseCli(): ICommandLineArguments { @@ -195,7 +195,6 @@ function parseCli(): ICommandLineArguments { return { pathArguments: pathArguments, programStart: parseNumber(args, '--timestamp', 0, 0), - workers: parseNumber(args, '--workers', -1, -1), enablePerformance: !!opts['p'], verboseLogging: !!opts['verbose'], debugPluginHostPort: debugPluginHostPort, @@ -210,7 +209,8 @@ function parseCli(): ICommandLineArguments { extensionDevelopmentPath: normalizePath(parseString(args, '--extensionDevelopmentPath')), extensionTestsPath: normalizePath(parseString(args, '--extensionTestsPath')), disableExtensions: !!opts['disableExtensions'] || !!opts['disable-extensions'], - locale: parseString(args, '--locale') + locale: parseString(args, '--locale'), + waitForWindowClose: !!opts['w'] || !!opts['wait'] }; } diff --git a/src/vs/workbench/electron-main/main.ts b/src/vs/workbench/electron-main/main.ts index deea6f5ec44..9599683145d 100644 --- a/src/vs/workbench/electron-main/main.ts +++ b/src/vs/workbench/electron-main/main.ts @@ -24,9 +24,11 @@ import {spawnSharedProcess} from 'vs/workbench/electron-main/sharedProcess'; import {Mutex} from 'windows-mutex'; export class LaunchService { - public start(args: env.ICommandLineArguments, userEnv: env.IProcessEnvironment): TPromise { + public start(args: env.ICommandLineArguments, userEnv: env.IProcessEnvironment, otherInstancePid: number): TPromise { env.log('Received data from other instance', args); + let killOtherInstance = args.waitForWindowClose; + // Otherwise handle in windows manager if (!!args.extensionDevelopmentPath) { windows.manager.openPluginDevelopmentHostWindow({ cli: args, userEnv: userEnv }); @@ -35,7 +37,25 @@ export class LaunchService { } else if (args.pathArguments.length === 0) { windows.manager.focusLastActive(args); } else { - windows.manager.open({ cli: args, userEnv: userEnv, forceNewWindow: !args.openInSameWindow }); + let usedWindows = windows.manager.open({ cli: args, userEnv: userEnv, forceNewWindow: args.waitForWindowClose || !args.openInSameWindow }); + + // If the other instance is waiting to be killed, we hook up a window listener if one window + // is being used and kill the other instance when that window is being closed + if (args.waitForWindowClose && usedWindows && usedWindows.length === 1) { + let windowToObserve = usedWindows[0]; + killOtherInstance = false; // only scenario where the "-w" switch is supported and makes sense + + let unbind = windows.onClose(id => { + if (id === windowToObserve.id) { + unbind(); + process.kill(otherInstancePid); + } + }); + } + } + + if (killOtherInstance) { + process.kill(otherInstancePid); } return TPromise.as(null); @@ -77,7 +97,10 @@ function quit(arg?: any) { } } - process.exit(exitCode); + // If not in wait mode or seeing an error: exit directly + if (!env.cliArgs.waitForWindowClose || exitCode) { + process.exit(exitCode); + } } function main(ipcServer: Server, userEnv: env.IProcessEnvironment): void { @@ -200,23 +223,27 @@ function setupIPC(): TPromise { return TPromise.wrapError(err); } - // there's a running instance, let's connect to it - return connect(env.mainIPCHandle).then( - client => { + // Since we are the second instance, we do not want to show the dock + if (platform.isMacintosh) { + app.dock.hide(); + } - // Tests from CLI require to be the only instance currently (TODO@Ben support multiple instances and output) - if (env.isTestingFromCli) { - const errorMsg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; - console.error(errorMsg); + // Tests from CLI require to be the only instance currently (TODO@Ben support multiple instances and output) + if (env.isTestingFromCli) { + const errorMsg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; + console.error(errorMsg); - return TPromise.wrapError(errorMsg); - } + return TPromise.wrapError(errorMsg); + } + // there's a running instance, let's connect to it + return connect(env.mainIPCHandle).then( + client => { env.log('Sending env to running instance...'); const service = client.getService('LaunchService', LaunchService); - return service.start(env.cliArgs, process.env) + return service.start(env.cliArgs, process.env, process.pid) .then(() => client.dispose()) .then(() => TPromise.wrapError('Sent env to running instance. Terminating...')); }, diff --git a/src/vs/workbench/electron-main/menus.ts b/src/vs/workbench/electron-main/menus.ts index 9eae8ff2315..b42e0824831 100644 --- a/src/vs/workbench/electron-main/menus.ts +++ b/src/vs/workbench/electron-main/menus.ts @@ -60,7 +60,7 @@ export class VSCodeMenu { // Listen to "open" & "close" event from window manager windows.onOpen((paths) => this.onOpen(paths)); - windows.onClose((remainingWindowCount) => this.onClose(remainingWindowCount)); + windows.onClose(_ => this.onClose(windows.manager.getWindowCount())); // Resolve keybindings when any first workbench is loaded windows.onReady((win) => this.resolveKeybindings(win)); @@ -422,7 +422,7 @@ export class VSCodeMenu { private createOpenRecentMenuItem(path:string): Electron.MenuItem { return new MenuItem({ label: path, click: () => { - let success = windows.manager.open({ cli: env.cliArgs, pathsToOpen: [path] }); + let success = !!windows.manager.open({ cli: env.cliArgs, pathsToOpen: [path] }); if (!success) { this.removeFromOpenedPathsList(path); this.updateMenu(); diff --git a/src/vs/workbench/electron-main/windows.ts b/src/vs/workbench/electron-main/windows.ts index 80c9e72f653..dc59b315d4d 100644 --- a/src/vs/workbench/electron-main/windows.ts +++ b/src/vs/workbench/electron-main/windows.ts @@ -44,7 +44,7 @@ export function onReady(clb: (win: window.VSCodeWindow) => void): () => void return () => eventEmitter.removeListener(EventTypes.READY, clb); } -export function onClose(clb: (remainingWindowCount: number) => void): () => void { +export function onClose(clb: (id: number) => void): () => void { eventEmitter.addListener(EventTypes.CLOSE, clb); return () => eventEmitter.removeListener(EventTypes.CLOSE, clb); @@ -429,8 +429,9 @@ export class WindowsManager { }); } - public open(openConfig: IOpenConfiguration): boolean { + public open(openConfig: IOpenConfiguration): window.VSCodeWindow[] { let iPathsToOpen: window.IPath[]; + let usedWindows: window.VSCodeWindow[] = []; // Find paths from provided paths if any if (openConfig.pathsToOpen && openConfig.pathsToOpen.length > 0) { @@ -463,7 +464,7 @@ export class WindowsManager { iPathsToOpen = arrays.coalesce(iPathsToOpen); if (iPathsToOpen.length === 0) { - return false; // indicate to outside that open failed + return null; // indicate to outside that open failed } } @@ -518,12 +519,15 @@ export class WindowsManager { readyWindow.send('vscode:installExtensions', { extensionsToInstall }); } }); + + usedWindows.push(lastActiveWindow); } // Otherwise open instance with files else { configuration = this.toConfiguration(openConfig.userEnv || this.initialUserEnv, openConfig.cli, null, filesToOpen, filesToCreate, filesToDiff, extensionsToInstall); - this.openInBrowserWindow(configuration, true /* new window */); + let browserWindow = this.openInBrowserWindow(configuration, true /* new window */); + usedWindows.push(browserWindow); openConfig.forceNewWindow = true; // any other folders to open must open in new window then } @@ -535,8 +539,9 @@ export class WindowsManager { // Check for existing instances let windowsOnWorkspacePath = arrays.coalesce(foldersToOpen.map((iPath) => this.findWindow(iPath.workspacePath))); if (windowsOnWorkspacePath.length > 0) { - windowsOnWorkspacePath[0].focus(); // just focus one of them - windowsOnWorkspacePath[0].ready().then((readyWindow) => { + let browserWindow = windowsOnWorkspacePath[0]; + browserWindow.focus(); // just focus one of them + browserWindow.ready().then((readyWindow) => { readyWindow.send('vscode:openFiles', { filesToOpen: filesToOpen, filesToCreate: filesToCreate, @@ -548,6 +553,8 @@ export class WindowsManager { } }); + usedWindows.push(browserWindow); + // Reset these because we handled them filesToOpen = []; filesToCreate = []; @@ -564,7 +571,8 @@ export class WindowsManager { } configuration = this.toConfiguration(openConfig.userEnv || this.initialUserEnv, openConfig.cli, folderToOpen.workspacePath, filesToOpen, filesToCreate, filesToDiff, extensionsToInstall); - this.openInBrowserWindow(configuration, openConfig.forceNewWindow, openConfig.forceNewWindow ? void 0 : openConfig.windowToUse); + let browserWindow = this.openInBrowserWindow(configuration, openConfig.forceNewWindow, openConfig.forceNewWindow ? void 0 : openConfig.windowToUse); + usedWindows.push(browserWindow); // Reset these because we handled them filesToOpen = []; @@ -580,7 +588,8 @@ export class WindowsManager { if (emptyToOpen.length > 0) { emptyToOpen.forEach(() => { let configuration = this.toConfiguration(openConfig.userEnv || this.initialUserEnv, openConfig.cli); - this.openInBrowserWindow(configuration, openConfig.forceNewWindow, openConfig.forceNewWindow ? void 0 : openConfig.windowToUse); + let browserWindow = this.openInBrowserWindow(configuration, openConfig.forceNewWindow, openConfig.forceNewWindow ? void 0 : openConfig.windowToUse); + usedWindows.push(browserWindow); openConfig.forceNewWindow = true; // any other folders to open must open in new window then }); @@ -596,7 +605,7 @@ export class WindowsManager { // Emit events iPathsToOpen.forEach((iPath) => eventEmitter.emit(EventTypes.OPEN, iPath)); - return true; + return arrays.distinct(usedWindows); } public openPluginDevelopmentHostWindow(openConfig: IOpenConfiguration): void { @@ -773,7 +782,7 @@ export class WindowsManager { return [Object.create(null)]; } - private openInBrowserWindow(configuration: window.IWindowConfiguration, forceNewWindow?: boolean, windowToUse?: window.VSCodeWindow): void { + private openInBrowserWindow(configuration: window.IWindowConfiguration, forceNewWindow?: boolean, windowToUse?: window.VSCodeWindow): window.VSCodeWindow { let vscodeWindow: window.VSCodeWindow; if (!forceNewWindow) { @@ -827,6 +836,8 @@ export class WindowsManager { vscodeWindow.load(configuration); } }); + + return vscodeWindow; } private getNewWindowState(configuration: window.IWindowConfiguration): window.IWindowState { @@ -1138,7 +1149,7 @@ export class WindowsManager { WindowsManager.WINDOWS.splice(index, 1); // Emit - eventEmitter.emit(EventTypes.CLOSE, WindowsManager.WINDOWS.length); + eventEmitter.emit(EventTypes.CLOSE, win.id); } private isPathEqual(pathA: string, pathB: string): boolean { -- GitLab