From be24140cfc8cceb8cbfb43fdb4c01ebb6a17ccf5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 5 Apr 2017 13:04:51 +0200 Subject: [PATCH] debt - decouple app from main code --- src/vs/code/electron-main/app.ts | 268 +++++++++++++++ src/vs/code/electron-main/main.ts | 322 +++--------------- .../electron-browser/bootstrap/index.js | 1 - 3 files changed, 320 insertions(+), 271 deletions(-) create mode 100644 src/vs/code/electron-main/app.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts new file mode 100644 index 00000000000..a90cf39b9d1 --- /dev/null +++ b/src/vs/code/electron-main/app.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { app, ipcMain as ipc, BrowserWindow } from 'electron'; +import * as platform from 'vs/base/common/platform'; +import { OpenContext } from 'vs/code/common/windows'; +import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; +import { ILifecycleService } from 'vs/code/electron-main/lifecycle'; +import { VSCodeMenu } from 'vs/code/electron-main/menus'; +import { getShellEnvironment } from 'vs/code/electron-main/shellEnv'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; +import { UpdateService } from 'vs/platform/update/electron-main/updateService'; +import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; +import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net'; +import { AskpassChannel } from 'vs/workbench/parts/git/common/gitIpc'; +import { GitAskpassService } from 'vs/workbench/parts/git/electron-main/askpassService'; +import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; +import { Mutex } from 'windows-mutex'; +import { LaunchService, LaunchChannel, ILaunchService } from './launch'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ILogService } from 'vs/code/electron-main/log'; +import { IStorageService } from 'vs/code/electron-main/storage'; +import { IBackupMainService } from 'vs/platform/backup/common/backup'; +import { BackupChannel } from 'vs/platform/backup/common/backupIpc'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IURLService } from 'vs/platform/url/common/url'; +import { URLChannel } from 'vs/platform/url/common/urlIpc'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { resolveCommonProperties, machineIdStorageKey, machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties'; +import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { IDisposable, dispose } from "vs/base/common/lifecycle"; +import { ConfigurationService } from "vs/platform/configuration/node/configurationService"; +import { TPromise } from "vs/base/common/winjs.base"; + +export class VSCodeApplication { + private toDispose: IDisposable[]; + private windowsMainService: IWindowsMainService; + + private electronIpcServer: ElectronIPCServer; + + private sharedProcess: SharedProcess; + private sharedProcessClient: TPromise; + + constructor( + private mainIpcServer: Server, + private userEnv: platform.IProcessEnvironment, + @IInstantiationService private instantiationService: IInstantiationService, + @ILogService private logService: ILogService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ILifecycleService private lifecycleService: ILifecycleService, + @IConfigurationService private configurationService: ConfigurationService, + @IStorageService private storageService: IStorageService + ) { + this.toDispose = [mainIpcServer, configurationService]; + + this.registerListeners(); + } + + private registerListeners(): void { + + // We handle uncaught exceptions here to prevent electron from opening a dialog to the user + process.on('uncaughtException', (err: any) => { + if (err) { + + // take only the message and stack property + const friendlyError = { + message: err.message, + stack: err.stack + }; + + // handle on client side + if (this.windowsMainService) { + this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); + } + } + + console.error('[uncaught exception in main]: ' + err); + if (err.stack) { + console.error(err.stack); + } + }); + + ipc.on(machineIdIpcChannel, (event, machineId: string) => { + this.logService.log('IPC#vscode-machineId'); + this.storageService.setItem(machineIdStorageKey, machineId); + }); + + ipc.on('vscode:fetchShellEnv', (event, windowId) => { + const win = BrowserWindow.fromId(windowId); + getShellEnvironment().then(shellEnv => { + win.webContents.send('vscode:acceptShellEnv', shellEnv); + }, err => { + win.webContents.send('vscode:acceptShellEnv', {}); + console.error('Error fetching shell env', err); + }); + }); + + app.on('will-quit', () => { + this.logService.log('App#will-quit: disposing resources'); + + this.dispose(); + }); + + ipc.on('vscode:exit', (event, code: number) => { + this.logService.log('IPC#vscode:exit', code); + + this.dispose(); + this.lifecycleService.kill(code); + }); + } + + public startup(): void { + this.logService.log('Starting VS Code in verbose mode'); + this.logService.log(`from: ${this.environmentService.appRoot}`); + this.logService.log('args:', this.environmentService.args); + + // Setup Windows mutex + let windowsMutex: Mutex = null; + if (platform.isWindows) { + try { + const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex; + windowsMutex = new Mutex(product.win32MutexName); + this.toDispose.push({ dispose: () => windowsMutex.release() }); + } catch (e) { + // noop + } + } + + // Make sure we associate the program with the app user model id + // This will help Windows to associate the running program with + // any shortcut that is pinned to the taskbar and prevent showing + // two icons in the taskbar for the same app. + if (platform.isWindows && product.win32AppUserModelId) { + app.setAppUserModelId(product.win32AppUserModelId); + } + + // Register Main IPC connections + const askpassService = new GitAskpassService(); + const askpassChannel = new AskpassChannel(askpassService); + this.mainIpcServer.registerChannel('askpass', askpassChannel); + + // Create Electron IPC Server + this.electronIpcServer = new ElectronIPCServer(); + + // Spawn shared process + this.sharedProcess = new SharedProcess(this.environmentService, this.userEnv); + this.toDispose.push(this.sharedProcess); + this.sharedProcessClient = this.sharedProcess.whenReady() + .then(() => connect(this.environmentService.sharedIPCHandle, 'main')); + + // Services + const appInstantiationService = this.initServices(); + + // Do Startup + appInstantiationService.invokeFunction(accessor => this.doStartup(accessor)); + } + + private initServices(): IInstantiationService { + const services = new ServiceCollection(); + + services.set(IUpdateService, new SyncDescriptor(UpdateService)); + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager)); + services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess)); + services.set(ILaunchService, new SyncDescriptor(LaunchService)); + + // Telemtry + if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { + const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); + const appender = new TelemetryAppenderClient(channel); + const commonProperties = resolveCommonProperties(product.commit, pkg.version) + .then(result => Object.defineProperty(result, 'common.machineId', { + get: () => this.storageService.getItem(machineIdStorageKey), + enumerable: true + })); + const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + return this.instantiationService.createChild(services); + } + + private doStartup(accessor: ServicesAccessor): void { + const appInstantiationService = accessor.get(IInstantiationService); + + // TODO@Joao: unfold this + this.windowsMainService = accessor.get(IWindowsMainService); + + // TODO@Joao: so ugly... + this.windowsMainService.onWindowClose(() => { + if (!platform.isMacintosh && this.windowsMainService.getWindowCount() === 0) { + this.sharedProcess.dispose(); + } + }); + + // Register more Main IPC services + const launchService = accessor.get(ILaunchService); + const launchChannel = new LaunchChannel(launchService); + this.mainIpcServer.registerChannel('launch', launchChannel); + + // Register more Electron IPC services + const updateService = accessor.get(IUpdateService); + const updateChannel = new UpdateChannel(updateService); + this.electronIpcServer.registerChannel('update', updateChannel); + + const urlService = accessor.get(IURLService); + const urlChannel = appInstantiationService.createInstance(URLChannel, urlService); + this.electronIpcServer.registerChannel('url', urlChannel); + + const backupService = accessor.get(IBackupMainService); + const backupChannel = appInstantiationService.createInstance(BackupChannel, backupService); + this.electronIpcServer.registerChannel('backup', backupChannel); + + const windowsService = accessor.get(IWindowsService); + const windowsChannel = new WindowsChannel(windowsService); + this.electronIpcServer.registerChannel('windows', windowsChannel); + this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel)); + + // Lifecycle + this.lifecycleService.ready(); + + // Propagate to clients + this.windowsMainService.ready(this.userEnv); + + // Open our first window + const args = this.environmentService.args; + const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + if (args['new-window'] && args._.length === 0) { + this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths + } else if (global.macOpenFiles && global.macOpenFiles.length && (!args._ || !args._.length)) { + this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup + } else { + this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!args._.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli + } + + // Install Menu + appInstantiationService.createInstance(VSCodeMenu); + + // Jump List + this.windowsMainService.updateWindowsJumpList(); + this.windowsMainService.onRecentPathsChange(() => this.windowsMainService.updateWindowsJumpList()); + + // Start shared process here + this.sharedProcess.spawn(); + } + + private dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 90b77b0a742..b255a1fcb8f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -5,31 +5,16 @@ 'use strict'; -import { app, ipcMain as ipc, BrowserWindow } from 'electron'; +import { app } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { parseMainProcessArgv } from 'vs/platform/environment/node/argv'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/electron-main/paths'; -import { OpenContext } from 'vs/code/common/windows'; -import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; -import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { LifecycleService, ILifecycleService } from 'vs/code/electron-main/lifecycle'; -import { VSCodeMenu } from 'vs/code/electron-main/menus'; -import { getShellEnvironment } from 'vs/code/electron-main/shellEnv'; -import { IUpdateService } from 'vs/platform/update/common/update'; -import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; -import { UpdateService } from 'vs/platform/update/electron-main/updateService'; -import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; -import { AskpassChannel } from 'vs/workbench/parts/git/common/gitIpc'; -import { GitAskpassService } from 'vs/workbench/parts/git/electron-main/askpassService'; -import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; -import { Mutex } from 'windows-mutex'; -import { LaunchService, ILaunchChannel, LaunchChannel, LaunchChannelClient, ILaunchService } from './launch'; +import { ILaunchChannel, LaunchChannelClient } from './launch'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -37,7 +22,6 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, MainLogService } from 'vs/code/electron-main/log'; import { IStorageService, StorageService } from 'vs/code/electron-main/storage'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; -import { BackupChannel } from 'vs/platform/backup/common/backupIpc'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; @@ -46,238 +30,52 @@ import { ConfigurationService } from 'vs/platform/configuration/node/configurati import { IRequestService } from 'vs/platform/request/node/request'; import { RequestService } from 'vs/platform/request/electron-main/requestService'; import { IURLService } from 'vs/platform/url/common/url'; -import { URLChannel } from 'vs/platform/url/common/urlIpc'; import { URLService } from 'vs/platform/url/electron-main/urlService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; -import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; -import { resolveCommonProperties, machineIdStorageKey, machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties'; -import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; -import product from 'vs/platform/node/product'; -import pkg from 'vs/platform/node/package'; import * as fs from 'original-fs'; +import { VSCodeApplication } from "vs/code/electron-main/app"; -ipc.on('vscode:fetchShellEnv', (event, windowId) => { - const win = BrowserWindow.fromId(windowId); - getShellEnvironment().then(shellEnv => { - win.webContents.send('vscode:acceptShellEnv', shellEnv); - }, err => { - win.webContents.send('vscode:acceptShellEnv', {}); - console.error('Error fetching shell env', err); - }); -}); - -function quit(accessor: ServicesAccessor, errorOrMessage?: Error | string): void { - const logService = accessor.get(ILogService); - const lifecycleService = accessor.get(ILifecycleService); - - let exitCode = 0; - if (typeof errorOrMessage === 'string') { - logService.log(errorOrMessage); - } else if (errorOrMessage) { - exitCode = 1; // signal error to the outside - if (errorOrMessage.stack) { - console.error(errorOrMessage.stack); - } else { - console.error('Startup error: ' + errorOrMessage.toString()); - } - } - - lifecycleService.kill(exitCode); -} - -// TODO@Joao wow this is huge, clean up! -function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platform.IProcessEnvironment): void { - const instantiationService = accessor.get(IInstantiationService); - const logService = accessor.get(ILogService); - const environmentService = accessor.get(IEnvironmentService); - const lifecycleService = accessor.get(ILifecycleService); - const configurationService = accessor.get(IConfigurationService) as ConfigurationService; - const storageService = accessor.get(IStorageService); - let windowsMainService: IWindowsMainService; - - // We handle uncaught exceptions here to prevent electron from opening a dialog to the user - process.on('uncaughtException', (err: any) => { - if (err) { - - // take only the message and stack property - const friendlyError = { - message: err.message, - stack: err.stack - }; - - // handle on client side - if (windowsMainService) { - windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); - } - } - - console.error('[uncaught exception in main]: ' + err); - if (err.stack) { - console.error(err.stack); - } - }); - - ipc.on(machineIdIpcChannel, (event, machineId: string) => { - logService.log('IPC#vscode-machineId'); - storageService.setItem(machineIdStorageKey, machineId); - }); - - logService.log('Starting VS Code in verbose mode'); - logService.log(`from: ${environmentService.appRoot}`); - logService.log('args:', environmentService.args); - - // Setup Windows mutex - let windowsMutex: Mutex = null; - if (platform.isWindows) { - try { - const Mutex = (require.__$__nodeRequire('windows-mutex')).Mutex; - windowsMutex = new Mutex(product.win32MutexName); - } catch (e) { - // noop - } - } - - // Register Main IPC services - const askpassService = new GitAskpassService(); - const askpassChannel = new AskpassChannel(askpassService); - mainIpcServer.registerChannel('askpass', askpassChannel); - - // Create Electron IPC Server - const electronIpcServer = new ElectronIPCServer(); - - // Spawn shared process - const sharedProcess = new SharedProcess(environmentService, userEnv); - const sharedProcessClient = sharedProcess.whenReady() - .then(() => connect(environmentService.sharedIPCHandle, 'main')); - - // Create a new service collection, because the telemetry service - // requires a connection to shared process, which was only established - // now. - const services = new ServiceCollection(); +// +// Main Startup Sequence +// +(function () { + let args: ParsedArgs; - services.set(IUpdateService, new SyncDescriptor(UpdateService)); - services.set(IWindowsMainService, new SyncDescriptor(WindowsManager)); - services.set(IWindowsService, new SyncDescriptor(WindowsService, sharedProcess)); - services.set(ILaunchService, new SyncDescriptor(LaunchService)); + try { + args = parseMainProcessArgv(process.argv); + args = validatePaths(args); + } catch (err) { + console.error(err.message); + app.exit(1); - if (environmentService.isBuilt && !environmentService.isExtensionDevelopment && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); - const appender = new TelemetryAppenderClient(channel); - const commonProperties = resolveCommonProperties(product.commit, pkg.version) - .then(result => Object.defineProperty(result, 'common.machineId', { - get: () => storageService.getItem(machineIdStorageKey), - enumerable: true - })); - const piiPaths = [environmentService.appRoot, environmentService.extensionsPath]; - const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); - } else { - services.set(ITelemetryService, NullTelemetryService); + return; } - const instantiationService2 = instantiationService.createChild(services); - - instantiationService2.invokeFunction(accessor => { - // TODO@Joao: unfold this - windowsMainService = accessor.get(IWindowsMainService); - - // TODO@Joao: so ugly... - windowsMainService.onWindowClose(() => { - if (!platform.isMacintosh && windowsMainService.getWindowCount() === 0) { - sharedProcess.dispose(); - } - }); - - // Register more Main IPC services - const launchService = accessor.get(ILaunchService); - const launchChannel = new LaunchChannel(launchService); - mainIpcServer.registerChannel('launch', launchChannel); - - // Register more Electron IPC services - const updateService = accessor.get(IUpdateService); - const updateChannel = new UpdateChannel(updateService); - electronIpcServer.registerChannel('update', updateChannel); - - const urlService = accessor.get(IURLService); - const urlChannel = instantiationService2.createInstance(URLChannel, urlService); - electronIpcServer.registerChannel('url', urlChannel); - - const backupService = accessor.get(IBackupMainService); - const backupChannel = instantiationService2.createInstance(BackupChannel, backupService); - electronIpcServer.registerChannel('backup', backupChannel); - - const windowsService = accessor.get(IWindowsService); - const windowsChannel = new WindowsChannel(windowsService); - electronIpcServer.registerChannel('windows', windowsChannel); - sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel)); - - // Make sure we associate the program with the app user model id - // This will help Windows to associate the running program with - // any shortcut that is pinned to the taskbar and prevent showing - // two icons in the taskbar for the same app. - if (platform.isWindows && product.win32AppUserModelId) { - app.setAppUserModelId(product.win32AppUserModelId); - } - - function dispose() { - if (mainIpcServer) { - mainIpcServer.dispose(); - mainIpcServer = null; - } - - if (windowsMutex) { - windowsMutex.release(); - } - - configurationService.dispose(); - sharedProcess.dispose(); - } - - // Dispose on app quit - app.on('will-quit', () => { - logService.log('App#will-quit: disposing resources'); - - dispose(); - }); - - // Dispose on vscode:exit - ipc.on('vscode:exit', (event, code: number) => { - logService.log('IPC#vscode:exit', code); - - dispose(); - lifecycleService.kill(code); - }); - - // Lifecycle - lifecycleService.ready(); - - // Propagate to clients - windowsMainService.ready(userEnv); - - // Open our first window - const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; - if (environmentService.args['new-window'] && environmentService.args._.length === 0) { - windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths - } else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) { - windowsMainService.open({ context: OpenContext.DOCK, cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup - } else { - windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'] || (!environmentService.args._.length && environmentService.args['unity-launch']), diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli - } + const instantiationService = createServices(args); - // Install Menu - instantiationService2.createInstance(VSCodeMenu); + return instantiationService.invokeFunction(accessor => { - // Jump List - windowsMainService.updateWindowsJumpList(); - windowsMainService.onRecentPathsChange(() => windowsMainService.updateWindowsJumpList()); + // Patch `process.env` with the instance's environment + const environmentService = accessor.get(IEnvironmentService); + const instanceEnv: typeof process.env = { + VSCODE_PID: String(process.pid), + VSCODE_IPC_HOOK: environmentService.mainIPCHandle, + VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'] + }; + assign(process.env, instanceEnv); - // Start shared process here - sharedProcess.spawn(); - }); -} + // Startup + return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService))) + .then(() => instantiationService.invokeFunction(setupIPC)) + .then(mainIpcServer => { + const app = instantiationService.createInstance(VSCodeApplication, mainIpcServer, instanceEnv); + app.startup(); + }); + }).done(null, err => instantiationService.invokeFunction(quit, err)); +})(); +// +// Helpers +// function setupIPC(accessor: ServicesAccessor): TPromise { const logService = accessor.get(ILogService); const environmentService = accessor.get(IEnvironmentService); @@ -364,7 +162,6 @@ function setupIPC(accessor: ServicesAccessor): TPromise { return setup(true); } - function createPaths(environmentService: IEnvironmentService): TPromise { const paths = [ environmentService.appSettingsHome, @@ -389,36 +186,21 @@ function createServices(args: ParsedArgs): IInstantiationService { return new InstantiationService(services, true); } -function start(): void { - let args: ParsedArgs; - - try { - args = parseMainProcessArgv(process.argv); - args = validatePaths(args); - } catch (err) { - console.error(err.message); - app.exit(1); +function quit(accessor: ServicesAccessor, errorOrMessage?: Error | string): void { + const logService = accessor.get(ILogService); + const lifecycleService = accessor.get(ILifecycleService); - return; + let exitCode = 0; + if (typeof errorOrMessage === 'string') { + logService.log(errorOrMessage); + } else if (errorOrMessage) { + exitCode = 1; // signal error to the outside + if (errorOrMessage.stack) { + console.error(errorOrMessage.stack); + } else { + console.error('Startup error: ' + errorOrMessage.toString()); + } } - const instantiationService = createServices(args); - - return instantiationService.invokeFunction(accessor => { - const environmentService = accessor.get(IEnvironmentService); - const instanceEnv: typeof process.env = { - VSCODE_PID: String(process.pid), - VSCODE_IPC_HOOK: environmentService.mainIPCHandle, - VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'] - }; - - // Patch `process.env` with the instance's environment - assign(process.env, instanceEnv); - - return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService))) - .then(() => instantiationService.invokeFunction(setupIPC)) - .then(mainIpcServer => instantiationService.invokeFunction(main, mainIpcServer, instanceEnv)); - }).done(null, err => instantiationService.invokeFunction(quit, err)); -} - -start(); + lifecycleService.kill(exitCode); +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index b70fb15eeb0..f2ebeed20cf 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -19,7 +19,6 @@ const electron = require('electron'); const remote = electron.remote; const ipc = electron.ipcRenderer; - process.lazyEnv = new Promise(function (resolve) { ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { assign(process.env, shellEnv); -- GitLab