wip: url service

This commit is contained in:
Joao Moreno 2016-08-04 11:51:40 +02:00
parent dccf029ef6
commit fe33ef722a
8 changed files with 118 additions and 61 deletions

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { Server as IPCServer, Client as IPCClient, IServer, IClient, IChannel } from 'vs/base/parts/ipc/common/ipc';
@ -12,24 +11,26 @@ const Hello = 'ipc:hello';
const Goodbye = 'ipc:goodbye';
const Message = 'ipc:message';
export interface IPC extends NodeJS.EventEmitter {
export interface Sender {
send(channel: string, ...args: any[]): void;
}
export interface IPC extends Sender, NodeJS.EventEmitter {}
class Protocol implements IMessagePassingProtocol {
private listener: IDisposable;
constructor(private ipc: IPC) {}
constructor(private sender: Sender, private receiver: NodeJS.EventEmitter) {}
send(message: any): void {
this.ipc.send(Message, message);
this.sender.send(Message, message);
}
onMessage(callback: (message: any) => void): void {
const cb = (_, m) => callback(m);
this.ipc.on(Message, cb);
this.listener = toDisposable(() => this.ipc.removeListener(Message, cb));
this.receiver.on(Message, cb);
this.listener = toDisposable(() => this.receiver.removeListener(Message, cb));
}
dispose(): void {
@ -41,11 +42,11 @@ export class Server implements IServer, IDisposable {
private channels: { [name: string]: IChannel };
constructor(ipc: IPC) {
constructor(ipc: NodeJS.EventEmitter) {
this.channels = Object.create(null);
ipc.on(Hello, ({ sender }) => {
const protocol = new Protocol(sender);
const protocol = new Protocol(sender, ipc);
const ipcServer = new IPCServer(protocol);
Object.keys(this.channels)
@ -55,8 +56,6 @@ export class Server implements IServer, IDisposable {
ipcServer.dispose();
protocol.dispose();
});
sender.send(Hello);
});
}
@ -75,7 +74,8 @@ export class Client implements IClient, IDisposable {
private ipcClient: IPCClient;
constructor(private ipc: IPC) {
this.protocol = new Protocol(ipc);
ipc.send(Hello);
this.protocol = new Protocol(ipc, ipc);
this.ipcClient = new IPCClient(this.protocol);
}
@ -87,16 +87,4 @@ export class Client implements IClient, IDisposable {
this.ipc.send(Goodbye);
this.protocol = dispose(this.protocol);
}
}
export function connect(ipc: IPC): TPromise<Client> {
return new TPromise<Client>((c, e) => {
ipc.once(Hello, () => {
ipc.removeListener('error', e);
c(new Client(ipc));
});
ipc.once('error', e);
ipc.send(Hello);
});
}

View file

@ -16,6 +16,7 @@ import { ILifecycleService, LifecycleService } from 'vs/code/electron-main/lifec
import { VSCodeMenu } from 'vs/code/electron-main/menus';
import { ISettingsService, SettingsManager } from 'vs/code/electron-main/settings';
import { IUpdateService, UpdateManager } from 'vs/code/electron-main/update-manager';
import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/common/ipc.electron';
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';
@ -31,6 +32,8 @@ import { ILogService, MainLogService } from 'vs/code/electron-main/log';
import { IStorageService, StorageService } from 'vs/code/electron-main/storage';
import * as cp from 'child_process';
import { generateUuid } from 'vs/base/common/uuid';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/electron-main/urlService';
function quit(accessor: ServicesAccessor, error?: Error);
function quit(accessor: ServicesAccessor, message?: string);
@ -52,7 +55,7 @@ function quit(accessor: ServicesAccessor, arg?: any) {
process.exit(exitCode); // in main, process.exit === app.exit
}
function main(accessor: ServicesAccessor, ipcServer: Server, userEnv: IProcessEnvironment): void {
function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: IProcessEnvironment): void {
const instantiationService = accessor.get(IInstantiationService);
const logService = accessor.get(ILogService);
const envService = accessor.get(IEnvironmentService);
@ -93,14 +96,22 @@ function main(accessor: ServicesAccessor, ipcServer: Server, userEnv: IProcessEn
// noop
}
// Register IPC services
// Register Main IPC services
const launchService = instantiationService.createInstance(LaunchService);
const launchChannel = new LaunchChannel(launchService);
ipcServer.registerChannel('launch', launchChannel);
mainIpcServer.registerChannel('launch', launchChannel);
const askpassService = new GitAskpassService();
const askpassChannel = new AskpassChannel(askpassService);
ipcServer.registerChannel('askpass', askpassChannel);
mainIpcServer.registerChannel('askpass', askpassChannel);
// Create Electron IPC Server
const electronIpcServer = new ElectronIPCServer(ipc);
// Register Electron IPC services
const urlService = instantiationService.createInstance(URLService);
const urlChannel = new URLChannel(urlService);
electronIpcServer.registerChannel('url', urlChannel);
// Used by sub processes to communicate back to the main instance
process.env['VSCODE_PID'] = '' + process.pid;
@ -125,9 +136,9 @@ function main(accessor: ServicesAccessor, ipcServer: Server, userEnv: IProcessEn
global.programStart = envService.cliArgs.programStart;
function dispose() {
if (ipcServer) {
ipcServer.dispose();
ipcServer = null;
if (mainIpcServer) {
mainIpcServer.dispose();
mainIpcServer = null;
}
sharedProcess.dispose();
@ -144,12 +155,6 @@ function main(accessor: ServicesAccessor, ipcServer: Server, userEnv: IProcessEn
dispose();
});
app.setAsDefaultProtocolClient('vscode');
app.on('open-url', url => {
console.log(url);
});
// Dispose on vscode:exit
ipc.on('vscode:exit', (event, code: number) => {
logService.log('IPC#vscode:exit', code);
@ -356,6 +361,6 @@ getUserEnvironment()
return instantiationService.invokeFunction(a => a.get(IEnvironmentService).createPaths())
.then(() => instantiationService.invokeFunction(setupIPC))
.then(ipcServer => instantiationService.invokeFunction(main, ipcServer, userEnv));
.then(mainIpcServer => instantiationService.invokeFunction(main, mainIpcServer, userEnv));
})
.done(null, err => instantiationService.invokeFunction(quit, err));

View file

@ -12,5 +12,6 @@ export const ID = 'urlService';
export const IURLService = createDecorator<IURLService>(ID);
export interface IURLService {
onOpenUrl: Event<string>;
_serviceBrand: any;
onOpenURL: Event<string>;
}

View file

@ -3,4 +3,35 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
import { IURLService } from './url';
import Event from 'vs/base/common/event';
export interface IURLChannel extends IChannel {
call(command: 'event:onOpenURL'): TPromise<void>;
call(command: string, arg: any): TPromise<any>;
}
export class URLChannel implements IURLChannel {
constructor(private service: IURLService) { }
call(command: string, arg: any): TPromise<any> {
switch (command) {
case 'event:onOpenURL': return eventToCall(this.service.onOpenURL);
}
}
}
export class URLChannelClient implements IURLService {
_serviceBrand: any;
constructor(private channel: IChannel) { }
private _onOpenURL = eventFromCall<string>(this.channel, 'event:onOpenURL');
get onOpenURL(): Event<string> { return this._onOpenURL; }
}

View file

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* 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 Event, {Emitter} from 'vs/base/common/event';
import {IDisposable, dispose, toDisposable} from 'vs/base/common/lifecycle';
import {IURLService} from 'vs/platform/url/common/url';
import {app} from 'electron';
export class URLService implements IURLService, IDisposable {
_serviceBrand: any;
private _onOpenURL = new Emitter<string>();
onOpenURL: Event<string> = this._onOpenURL.event;
private disposables: IDisposable[] = [];
constructor() {
const handler = (e: Electron.Event, url: string) => {
e.preventDefault();
this._onOpenURL.fire(url);
};
app.on('open-url', handler);
this.disposables.push(toDisposable(() => app.removeListener('open-url', handler)));
// app.setAsDefaultProtocolClient('vscode');
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View file

@ -1,19 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 Event, {Emitter} from 'vs/base/common/event';
import {IURLService} from 'vs/platform/url/common/url';
export class URLService implements IURLService {
private _onOpenUrl = new Emitter<string>();
onOpenUrl: Event<string> = this._onOpenUrl.event;
constructor() {
}
}

View file

@ -70,9 +70,13 @@ import {CrashReporter} from 'vs/workbench/electron-browser/crashReporter';
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {ThemeService} from 'vs/workbench/services/themes/electron-browser/themeService';
import {getDelayedChannel} from 'vs/base/parts/ipc/common/ipc';
import {connect} from 'vs/base/parts/ipc/node/ipc.net';
import {connect as connectNet} from 'vs/base/parts/ipc/node/ipc.net';
import {Client as ElectronIPCClient} from 'vs/base/parts/ipc/common/ipc.electron';
import {ipcRenderer} from 'electron';
import {IExtensionManagementChannel, ExtensionManagementChannelClient} from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import {IExtensionManagementService} from 'vs/platform/extensionManagement/common/extensionManagement';
import {URLChannelClient} from 'vs/platform/url/common/urlIpc';
import {IURLService} from 'vs/platform/url/common/url';
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';
// self registering services
@ -202,7 +206,7 @@ export class WorkbenchShell {
}
private initServiceCollection(): [InstantiationService, ServiceCollection] {
const sharedProcess = connect(process.env['VSCODE_SHARED_IPC_HOOK']);
const sharedProcess = connectNet(process.env['VSCODE_SHARED_IPC_HOOK']);
sharedProcess.done(service => {
service.onClose(() => {
this.messageService.show(Severity.Error, {
@ -212,6 +216,8 @@ export class WorkbenchShell {
});
}, errors.onUnexpectedError);
const mainProcessClient = new ElectronIPCClient(ipcRenderer);
const serviceCollection = new ServiceCollection();
serviceCollection.set(IEventService, this.eventService);
serviceCollection.set(IWorkspaceContextService, this.contextService);
@ -314,6 +320,10 @@ export class WorkbenchShell {
const extensionManagementChannelClient = instantiationService.createInstance(ExtensionManagementChannelClient, extensionManagementChannel);
serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient);
const urlChannel = mainProcessClient.getChannel('url');
const urlChannelClient = instantiationService.createInstance(URLChannelClient, urlChannel);
serviceCollection.set(IURLService, urlChannelClient);
return [instantiationService, serviceCollection];
}

View file

@ -17,6 +17,7 @@ import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common
import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityService';
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
import { ipcRenderer as ipc } from 'electron';
import { IURLService } from 'vs/platform/url/common/url';
interface IInstallExtensionsRequest {
extensionsToInstall: string[];
@ -31,7 +32,8 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
@IMessageService private messageService: IMessageService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IExtensionTipsService extenstionTips: IExtensionTipsService, // this is to eagerly start the service
@IExtensionGalleryService galleryService: IExtensionGalleryService
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@IURLService urlService: IURLService
) {
this.registerListeners();
@ -40,6 +42,9 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
if (options.extensionsToInstall && options.extensionsToInstall.length) {
this.install(options.extensionsToInstall).done(null, onUnexpectedError);
}
urlService.onOpenURL(url => console.log(url));
//actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallExtensionAction, InstallExtensionAction.ID, InstallExtensionAction.LABEL), 'Extensions: Install Extension', ExtensionsLabel);
}