Skip to content

Commit

Permalink
ipc - lay foundation for events support
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Sep 25, 2019
1 parent 9233b63 commit 6234336
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 115 deletions.
15 changes: 15 additions & 0 deletions src/vs/base/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,18 @@ export function withNullAsUndefined<T>(x: T | null): T | undefined {
export function withUndefinedAsNull<T>(x: T | undefined): T | null {
return typeof x === 'undefined' ? null : x;
}

/**
* Allows to add a first parameter to functions of a type.
*/
export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {

// For every property
[K in keyof Target]:

// Function: add param to function
Target[K] extends (...args: any) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :

// Else: just leave as is
Target[K]
};
10 changes: 5 additions & 5 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { SimpleServiceProxyChannel } from 'vs/platform/ipc/node/simpleIpcProxy';
import { createChannelReceiver } from 'vs/platform/ipc/node/ipcChannelCreator';
import product from 'vs/platform/product/common/product';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { Disposable } from 'vs/base/common/lifecycle';
Expand Down Expand Up @@ -541,15 +541,15 @@ export class CodeApplication extends Disposable {
electronIpcServer.registerChannel('update', updateChannel);

const issueService = accessor.get(IIssueService);
const issueChannel = new SimpleServiceProxyChannel(issueService);
const issueChannel = createChannelReceiver(issueService);
electronIpcServer.registerChannel('issue', issueChannel);

const electronService = accessor.get(IElectronService);
const electronChannel = new SimpleServiceProxyChannel(electronService);
const electronChannel = createChannelReceiver(electronService);
electronIpcServer.registerChannel('electron', electronChannel);

const sharedProcessMainService = accessor.get(ISharedProcessMainService);
const sharedProcessChannel = new SimpleServiceProxyChannel(sharedProcessMainService);
const sharedProcessChannel = createChannelReceiver(sharedProcessMainService);
electronIpcServer.registerChannel('sharedProcess', sharedProcessChannel);

const workspacesMainService = accessor.get(IWorkspacesMainService);
Expand All @@ -562,7 +562,7 @@ export class CodeApplication extends Disposable {
sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));

const menubarService = accessor.get(IMenubarService);
const menubarChannel = new SimpleServiceProxyChannel(menubarService);
const menubarChannel = createChannelReceiver(menubarService);
electronIpcServer.registerChannel('menubar', menubarChannel);

const urlService = accessor.get(IURLService);
Expand Down
4 changes: 2 additions & 2 deletions src/vs/platform/electron/electron-browser/electronService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { IElectronService } from 'vs/platform/electron/node/electron';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
import { createChannelSender } from 'vs/platform/ipc/node/ipcChannelCreator';
import { IWindowService } from 'vs/platform/windows/common/windows';

export class ElectronService {
Expand All @@ -16,6 +16,6 @@ export class ElectronService {
@IMainProcessService mainProcessService: IMainProcessService,
@IWindowService windowService: IWindowService
) {
return createSimpleChannelProxy<IElectronService>(mainProcessService.getChannel('electron'), windowService.windowId);
return createChannelSender<IElectronService>(mainProcessService.getChannel('electron'), { context: windowService.windowId });
}
}
7 changes: 4 additions & 3 deletions src/vs/platform/electron/electron-main/electronMainService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Event } from 'vs/base/common/event';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu } from 'electron';
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { OpenContext, INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions, IOpenedWindow, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { isMacintosh } from 'vs/base/common/platform';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { AddContextToFunctions } from 'vs/platform/ipc/node/simpleIpcProxy';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { AddFirstParameterToFunctions } from 'vs/base/common/types';

export class ElectronMainService implements AddContextToFunctions<IElectronService, number> {
export class ElectronMainService implements AddFirstParameterToFunctions<IElectronService, Promise<any> /* only methods, not events */, number /* window ID */> {

_serviceBrand: undefined;

Expand Down
123 changes: 123 additions & 0 deletions src/vs/platform/ipc/node/ipcChannelCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Event } from 'vs/base/common/event';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { revive } from 'vs/base/common/marshalling';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { isUpperAsciiLetter } from 'vs/base/common/strings';

//
// Use both `createChannelReceiver` and `createChannelSender`
// for automated process <=> process communication over methods.
//

export interface IBaseChannelOptions {

/**
* Disables automatic marshalling of `URI`.
* If marshalling is disabled, `UriComponents`
* must be used instead.
*/
disableMarshalling?: boolean;
}

export interface IChannelReceiverOptions extends IBaseChannelOptions { }

export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel {
const handler = service as { [key: string]: unknown };
const disableMarshalling = options && options.disableMarshalling;

// Buffer any event that should be supported by
// iterating over all property keys and finding them
const mapEventNameToEvent = new Map<string, Event<unknown>>();
for (const key in handler) {
if (propertyIsEvent(key)) {
mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event<unknown>, true));
}
}

return new class implements IServerChannel {

listen<T>(_: unknown, event: string): Event<T> {
const eventImpl = mapEventNameToEvent.get(event);
if (eventImpl) {
return eventImpl as Event<T>;
}

throw new Error(`Event not found: ${event}`);
}

call(_: unknown, command: string, args?: any[]): Promise<any> {
const target = handler[command];
if (typeof target === 'function') {

// Revive unless marshalling disabled
if (!disableMarshalling && Array.isArray(args)) {
for (let i = 0; i < args.length; i++) {
args[i] = revive(args[i]);
}
}

return target.apply(handler, args);
}

throw new Error(`Method not found: ${command}`);
}
};
}

export interface IChannelSenderOptions extends IBaseChannelOptions {

/**
* If provided, will add the value of `context`
* to each method call to the target.
*/
context?: unknown;
}

export function createChannelSender<T>(channel: IChannel, options?: IChannelSenderOptions): T {
const disableMarshalling = options && options.disableMarshalling;

return new Proxy({}, {
get(_target, propKey, _receiver) {
if (typeof propKey === 'string') {

// Event
if (propertyIsEvent(propKey)) {
return channel.listen(propKey);
}

// Function
return async function (...args: any[]) {

// Add context if any
let methodArgs: any[];
if (options && !isUndefinedOrNull(options.context)) {
methodArgs = [options.context, ...args];
} else {
methodArgs = args;
}

const result = await channel.call(propKey, methodArgs);

// Revive unless marshalling disabled
if (!disableMarshalling) {
return revive(result);
}

return result;
};
}

throw new Error(`Property not found: ${String(propKey)}`);
}
}) as T;
}

function propertyIsEvent(name: string): boolean {
// Assume a property is an event if it has a form of "onSomething"
return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2));
}
101 changes: 0 additions & 101 deletions src/vs/platform/ipc/node/simpleIpcProxy.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/vs/platform/issue/electron-browser/issueService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

import { IIssueService } from 'vs/platform/issue/node/issue';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
import { createChannelSender } from 'vs/platform/ipc/node/ipcChannelCreator';

export class IssueService {

_serviceBrand: undefined;

constructor(@IMainProcessService mainProcessService: IMainProcessService) {
return createSimpleChannelProxy<IIssueService>(mainProcessService.getChannel('issue'));
return createChannelSender<IIssueService>(mainProcessService.getChannel('issue'));
}
}
4 changes: 2 additions & 2 deletions src/vs/platform/menubar/electron-browser/menubarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

import { IMenubarService } from 'vs/platform/menubar/node/menubar';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
import { createChannelSender } from 'vs/platform/ipc/node/ipcChannelCreator';

export class MenubarService {

_serviceBrand: undefined;

constructor(@IMainProcessService mainProcessService: IMainProcessService) {
return createSimpleChannelProxy<IMenubarService>(mainProcessService.getChannel('menubar'));
return createChannelSender<IMenubarService>(mainProcessService.getChannel('menubar'));
}
}

0 comments on commit 6234336

Please sign in to comment.