Преглед изворни кода

Adds optional binary serialization to ipc messages

- Improves performance on sending lots of data to the Graph
main
Eric Amodio пре 1 година
родитељ
комит
be50f2b3ed
15 измењених фајлова са 106 додато и 84 уклоњено
  1. +6
    -2
      src/plus/webviews/graph/protocol.ts
  2. +3
    -1
      src/system/logger.scope.ts
  3. +2
    -5
      src/webviews/apps/commitDetails/commitDetails.ts
  4. +2
    -6
      src/webviews/apps/home/home.ts
  5. +2
    -6
      src/webviews/apps/plus/account/account.ts
  6. +3
    -4
      src/webviews/apps/plus/focus/focus.ts
  7. +20
    -20
      src/webviews/apps/plus/graph/graph.tsx
  8. +2
    -5
      src/webviews/apps/plus/patchDetails/patchDetails.ts
  9. +2
    -6
      src/webviews/apps/plus/timeline/timeline.ts
  10. +3
    -6
      src/webviews/apps/rebase/rebase.ts
  11. +2
    -6
      src/webviews/apps/settings/settings.ts
  12. +37
    -8
      src/webviews/apps/shared/appBase.ts
  13. +2
    -6
      src/webviews/apps/welcome/welcome.ts
  14. +2
    -0
      src/webviews/protocol.ts
  15. +18
    -3
      src/webviews/webviewController.ts

+ 6
- 2
src/plus/webviews/graph/protocol.ts Прегледај датотеку

@ -309,7 +309,7 @@ export const UpdateSelectionCommandType = new IpcCommandType
export interface DidChangeParams {
state: State;
}
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('graph/didChange', true);
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('graph/didChange', true, true);
export interface DidChangeGraphConfigurationParams {
config: GraphComponentConfig;
@ -387,7 +387,11 @@ export interface DidChangeRowsParams {
rowsStatsLoading: boolean;
selectedRows?: GraphSelectedRows;
}
export const DidChangeRowsNotificationType = new IpcNotificationType<DidChangeRowsParams>('graph/rows/didChange');
export const DidChangeRowsNotificationType = new IpcNotificationType<DidChangeRowsParams>(
'graph/rows/didChange',
undefined,
true,
);
export interface DidChangeRowsStatsParams {
rowsStats: Record<string, GraphRowStats>;

+ 3
- 1
src/system/logger.scope.ts Прегледај датотеку

@ -18,7 +18,9 @@ export function getLogScope(): LogScope | undefined {
return scopes.get(scopeCounter);
}
export function getNewLogScope(prefix: string): LogScope {
export function getNewLogScope(prefix: string, scope?: LogScope | undefined): LogScope {
if (scope != null) return { scopeId: scope.scopeId, prefix: `${scope.prefix}${prefix}` };
const scopeId = getNextLogScopeId();
return {
scopeId: scopeId,

+ 2
- 5
src/webviews/apps/commitDetails/commitDetails.ts Прегледај датотеку

@ -105,10 +105,7 @@ export class CommitDetailsApp extends App> {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
// case DidChangeRichStateNotificationType.method:
// onIpc(DidChangeRichStateNotificationType, msg, params => {
@ -152,7 +149,7 @@ export class CommitDetailsApp extends App> {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}

+ 2
- 6
src/webviews/apps/home/home.ts Прегледај датотеку

@ -38,13 +38,9 @@ export class HomeApp extends App {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeRepositoriesType.method:
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
onIpc(DidChangeRepositoriesType, msg, params => {
this.state.repositories = params;
this.state.timestamp = Date.now();
@ -53,7 +49,7 @@ export class HomeApp extends App {
});
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
break;
}
}

+ 2
- 6
src/webviews/apps/plus/account/account.ts Прегледај датотеку

@ -31,13 +31,9 @@ export class AccountApp extends App {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeSubscriptionNotificationType.method:
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
onIpc(DidChangeSubscriptionNotificationType, msg, params => {
this.state.subscription = params.subscription;
this.state.avatar = params.avatar;
@ -48,7 +44,7 @@ export class AccountApp extends App {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
break;
}
}

+ 3
- 4
src/webviews/apps/plus/focus/focus.ts Прегледај датотеку

@ -118,10 +118,7 @@ export class FocusApp extends App {
}
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeNotificationType.method:
onIpc(DidChangeNotificationType, msg, params => {
@ -130,6 +127,8 @@ export class FocusApp extends App {
this.attachState();
});
break;
default:
super.onMessageReceived?.(msg);
}
}
}

+ 20
- 20
src/webviews/apps/plus/graph/graph.tsx Прегледај датотеку

@ -51,7 +51,9 @@ import {
UpdateSelectionCommandType,
} from '../../../../plus/webviews/graph/protocol';
import { Color, darken, getCssVariable, lighten, mix, opacity } from '../../../../system/color';
import { debug } from '../../../../system/decorators/log';
import { debounce } from '../../../../system/function';
import { getLogScope, setLogScopeExit } from '../../../../system/logger.scope';
import type { IpcMessage, IpcNotificationType } from '../../../protocol';
import { onIpc } from '../../../protocol';
import { App } from '../../shared/appBase';
@ -73,7 +75,7 @@ const graphLaneThemeColors = new Map([
]);
export class GraphApp extends App<State> {
private callback?: UpdateStateCallback;
private updateStateCallback?: UpdateStateCallback;
constructor() {
super('GraphApp');
@ -93,7 +95,7 @@ export class GraphApp extends App {
<GraphWrapper
nonce={this.state.nonce}
state={this.state}
subscriber={(callback: UpdateStateCallback) => this.registerEvents(callback)}
subscriber={(updateState: UpdateStateCallback) => this.registerUpdateStateCallback(updateState)}
onColumnsChange={debounce<GraphApp['onColumnsChanged']>(
settings => this.onColumnsChanged(settings),
250,
@ -139,14 +141,13 @@ export class GraphApp extends App {
// }
// }
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
protected override onMessageReceived(msg: IpcMessage) {
const scope = getLogScope();
switch (msg.method) {
case DidChangeNotificationType.method:
onIpc(DidChangeNotificationType, msg, (params, type) => {
this.setState({ ...this.state, ...params.state }, type);
this.setState({ ...this.state, ...params }, type);
});
break;
@ -215,7 +216,8 @@ export class GraphApp extends App {
const newRowsLength = params.rows.length;
this.log(
`onMessageReceived(${msg.id}:${msg.method}): paging in ${newRowsLength} rows into existing ${previousRowsLength} rows at ${params.paging.startingCursor} (last existing row: ${lastId})`,
scope,
`paging in ${newRowsLength} rows into existing ${previousRowsLength} rows at ${params.paging.startingCursor} (last existing row: ${lastId})`,
);
rows = [];
@ -223,18 +225,14 @@ export class GraphApp extends App {
rows.length = previousRowsLength + newRowsLength;
if (params.paging.startingCursor !== lastId) {
this.log(
`onMessageReceived(${msg.id}:${msg.method}): searching for ${params.paging.startingCursor} in existing rows`,
);
this.log(scope, `searching for ${params.paging.startingCursor} in existing rows`);
let i = 0;
let row;
for (row of previousRows) {
rows[i++] = row;
if (row.sha === params.paging.startingCursor) {
this.log(
`onMessageReceived(${msg.id}:${msg.method}): found ${params.paging.startingCursor} in existing rows`,
);
this.log(scope, `found ${params.paging.startingCursor} in existing rows`);
previousRowsLength = i;
@ -256,7 +254,7 @@ export class GraphApp extends App {
rows[previousRowsLength + i] = params.rows[i];
}
} else {
this.log(`onMessageReceived(${msg.id}:${msg.method}): setting to ${params.rows.length} rows`);
this.log(scope, `setting to ${params.rows.length} rows`);
if (params.rows.length === 0) {
rows = this.state.rows;
@ -281,6 +279,8 @@ export class GraphApp extends App {
}
this.state.loading = false;
this.setState(this.state, type);
setLogScopeExit(scope, ` \u2022 rows=${this.state.rows?.length ?? 0}`);
});
break;
@ -339,7 +339,7 @@ export class GraphApp extends App {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}
@ -474,14 +474,14 @@ export class GraphApp extends App {
this.setState(this.state, 'didChangeTheme');
}
@debug({ args: false, singleLine: true })
protected override setState(state: State, type?: IpcNotificationType<any> | InternalNotificationType) {
this.log(`setState()`);
const themingChanged = this.ensureTheming(state);
this.state = state;
super.setState({ timestamp: state.timestamp, selectedRepository: state.selectedRepository });
this.callback?.(this.state, type, themingChanged);
this.updateStateCallback?.(this.state, type, themingChanged);
}
private ensureTheming(state: State): boolean {
@ -691,11 +691,11 @@ export class GraphApp extends App {
});
}
private registerEvents(callback: UpdateStateCallback): () => void {
this.callback = callback;
private registerUpdateStateCallback(updateState: UpdateStateCallback): () => void {
this.updateStateCallback = updateState;
return () => {
this.callback = undefined;
this.updateStateCallback = undefined;
};
}
}

+ 2
- 5
src/webviews/apps/plus/patchDetails/patchDetails.ts Прегледај датотеку

@ -135,10 +135,7 @@ export class PatchDetailsApp extends App> {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
// case DidChangeRichStateNotificationType.method:
// onIpc(DidChangeRichStateNotificationType, msg, params => {
@ -222,7 +219,7 @@ export class PatchDetailsApp extends App> {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}

+ 2
- 6
src/webviews/apps/plus/timeline/timeline.ts Прегледај датотеку

@ -48,13 +48,9 @@ export class TimelineApp extends App {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeNotificationType.method:
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
onIpc(DidChangeNotificationType, msg, params => {
this.state = params.state;
this.setState(this.state);
@ -63,7 +59,7 @@ export class TimelineApp extends App {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}

+ 3
- 6
src/webviews/apps/rebase/rebase.ts Прегледај датотеку

@ -1,6 +1,7 @@
/*global document window*/
import './rebase.scss';
import Sortable from 'sortablejs';
import type { IpcMessage } from '../../protocol';
import { onIpc } from '../../protocol';
import type { RebaseEntry, RebaseEntryAction, State } from '../../rebase/protocol';
import {
@ -318,13 +319,9 @@ class RebaseEditor extends App {
});
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data;
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeNotificationType.method:
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
onIpc(DidChangeNotificationType, msg, params => {
this.state = params.state;
this.setState(this.state);
@ -333,7 +330,7 @@ class RebaseEditor extends App {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}

+ 2
- 6
src/webviews/apps/settings/settings.ts Прегледај датотеку

@ -133,11 +133,7 @@ export class SettingsApp extends App {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidOpenAnchorNotificationType.method: {
onIpc(DidOpenAnchorNotificationType, msg, params => {
@ -157,7 +153,7 @@ export class SettingsApp extends App {
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
}
}

+ 37
- 8
src/webviews/apps/shared/appBase.ts Прегледај датотеку

@ -1,7 +1,11 @@
/*global window document*/
import type { CustomEditorIds, WebviewIds, WebviewViewIds } from '../../../constants';
import { debug } from '../../../system/decorators/log';
import { debounce } from '../../../system/function';
import { Logger } from '../../../system/logger';
import type { LogScope } from '../../../system/logger.scope';
import { getLogScope, getNewLogScope } from '../../../system/logger.scope';
import { maybeStopWatch } from '../../../system/stopwatch';
import type {
IpcCommandType,
IpcMessage,
@ -38,6 +42,8 @@ function nextIpcId() {
return `webview:${ipcSequence}`;
}
const textDecoder = new TextDecoder();
export abstract class App<
State extends { webviewId: CustomEditorIds | WebviewIds | WebviewViewIds; timestamp: number } = {
webviewId: CustomEditorIds | WebviewIds | WebviewViewIds;
@ -77,7 +83,7 @@ export abstract class App<
DEBUG ? 'debug' : 'off',
);
this.log(`ctor()`);
this.log(`${appName}()`);
// this.log(`ctor(${this.state ? JSON.stringify(this.state) : ''})`);
this._api = acquireVsCodeApi();
@ -93,14 +99,14 @@ export abstract class App<
disposables.push(watchThemeColors());
requestAnimationFrame(() => {
this.log(`ctor(): initializing...`);
this.log(`${appName}(): initializing...`);
try {
this.onInitialize?.();
this.bind();
if (this.onMessageReceived != null) {
disposables.push(DOM.on(window, 'message', this.onMessageReceived.bind(this)));
disposables.push(DOM.on(window, 'message', e => this.onMessageReceivedCore(e)));
}
this.sendCommand(WebviewReadyCommandType, undefined);
@ -127,9 +133,26 @@ export abstract class App<
protected onInitialize?(): void;
protected onBind?(): Disposable[];
protected onInitialized?(): void;
protected onMessageReceived?(e: MessageEvent): void;
protected onMessageReceived?(msg: IpcMessage): void;
protected onThemeUpdated?(e: ThemeChangeEvent): void;
@debug<App['onMessageReceivedCore']>({ args: { 0: e => `${e.data.id}, method=${e.data.method}` } })
private onMessageReceivedCore(e: MessageEvent) {
const scope = getLogScope();
const msg = e.data as IpcMessage;
if (msg.packed && msg.params instanceof Uint8Array) {
const sw = maybeStopWatch(getNewLogScope(` deserializing msg=${e.data.method}`, scope), {
log: false,
logLevel: 'debug',
});
msg.params = JSON.parse(textDecoder.decode(msg.params));
sw?.stop();
}
this.onMessageReceived!(msg);
}
private _focused?: boolean;
private _inputFocused?: boolean;
@ -166,8 +189,14 @@ export abstract class App<
);
}
protected log(message: string, ...optionalParams: any[]) {
Logger.log(message, ...optionalParams);
protected log(message: string, ...optionalParams: any[]): void;
protected log(scope: LogScope | undefined, message: string, ...optionalParams: any[]): void;
protected log(scopeOrMessage: LogScope | string | undefined, ...optionalParams: any[]): void {
if (typeof scopeOrMessage === 'string') {
Logger.log(scopeOrMessage, ...optionalParams);
} else {
Logger.log(scopeOrMessage, optionalParams.shift(), ...optionalParams);
}
}
protected getState(): State | undefined {
@ -179,7 +208,7 @@ export abstract class App<
params: IpcMessageParams<TCommand>,
): void {
const id = nextIpcId();
this.log(`sendCommand(${id}): name=${command.method}`);
this.log(`${this.appName}.sendCommand(${id}): name=${command.method}`);
this.postMessage({ id: id, method: command.method, params: params });
}
@ -193,7 +222,7 @@ export abstract class App<
completion: TCompletion,
): Promise<IpcMessageParams<TCompletion>> {
const id = nextIpcId();
this.log(`sendCommandWithCompletion(${id}): name=${command.method}`);
this.log(`${this.appName}.sendCommandWithCompletion(${id}): name=${command.method}`);
const promise = new Promise<IpcMessageParams<TCompletion>>((resolve, reject) => {
let timeout: ReturnType<typeof setTimeout> | undefined;

+ 2
- 6
src/webviews/apps/welcome/welcome.ts Прегледај датотеку

@ -41,13 +41,9 @@ export class WelcomeApp extends App {
return disposables;
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
protected override onMessageReceived(msg: IpcMessage) {
switch (msg.method) {
case DidChangeNotificationType.method:
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
onIpc(DidChangeNotificationType, msg, params => {
this.state = params.state;
this.setState(this.state);
@ -55,7 +51,7 @@ export class WelcomeApp extends App {
});
break;
default:
super.onMessageReceived?.(e);
super.onMessageReceived?.(msg);
break;
}
}

+ 2
- 0
src/webviews/protocol.ts Прегледај датотеку

@ -5,6 +5,7 @@ import type { ConfigPath, ConfigPathValue, Path, PathValue } from '../system/con
export interface IpcMessage {
id: string;
method: string;
packed?: boolean;
params?: unknown;
completionId?: string;
}
@ -14,6 +15,7 @@ abstract class IpcMessageType {
constructor(
public readonly method: string,
public readonly reset: boolean = false,
public readonly pack: boolean = false,
) {}
}
export type IpcMessageParams<T> = T extends IpcMessageType<infer P> ? P : never;

+ 18
- 3
src/webviews/webviewController.ts Прегледај датотеку

@ -9,8 +9,9 @@ import { setContext } from '../system/context';
import { debug, logName } from '../system/decorators/log';
import { serialize } from '../system/decorators/serialize';
import { Logger } from '../system/logger';
import { getLogScope, setLogScopeExit } from '../system/logger.scope';
import { getLogScope, getNewLogScope, setLogScopeExit } from '../system/logger.scope';
import { isPromise } from '../system/promise';
import { maybeStopWatch } from '../system/stopwatch';
import type { WebviewContext } from '../system/webview';
import type {
IpcMessage,
@ -25,6 +26,7 @@ import type { WebviewPanelDescriptor, WebviewShowOptions, WebviewViewDescriptor
const maxSmallIntegerV8 = 2 ** 30; // Max number that can be stored in V8's smis (small integers)
const utf8TextDecoder = new TextDecoder('utf8');
const utf8TextEncoder = new TextEncoder();
let ipcSequence = 0;
function nextIpcId() {
@ -385,7 +387,7 @@ export class WebviewController<
}
@debug<WebviewController<State>['onMessageReceivedCore']>({
args: { 0: e => (e != null ? `${e.id}: method=${e.method}` : '<undefined>') },
args: { 0: e => (e != null ? `${e.id}, method=${e.method}` : '<undefined>') },
})
private onMessageReceivedCore(e: IpcMessage) {
if (e == null) return;
@ -534,10 +536,23 @@ export class WebviewController<
params: IpcMessageParams<T>,
completionId?: string,
): Promise<boolean> {
let packed;
if (type.pack && params != null) {
const scope = getLogScope();
const sw = maybeStopWatch(getNewLogScope(` serializing msg=${type.method}`, scope), {
log: false,
logLevel: 'debug',
});
packed = utf8TextEncoder.encode(JSON.stringify(params));
sw?.stop();
}
const msg: IpcMessage = {
id: this.nextIpcId(),
method: type.method,
params: params,
params: packed ?? params,
packed: packed != null,
completionId: completionId,
};

Loading…
Откажи
Сачувај