diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index eb9bd1d3ce264ef6a542026b9abf1b4033a63bd5..1e76db167f72359f44a473b98b41ad4cfb29ccf2 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -217,12 +217,12 @@ export function once(event: Event): Event { }; } -export function any(...events: Event[]): Event { +export function any(...events: Event[]): Event { let listeners = []; - const emitter = new Emitter({ + const emitter = new Emitter({ onFirstListenerAdd() { - listeners = events.map(e => e(() => emitter.fire(), null)); + listeners = events.map(e => e(r => emitter.fire(r))); }, onLastListenerRemove() { listeners = dispose(listeners); diff --git a/src/vs/code/electron-main/env.ts b/src/vs/code/electron-main/env.ts index 43bd79b713e6156f0a8ef117aa8db3bb21e96f5c..650f23f5246e1d2364a3dc7cd0bbdd31ae71c673 100644 --- a/src/vs/code/electron-main/env.ts +++ b/src/vs/code/electron-main/env.ts @@ -121,6 +121,7 @@ export class EnvService implements IEnvService { extensionDevelopmentPath: normalizePath(argv.extensionDevelopmentPath), extensionTestsPath: normalizePath(argv.extensionTestsPath), 'disable-extensions': argv['disable-extensions'], + 'open-url': argv['open-url'], locale: argv.locale, wait: argv.wait }); diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 99678232b30978d3a7e145e1775123b901ab4f24..ae604e7ab0c9201a653d159f032ae3e0a3b8d87e 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -11,6 +11,7 @@ import { VSCodeWindow } from 'vs/code/electron-main/window'; import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/code/electron-main/log'; +import { IURLService } from 'vs/platform/url/common/url'; export interface IStartArguments { args: ICommandLineArguments; @@ -52,12 +53,21 @@ export class LaunchService implements ILaunchService { constructor( @ILogService private logService: ILogService, - @IWindowsService private windowsService: IWindowsService + @IWindowsService private windowsService: IWindowsService, + @IURLService private urlService: IURLService ) {} start(args: ICommandLineArguments, userEnv: IProcessEnvironment): TPromise { this.logService.log('Received data from other instance: ', args, userEnv); + const openUrlArg = args['open-url'] || []; + const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg; + + if (openUrl.length > 0) { + openUrl.forEach(url => this.urlService.open(url)); + return TPromise.as(null); + } + // Otherwise handle in windows service let usedWindows: VSCodeWindow[]; if (!!args.extensionDevelopmentPath) { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 122121e8d6a487b63b2b52ee05940854628c87af..43a9452e3ae64c45e6c00e4f3b5f396ab53c9e31 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -40,6 +40,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; import { generateUuid } from 'vs/base/common/uuid'; import { getPathLabel } from 'vs/base/common/labels'; +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'; @@ -125,7 +126,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: IProce const electronIpcServer = new ElectronIPCServer(ipc); // Register Electron IPC services - const urlService = instantiationService.createInstance(URLService); + const urlService = accessor.get(IURLService); const urlChannel = instantiationService.createInstance(URLChannel, urlService); electronIpcServer.registerChannel('url', urlChannel); @@ -455,6 +456,7 @@ function start(): void { services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IUpdateService, new SyncDescriptor(UpdateManager)); + services.set(IURLService, new SyncDescriptor(URLService, args['open-url'])); const instantiationService = new InstantiationService(services); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 166210c03a7c4c295d9c6d81d101830e40ebb971..8cd9da04231a6fbb382f7d009f2ceb2a97806527 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -31,6 +31,7 @@ export interface ParsedArgs extends minimist.ParsedArgs { 'list-extensions'?: boolean; 'install-extension'?: string | string[]; 'uninstall-extension'?: string | string[]; + 'open-url'?: string | string[]; } const options: minimist.Opts = { @@ -43,7 +44,8 @@ const options: minimist.Opts = { 'install-extension', 'uninstall-extension', 'debugBrkPluginHost', - 'debugPluginHost' + 'debugPluginHost', + 'open-url' ], boolean: [ 'help', diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 6e762cbceaf2f4f1f12c841eb70a84e18468622d..13f7822b4b84a51148797f0d0e7c56ff6baee72e 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -14,5 +14,6 @@ export const IURLService = createDecorator(ID); export interface IURLService { _serviceBrand: any; + open(url: string): void; onOpenURL: Event; } diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index b1c165c3eaa028b6456b99e3a98028748391ab4a..26764eec43d25abb73a63897116f47c3e555faba 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -55,4 +55,8 @@ export class URLChannelClient implements IURLService { private _onOpenURL = eventFromCall(this.channel, 'event:onOpenURL', this.windowID, URIDeserializer); get onOpenURL(): Event { return this._onOpenURL; } + + open(url: string): void { + return; // not implemented + } } \ No newline at end of file diff --git a/src/vs/platform/url/electron-main/urlService.ts b/src/vs/platform/url/electron-main/urlService.ts index 993d082257173ba5c7504d8ba426b60d48d74d6c..c7930991a34641b1a90bc3e30297b982db00af4a 100644 --- a/src/vs/platform/url/electron-main/urlService.ts +++ b/src/vs/platform/url/electron-main/urlService.ts @@ -5,7 +5,7 @@ 'use strict'; -import Event, {chain, mapEvent, buffer} from 'vs/base/common/event'; +import Event, {mapEvent, chain, buffer, Emitter, any} from 'vs/base/common/event'; import {fromEventEmitter} from 'vs/base/node/event'; import {IURLService} from 'vs/platform/url/common/url'; import product from 'vs/platform/product'; @@ -16,23 +16,30 @@ export class URLService implements IURLService { _serviceBrand: any; + private openUrlEmitter: Emitter = new Emitter(); onOpenURL: Event; - constructor() { - app.setAsDefaultProtocolClient(product.urlProtocol); + constructor(initial: string | string[] = []) { + const globalBuffer = (global.getOpenUrls() || []) as string[]; + const initialBuffer = [ + ...(typeof initial === 'string' ? [initial] : initial), + ...globalBuffer + ]; + + app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url']); const rawOnOpenUrl = fromEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url })); // always prevent default and return the url as string - const onOpenUrl = mapEvent(rawOnOpenUrl, ({ event, url }) => { + const preventedOnOpenUrl = mapEvent(rawOnOpenUrl, ({ event, url }) => { event.preventDefault(); return url; }); // buffer all `onOpenUrl` events until someone starts listening - const bufferedOnOpenUrl = buffer(onOpenUrl, true, global.getOpenUrls()); + const bufferedOnOpenUrl = buffer(preventedOnOpenUrl, true, initialBuffer); - this.onOpenURL = chain(bufferedOnOpenUrl) + this.onOpenURL = chain(any(bufferedOnOpenUrl, this.openUrlEmitter.event)) .map(url => { try { return URI.parse(url); @@ -43,4 +50,8 @@ export class URLService implements IURLService { .filter(uri => !!uri) .event; } + + open(url: string): void { + this.openUrlEmitter.fire(url); + } } \ No newline at end of file