diff --git a/src/plus/webviews/graph/protocol.ts b/src/plus/webviews/graph/protocol.ts index a5f6cd0..e3f07b9 100644 --- a/src/plus/webviews/graph/protocol.ts +++ b/src/plus/webviews/graph/protocol.ts @@ -309,7 +309,7 @@ export const UpdateSelectionCommandType = new IpcCommandType('graph/didChange', true); +export const DidChangeNotificationType = new IpcNotificationType('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('graph/rows/didChange'); +export const DidChangeRowsNotificationType = new IpcNotificationType( + 'graph/rows/didChange', + undefined, + true, +); export interface DidChangeRowsStatsParams { rowsStats: Record; diff --git a/src/system/logger.scope.ts b/src/system/logger.scope.ts index 95d746a..48e55b0 100644 --- a/src/system/logger.scope.ts +++ b/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, diff --git a/src/webviews/apps/commitDetails/commitDetails.ts b/src/webviews/apps/commitDetails/commitDetails.ts index 40526fc..f8be93a 100644 --- a/src/webviews/apps/commitDetails/commitDetails.ts +++ b/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); } } diff --git a/src/webviews/apps/home/home.ts b/src/webviews/apps/home/home.ts index 349b65f..e3145bc 100644 --- a/src/webviews/apps/home/home.ts +++ b/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; } } diff --git a/src/webviews/apps/plus/account/account.ts b/src/webviews/apps/plus/account/account.ts index 74fc3d9..cecc557 100644 --- a/src/webviews/apps/plus/account/account.ts +++ b/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; } } diff --git a/src/webviews/apps/plus/focus/focus.ts b/src/webviews/apps/plus/focus/focus.ts index dbe3437..6a9b2bd 100644 --- a/src/webviews/apps/plus/focus/focus.ts +++ b/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); } } } diff --git a/src/webviews/apps/plus/graph/graph.tsx b/src/webviews/apps/plus/graph/graph.tsx index 5286b96..fdee004 100644 --- a/src/webviews/apps/plus/graph/graph.tsx +++ b/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 { - private callback?: UpdateStateCallback; + private updateStateCallback?: UpdateStateCallback; constructor() { super('GraphApp'); @@ -93,7 +95,7 @@ export class GraphApp extends App { this.registerEvents(callback)} + subscriber={(updateState: UpdateStateCallback) => this.registerUpdateStateCallback(updateState)} onColumnsChange={debounce( 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 | 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; }; } } diff --git a/src/webviews/apps/plus/patchDetails/patchDetails.ts b/src/webviews/apps/plus/patchDetails/patchDetails.ts index f3a9a68..09da587 100644 --- a/src/webviews/apps/plus/patchDetails/patchDetails.ts +++ b/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); } } diff --git a/src/webviews/apps/plus/timeline/timeline.ts b/src/webviews/apps/plus/timeline/timeline.ts index a097811..9c656ae 100644 --- a/src/webviews/apps/plus/timeline/timeline.ts +++ b/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); } } diff --git a/src/webviews/apps/rebase/rebase.ts b/src/webviews/apps/rebase/rebase.ts index 4ed4df5..193d428 100644 --- a/src/webviews/apps/rebase/rebase.ts +++ b/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); } } diff --git a/src/webviews/apps/settings/settings.ts b/src/webviews/apps/settings/settings.ts index f70f186..2fae79c 100644 --- a/src/webviews/apps/settings/settings.ts +++ b/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); } } diff --git a/src/webviews/apps/shared/appBase.ts b/src/webviews/apps/shared/appBase.ts index 3ee4b29..90e8a7c 100644 --- a/src/webviews/apps/shared/appBase.ts +++ b/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({ 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, ): 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> { const id = nextIpcId(); - this.log(`sendCommandWithCompletion(${id}): name=${command.method}`); + this.log(`${this.appName}.sendCommandWithCompletion(${id}): name=${command.method}`); const promise = new Promise>((resolve, reject) => { let timeout: ReturnType | undefined; diff --git a/src/webviews/apps/welcome/welcome.ts b/src/webviews/apps/welcome/welcome.ts index da127c4..523bbc7 100644 --- a/src/webviews/apps/welcome/welcome.ts +++ b/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; } } diff --git a/src/webviews/protocol.ts b/src/webviews/protocol.ts index c91235e..178dc11 100644 --- a/src/webviews/protocol.ts +++ b/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 extends IpcMessageType ? P : never; diff --git a/src/webviews/webviewController.ts b/src/webviews/webviewController.ts index 040c7d1..1b89a40 100644 --- a/src/webviews/webviewController.ts +++ b/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['onMessageReceivedCore']>({ - args: { 0: e => (e != null ? `${e.id}: method=${e.method}` : '') }, + args: { 0: e => (e != null ? `${e.id}, method=${e.method}` : '') }, }) private onMessageReceivedCore(e: IpcMessage) { if (e == null) return; @@ -534,10 +536,23 @@ export class WebviewController< params: IpcMessageParams, completionId?: string, ): Promise { + 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, };