Skip to content

Commit 6234336

Browse files
author
Benjamin Pasero
committed
ipc - lay foundation for events support
1 parent 9233b63 commit 6234336

8 files changed

Lines changed: 153 additions & 115 deletions

File tree

src/vs/base/common/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,18 @@ export function withNullAsUndefined<T>(x: T | null): T | undefined {
207207
export function withUndefinedAsNull<T>(x: T | undefined): T | null {
208208
return typeof x === 'undefined' ? null : x;
209209
}
210+
211+
/**
212+
* Allows to add a first parameter to functions of a type.
213+
*/
214+
export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {
215+
216+
// For every property
217+
[K in keyof Target]:
218+
219+
// Function: add param to function
220+
Target[K] extends (...args: any) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
221+
222+
// Else: just leave as is
223+
Target[K]
224+
};

src/vs/code/electron-main/app.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc
3434
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
3535
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
3636
import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
37-
import { SimpleServiceProxyChannel } from 'vs/platform/ipc/node/simpleIpcProxy';
37+
import { createChannelReceiver } from 'vs/platform/ipc/node/ipcChannelCreator';
3838
import product from 'vs/platform/product/common/product';
3939
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
4040
import { Disposable } from 'vs/base/common/lifecycle';
@@ -541,15 +541,15 @@ export class CodeApplication extends Disposable {
541541
electronIpcServer.registerChannel('update', updateChannel);
542542

543543
const issueService = accessor.get(IIssueService);
544-
const issueChannel = new SimpleServiceProxyChannel(issueService);
544+
const issueChannel = createChannelReceiver(issueService);
545545
electronIpcServer.registerChannel('issue', issueChannel);
546546

547547
const electronService = accessor.get(IElectronService);
548-
const electronChannel = new SimpleServiceProxyChannel(electronService);
548+
const electronChannel = createChannelReceiver(electronService);
549549
electronIpcServer.registerChannel('electron', electronChannel);
550550

551551
const sharedProcessMainService = accessor.get(ISharedProcessMainService);
552-
const sharedProcessChannel = new SimpleServiceProxyChannel(sharedProcessMainService);
552+
const sharedProcessChannel = createChannelReceiver(sharedProcessMainService);
553553
electronIpcServer.registerChannel('sharedProcess', sharedProcessChannel);
554554

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

564564
const menubarService = accessor.get(IMenubarService);
565-
const menubarChannel = new SimpleServiceProxyChannel(menubarService);
565+
const menubarChannel = createChannelReceiver(menubarService);
566566
electronIpcServer.registerChannel('menubar', menubarChannel);
567567

568568
const urlService = accessor.get(IURLService);

src/vs/platform/electron/electron-browser/electronService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

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

1111
export class ElectronService {
@@ -16,6 +16,6 @@ export class ElectronService {
1616
@IMainProcessService mainProcessService: IMainProcessService,
1717
@IWindowService windowService: IWindowService
1818
) {
19-
return createSimpleChannelProxy<IElectronService>(mainProcessService.getChannel('electron'), windowService.windowId);
19+
return createChannelSender<IElectronService>(mainProcessService.getChannel('electron'), { context: windowService.windowId });
2020
}
2121
}

src/vs/platform/electron/electron-main/electronMainService.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { Event } from 'vs/base/common/event';
67
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
7-
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu } from 'electron';
8+
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron';
89
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
910
import { OpenContext, INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions, IOpenedWindow, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
1011
import { isMacintosh } from 'vs/base/common/platform';
1112
import { IElectronService } from 'vs/platform/electron/node/electron';
1213
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
13-
import { AddContextToFunctions } from 'vs/platform/ipc/node/simpleIpcProxy';
1414
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
15+
import { AddFirstParameterToFunctions } from 'vs/base/common/types';
1516

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

1819
_serviceBrand: undefined;
1920

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Event } from 'vs/base/common/event';
7+
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
8+
import { revive } from 'vs/base/common/marshalling';
9+
import { isUndefinedOrNull } from 'vs/base/common/types';
10+
import { isUpperAsciiLetter } from 'vs/base/common/strings';
11+
12+
//
13+
// Use both `createChannelReceiver` and `createChannelSender`
14+
// for automated process <=> process communication over methods.
15+
//
16+
17+
export interface IBaseChannelOptions {
18+
19+
/**
20+
* Disables automatic marshalling of `URI`.
21+
* If marshalling is disabled, `UriComponents`
22+
* must be used instead.
23+
*/
24+
disableMarshalling?: boolean;
25+
}
26+
27+
export interface IChannelReceiverOptions extends IBaseChannelOptions { }
28+
29+
export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel {
30+
const handler = service as { [key: string]: unknown };
31+
const disableMarshalling = options && options.disableMarshalling;
32+
33+
// Buffer any event that should be supported by
34+
// iterating over all property keys and finding them
35+
const mapEventNameToEvent = new Map<string, Event<unknown>>();
36+
for (const key in handler) {
37+
if (propertyIsEvent(key)) {
38+
mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event<unknown>, true));
39+
}
40+
}
41+
42+
return new class implements IServerChannel {
43+
44+
listen<T>(_: unknown, event: string): Event<T> {
45+
const eventImpl = mapEventNameToEvent.get(event);
46+
if (eventImpl) {
47+
return eventImpl as Event<T>;
48+
}
49+
50+
throw new Error(`Event not found: ${event}`);
51+
}
52+
53+
call(_: unknown, command: string, args?: any[]): Promise<any> {
54+
const target = handler[command];
55+
if (typeof target === 'function') {
56+
57+
// Revive unless marshalling disabled
58+
if (!disableMarshalling && Array.isArray(args)) {
59+
for (let i = 0; i < args.length; i++) {
60+
args[i] = revive(args[i]);
61+
}
62+
}
63+
64+
return target.apply(handler, args);
65+
}
66+
67+
throw new Error(`Method not found: ${command}`);
68+
}
69+
};
70+
}
71+
72+
export interface IChannelSenderOptions extends IBaseChannelOptions {
73+
74+
/**
75+
* If provided, will add the value of `context`
76+
* to each method call to the target.
77+
*/
78+
context?: unknown;
79+
}
80+
81+
export function createChannelSender<T>(channel: IChannel, options?: IChannelSenderOptions): T {
82+
const disableMarshalling = options && options.disableMarshalling;
83+
84+
return new Proxy({}, {
85+
get(_target, propKey, _receiver) {
86+
if (typeof propKey === 'string') {
87+
88+
// Event
89+
if (propertyIsEvent(propKey)) {
90+
return channel.listen(propKey);
91+
}
92+
93+
// Function
94+
return async function (...args: any[]) {
95+
96+
// Add context if any
97+
let methodArgs: any[];
98+
if (options && !isUndefinedOrNull(options.context)) {
99+
methodArgs = [options.context, ...args];
100+
} else {
101+
methodArgs = args;
102+
}
103+
104+
const result = await channel.call(propKey, methodArgs);
105+
106+
// Revive unless marshalling disabled
107+
if (!disableMarshalling) {
108+
return revive(result);
109+
}
110+
111+
return result;
112+
};
113+
}
114+
115+
throw new Error(`Property not found: ${String(propKey)}`);
116+
}
117+
}) as T;
118+
}
119+
120+
function propertyIsEvent(name: string): boolean {
121+
// Assume a property is an event if it has a form of "onSomething"
122+
return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2));
123+
}

src/vs/platform/ipc/node/simpleIpcProxy.ts

Lines changed: 0 additions & 101 deletions
This file was deleted.

src/vs/platform/issue/electron-browser/issueService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

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

1010
export class IssueService {
1111

1212
_serviceBrand: undefined;
1313

1414
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
15-
return createSimpleChannelProxy<IIssueService>(mainProcessService.getChannel('issue'));
15+
return createChannelSender<IIssueService>(mainProcessService.getChannel('issue'));
1616
}
1717
}

src/vs/platform/menubar/electron-browser/menubarService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

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

1010
export class MenubarService {
1111

1212
_serviceBrand: undefined;
1313

1414
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
15-
return createSimpleChannelProxy<IMenubarService>(mainProcessService.getChannel('menubar'));
15+
return createChannelSender<IMenubarService>(mainProcessService.getChannel('menubar'));
1616
}
1717
}

0 commit comments

Comments
 (0)