Ver código fonte

Moves pending ipc handling to controller

main
Eric Amodio 1 ano atrás
pai
commit
b82080db60
7 arquivos alterados com 112 adições e 132 exclusões
  1. +0
    -1
      src/plus/webviews/focus/protocol.ts
  2. +48
    -101
      src/plus/webviews/graph/graphWebview.ts
  3. +2
    -11
      src/plus/webviews/graph/protocol.ts
  4. +0
    -4
      src/webviews/commitDetails/commitDetailsWebview.ts
  5. +1
    -1
      src/webviews/commitDetails/protocol.ts
  6. +1
    -1
      src/webviews/protocol.ts
  7. +60
    -13
      src/webviews/webviewController.ts

+ 0
- 1
src/plus/webviews/focus/protocol.ts Ver arquivo

@ -63,5 +63,4 @@ export interface DidChangeSubscriptionParams {
}
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'graph/subscription/didChange',
true,
);

+ 48
- 101
src/plus/webviews/graph/graphWebview.ts Ver arquivo

@ -76,7 +76,7 @@ import { getSettledValue } from '../../../system/promise';
import { isDarkTheme, isLightTheme } from '../../../system/utils';
import { isWebviewItemContext, isWebviewItemGroupContext, serializeWebviewItemContext } from '../../../system/webview';
import { RepositoryFolderNode } from '../../../views/nodes/viewNode';
import type { IpcMessage, IpcMessageParams, IpcNotificationType } from '../../../webviews/protocol';
import type { IpcMessage, IpcNotificationType } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol';
import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController';
import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
@ -205,7 +205,17 @@ export class GraphWebviewProvider implements WebviewProvider {
private _etagRepository?: number;
private _firstSelection = true;
private _graph?: GitGraph;
private _pendingIpcNotifications = new Map<IpcNotificationType, IpcMessage | (() => Promise<boolean>)>();
private readonly _ipcNotificationMap = new Map<IpcNotificationType<any>, () => Promise<boolean>>([
[DidChangeColumnsNotificationType, this.notifyDidChangeColumns],
[DidChangeGraphConfigurationNotificationType, this.notifyDidChangeConfiguration],
[DidChangeNotificationType, this.notifyDidChangeState],
[DidChangeRefsVisibilityNotificationType, this.notifyDidChangeRefsVisibility],
[DidChangeSelectionNotificationType, this.notifyDidChangeSelection],
[DidChangeSubscriptionNotificationType, this.notifyDidChangeSubscription],
[DidChangeWorkingTreeNotificationType, this.notifyDidChangeWorkingTree],
[DidChangeWindowFocusNotificationType, this.notifyDidChangeWindowFocus],
[DidFetchNotificationType, this.notifyDidFetch],
]);
private _refsMetadata: Map<string, GraphRefMetadata | null> | null | undefined;
private _search: GitSearch | undefined;
private _searchCancellation: CancellationTokenSource | undefined;
@ -294,7 +304,6 @@ export class GraphWebviewProvider implements WebviewProvider {
onRefresh(force?: boolean) {
if (force) {
this.resetRepositoryState();
this._pendingIpcNotifications.clear();
}
}
@ -388,10 +397,6 @@ export class GraphWebviewProvider implements WebviewProvider {
void this.notifyDidChangeWindowFocus();
}
onReady(): void {
this.sendPendingIpcNotifications();
}
onFocusChanged(focused: boolean): void {
if (!focused || this.activeSelection == null || !this.container.commitDetailsView.visible) {
this._showActiveSelectionDetailsDebounced?.cancel();
@ -417,7 +422,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (visible) {
if (this.host.ready) {
this.sendPendingIpcNotifications();
this.host.sendPendingIpcNotifications();
}
const { activeSelection } = this;
@ -734,7 +739,7 @@ export class GraphWebviewProvider implements WebviewProvider {
}
}
void this.notify(DidEnsureRowNotificationType, { id: id }, completionId);
void this.host.notify(DidEnsureRowNotificationType, { id: id }, completionId);
}
private async onGetMissingAvatars(e: GetMissingAvatarsParams) {
@ -910,7 +915,7 @@ export class GraphWebviewProvider implements WebviewProvider {
this._search = search;
void (await this.ensureSearchStartsInRange(this._graph!, search));
void this.notify(
void this.host.notify(
DidSearchNotificationType,
{
results:
@ -953,7 +958,7 @@ export class GraphWebviewProvider implements WebviewProvider {
} catch (ex) {
this._search = undefined;
void this.notify(
void this.host.notify(
DidSearchNotificationType,
{
results: {
@ -967,7 +972,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (cancellation.token.isCancellationRequested) {
if (completionId != null) {
void this.notify(DidSearchNotificationType, { results: undefined }, completionId);
void this.host.notify(DidSearchNotificationType, { results: undefined }, completionId);
}
return;
}
@ -985,7 +990,7 @@ export class GraphWebviewProvider implements WebviewProvider {
this.setSelectedRows(firstResult);
}
void this.notify(
void this.host.notify(
DidSearchNotificationType,
{
results:
@ -1103,7 +1108,7 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private updateState(immediate: boolean = false) {
this._pendingIpcNotifications.clear();
this.host.clearPendingIpcNotifications();
if (immediate) {
void this.notifyDidChangeState();
@ -1120,11 +1125,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeWindowFocus(): Promise<boolean> {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeWindowFocusNotificationType);
this.host.addPendingIpcNotification(DidChangeWindowFocusNotificationType, this._ipcNotificationMap, this);
return false;
}
return this.notify(DidChangeWindowFocusNotificationType, {
return this.host.notify(DidChangeWindowFocusNotificationType, {
focused: this.isWindowFocused,
});
}
@ -1151,7 +1156,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (this._graph == null) return;
const data = this._graph;
return this.notify(DidChangeAvatarsNotificationType, {
return this.host.notify(DidChangeAvatarsNotificationType, {
avatars: Object.fromEntries(data.avatars),
});
}
@ -1176,7 +1181,7 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeRefsMetadata() {
return this.notify(DidChangeRefsMetadataNotificationType, {
return this.host.notify(DidChangeRefsMetadataNotificationType, {
metadata: this._refsMetadata != null ? Object.fromEntries(this._refsMetadata) : this._refsMetadata,
});
}
@ -1184,13 +1189,13 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeColumns() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeColumnsNotificationType);
this.host.addPendingIpcNotification(DidChangeColumnsNotificationType, this._ipcNotificationMap, this);
return false;
}
const columns = this.getColumns();
const columnSettings = this.getColumnSettings(columns);
return this.notify(DidChangeColumnsNotificationType, {
return this.host.notify(DidChangeColumnsNotificationType, {
columns: columnSettings,
context: this.getColumnHeaderContext(columnSettings),
});
@ -1199,11 +1204,15 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeRefsVisibility() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeRefsVisibilityNotificationType);
this.host.addPendingIpcNotification(
DidChangeRefsVisibilityNotificationType,
this._ipcNotificationMap,
this,
);
return false;
}
return this.notify(DidChangeRefsVisibilityNotificationType, {
return this.host.notify(DidChangeRefsVisibilityNotificationType, {
excludeRefs: this.getExcludedRefs(this._graph),
excludeTypes: this.getExcludedTypes(this._graph),
includeOnlyRefs: this.getIncludeOnlyRefs(this._graph),
@ -1213,11 +1222,15 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeConfiguration() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeGraphConfigurationNotificationType);
this.host.addPendingIpcNotification(
DidChangeGraphConfigurationNotificationType,
this._ipcNotificationMap,
this,
);
return false;
}
return this.notify(DidChangeGraphConfigurationNotificationType, {
return this.host.notify(DidChangeGraphConfigurationNotificationType, {
config: this.getComponentConfig(),
});
}
@ -1225,12 +1238,12 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidFetch() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidFetchNotificationType);
this.host.addPendingIpcNotification(DidFetchNotificationType, this._ipcNotificationMap, this);
return false;
}
const lastFetched = await this.repository!.getLastFetched();
return this.notify(DidFetchNotificationType, {
return this.host.notify(DidFetchNotificationType, {
lastFetched: new Date(lastFetched),
});
}
@ -1240,7 +1253,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (this._graph == null) return;
const data = this._graph;
return this.notify(
return this.host.notify(
DidChangeRowsNotificationType,
{
rows: data.rows,
@ -1260,11 +1273,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeWorkingTree() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeWorkingTreeNotificationType);
this.host.addPendingIpcNotification(DidChangeWorkingTreeNotificationType, this._ipcNotificationMap, this);
return false;
}
return this.notify(DidChangeWorkingTreeNotificationType, {
return this.host.notify(DidChangeWorkingTreeNotificationType, {
stats: (await this.getWorkingTreeStats()) ?? { added: 0, deleted: 0, modified: 0 },
});
}
@ -1272,11 +1285,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeSelection() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeSelectionNotificationType);
this.host.addPendingIpcNotification(DidChangeSelectionNotificationType, this._ipcNotificationMap, this);
return false;
}
return this.notify(DidChangeSelectionNotificationType, {
return this.host.notify(DidChangeSelectionNotificationType, {
selection: this._selectedRows ?? {},
});
}
@ -1284,12 +1297,12 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeSubscription() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeSubscriptionNotificationType);
this.host.addPendingIpcNotification(DidChangeSubscriptionNotificationType, this._ipcNotificationMap, this);
return false;
}
const [access] = await this.getGraphAccess();
return this.notify(DidChangeSubscriptionNotificationType, {
return this.host.notify(DidChangeSubscriptionNotificationType, {
subscription: access.subscription.current,
allowed: access.allowed !== false,
});
@ -1298,77 +1311,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug()
private async notifyDidChangeState() {
if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeNotificationType);
this.host.addPendingIpcNotification(DidChangeNotificationType, this._ipcNotificationMap, this);
return false;
}
return this.notify(DidChangeNotificationType, { state: await this.getState() });
}
private async notify<T extends IpcNotificationType<any>>(
type: T,
params: IpcMessageParams<T>,
completionId?: string,
): Promise<boolean> {
const msg: IpcMessage = {
id: this.host.nextIpcId(),
method: type.method,
params: params,
completionId: completionId,
};
const success = await this.host.postMessage(msg);
if (success) {
this._pendingIpcNotifications.clear();
} else {
this.addPendingIpcNotification(type, msg);
}
return success;
}
private readonly _ipcNotificationMap = new Map<IpcNotificationType<any>, () => Promise<boolean>>([
[DidChangeColumnsNotificationType, this.notifyDidChangeColumns],
[DidChangeGraphConfigurationNotificationType, this.notifyDidChangeConfiguration],
[DidChangeNotificationType, this.notifyDidChangeState],
[DidChangeRefsVisibilityNotificationType, this.notifyDidChangeRefsVisibility],
[DidChangeSelectionNotificationType, this.notifyDidChangeSelection],
[DidChangeSubscriptionNotificationType, this.notifyDidChangeSubscription],
[DidChangeWorkingTreeNotificationType, this.notifyDidChangeWorkingTree],
[DidChangeWindowFocusNotificationType, this.notifyDidChangeWindowFocus],
[DidFetchNotificationType, this.notifyDidFetch],
]);
private addPendingIpcNotification(type: IpcNotificationType<any>, msg?: IpcMessage) {
if (type === DidChangeNotificationType) {
this._pendingIpcNotifications.clear();
} else if (type.overwriteable) {
this._pendingIpcNotifications.delete(type);
}
let msgOrFn: IpcMessage | (() => Promise<boolean>) | undefined;
if (msg == null) {
msgOrFn = this._ipcNotificationMap.get(type)?.bind(this);
if (msgOrFn == null) {
debugger;
return;
}
} else {
msgOrFn = msg;
}
this._pendingIpcNotifications.set(type, msgOrFn);
}
private sendPendingIpcNotifications() {
if (this._pendingIpcNotifications.size === 0) return;
const ipcs = new Map(this._pendingIpcNotifications);
this._pendingIpcNotifications.clear();
for (const msgOrFn of ipcs.values()) {
if (typeof msgOrFn === 'function') {
void msgOrFn();
} else {
void this.host.postMessage(msgOrFn);
}
}
return this.host.notify(DidChangeNotificationType, { state: await this.getState() });
}
private ensureRepositorySubscriptions(force?: boolean) {

+ 2
- 11
src/plus/webviews/graph/protocol.ts Ver arquivo

@ -308,7 +308,6 @@ export interface DidChangeGraphConfigurationParams {
}
export const DidChangeGraphConfigurationNotificationType = new IpcNotificationType<DidChangeGraphConfigurationParams>(
'graph/configuration/didChange',
true,
);
export interface DidChangeSubscriptionParams {
@ -317,7 +316,6 @@ export interface DidChangeSubscriptionParams {
}
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'graph/subscription/didChange',
true,
);
export interface DidChangeAvatarsParams {
@ -325,7 +323,6 @@ export interface DidChangeAvatarsParams {
}
export const DidChangeAvatarsNotificationType = new IpcNotificationType<DidChangeAvatarsParams>(
'graph/avatars/didChange',
true,
);
export interface DidChangeRefsMetadataParams {
@ -333,7 +330,6 @@ export interface DidChangeRefsMetadataParams {
}
export const DidChangeRefsMetadataNotificationType = new IpcNotificationType<DidChangeRefsMetadataParams>(
'graph/refs/didChangeMetadata',
true,
);
export interface DidChangeColumnsParams {
@ -342,7 +338,6 @@ export interface DidChangeColumnsParams {
}
export const DidChangeColumnsNotificationType = new IpcNotificationType<DidChangeColumnsParams>(
'graph/columns/didChange',
true,
);
export interface DidChangeWindowFocusParams {
@ -350,7 +345,6 @@ export interface DidChangeWindowFocusParams {
}
export const DidChangeWindowFocusNotificationType = new IpcNotificationType<DidChangeWindowFocusParams>(
'graph/window/focus/didChange',
true,
);
export interface DidChangeRefsVisibilityParams {
@ -360,7 +354,6 @@ export interface DidChangeRefsVisibilityParams {
}
export const DidChangeRefsVisibilityNotificationType = new IpcNotificationType<DidChangeRefsVisibilityParams>(
'graph/refs/didChangeVisibility',
true,
);
export interface DidChangeRowsParams {
@ -378,7 +371,6 @@ export interface DidChangeSelectionParams {
}
export const DidChangeSelectionNotificationType = new IpcNotificationType<DidChangeSelectionParams>(
'graph/selection/didChange',
true,
);
export interface DidChangeWorkingTreeParams {
@ -386,7 +378,6 @@ export interface DidChangeWorkingTreeParams {
}
export const DidChangeWorkingTreeNotificationType = new IpcNotificationType<DidChangeWorkingTreeParams>(
'graph/workingTree/didChange',
true,
);
export interface DidEnsureRowParams {
@ -408,12 +399,12 @@ export interface DidSearchParams {
results: GraphSearchResults | GraphSearchResultsError | undefined;
selectedRows?: GraphSelectedRows;
}
export const DidSearchNotificationType = new IpcNotificationType<DidSearchParams>('graph/didSearch', true);
export const DidSearchNotificationType = new IpcNotificationType<DidSearchParams>('graph/didSearch');
export interface DidFetchParams {
lastFetched: Date;
}
export const DidFetchNotificationType = new IpcNotificationType<DidFetchParams>('graph/didFetch', true);
export const DidFetchNotificationType = new IpcNotificationType<DidFetchParams>('graph/didFetch');
export interface ShowInCommitGraphCommandArgs {
ref: GitReference;

+ 0
- 4
src/webviews/commitDetails/commitDetailsWebview.ts Ver arquivo

@ -653,8 +653,6 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
private _notifyDidChangeStateDebounced: Deferrable<() => void> | undefined = undefined;
private updateState(immediate: boolean = false) {
if (!this.host.ready || !this.host.visible) return;
if (immediate) {
void this.notifyDidChangeState();
return;
@ -668,8 +666,6 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
}
private async notifyDidChangeState() {
if (!this.host.ready || !this.host.visible) return false;
const scope = getLogScope();
this._notifyDidChangeStateDebounced?.cancel();

+ 1
- 1
src/webviews/commitDetails/protocol.ts Ver arquivo

@ -104,7 +104,7 @@ export const PreferencesCommandType = new IpcCommandType('comm
export interface DidChangeParams {
state: Serialized<State>;
}
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('commit/didChange');
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('commit/didChange', true);
export type DidChangeRichStateParams = {
formattedMessage?: string;

+ 1
- 1
src/webviews/protocol.ts Ver arquivo

@ -10,7 +10,7 @@ export interface IpcMessage {
abstract class IpcMessageType<Params = void> {
_?: Params; // Required for type inferencing to work properly
constructor(public readonly method: string, public readonly overwriteable: boolean = false) {}
constructor(public readonly method: string, public readonly reset: boolean = false) {}
}
export type IpcMessageParams<T> = T extends IpcMessageType<infer P> ? P : never;

+ 60
- 13
src/webviews/webviewController.ts Ver arquivo

@ -117,7 +117,6 @@ export class WebviewController<
public readonly id: Descriptor['id'];
private _ready: boolean = false;
private _suspended: boolean = false;
get ready() {
return this._ready;
}
@ -174,7 +173,6 @@ export class WebviewController<
this.provider?.onVisibilityChanged?.(false);
this._ready = false;
this._suspended = false;
this._onDidDispose.fire();
this.disposable?.dispose();
@ -271,11 +269,13 @@ export class WebviewController<
@debug()
async refresh(force?: boolean): Promise<void> {
if (force) {
this.clearPendingIpcNotifications();
}
this.provider.onRefresh?.(force);
// Mark the webview as not ready, until we know if we are changing the html
this._ready = false;
this._suspended = false;
const html = await this.getHtml(this.webview);
if (force) {
@ -307,7 +307,7 @@ export class WebviewController<
case WebviewReadyCommandType.method:
onIpc(WebviewReadyCommandType, e, () => {
this._ready = true;
this._suspended = false;
this.sendPendingIpcNotifications();
this.provider.onReady?.();
});
@ -348,14 +348,11 @@ export class WebviewController<
private onParentVisibilityChanged(visible: boolean, active?: boolean) {
if (this.descriptor.webviewHostOptions?.retainContextWhenHidden !== true) {
if (visible) {
if (this._suspended) {
this._ready = true;
this._suspended = false;
this.provider.onReady?.();
if (this._ready) {
this.sendPendingIpcNotifications();
}
} else {
this._ready = false;
this._suspended = true;
}
}
@ -438,17 +435,24 @@ export class WebviewController<
return nextIpcId();
}
notify<T extends IpcNotificationType<any>>(
async notify<T extends IpcNotificationType<any>>(
type: T,
params: IpcMessageParams<T>,
completionId?: string,
): Promise<boolean> {
return this.postMessage({
const msg: IpcMessage = {
id: this.nextIpcId(),
method: type.method,
params: params,
completionId: completionId,
});
};
const success = await this.postMessage(msg);
if (success) {
this._pendingIpcNotifications.clear();
} else {
this.addPendingIpcNotificationCore(type, msg);
}
return success;
}
@serialize()
@ -457,7 +461,7 @@ export class WebviewController<
0: m => `{"id":${m.id},"method":${m.method}${m.completionId ? `,"completionId":${m.completionId}` : ''}}`,
},
})
async postMessage(message: IpcMessage): Promise<boolean> {
private async postMessage(message: IpcMessage): Promise<boolean> {
if (!this._ready) return Promise.resolve(false);
// It looks like there is a bug where `postMessage` can sometimes just hang infinitely. Not sure why, but ensure we don't hang
@ -467,6 +471,49 @@ export class WebviewController<
]);
return success;
}
private _pendingIpcNotifications = new Map<IpcNotificationType, IpcMessage | (() => Promise<boolean>)>();
addPendingIpcNotification(
type: IpcNotificationType<any>,
mapping: Map<IpcNotificationType<any>, () => Promise<boolean>>,
thisArg: any,
) {
this.addPendingIpcNotificationCore(type, mapping.get(type)?.bind(thisArg));
}
private addPendingIpcNotificationCore(
type: IpcNotificationType<any>,
msgOrFn: IpcMessage | (() => Promise<boolean>) | undefined,
) {
if (type.reset) {
this._pendingIpcNotifications.clear();
}
if (msgOrFn == null) {
debugger;
return;
}
this._pendingIpcNotifications.set(type, msgOrFn);
}
clearPendingIpcNotifications() {
this._pendingIpcNotifications.clear();
}
sendPendingIpcNotifications() {
if (this._pendingIpcNotifications.size === 0) return;
const ipcs = new Map(this._pendingIpcNotifications);
this._pendingIpcNotifications.clear();
for (const msgOrFn of ipcs.values()) {
if (typeof msgOrFn === 'function') {
void msgOrFn();
} else {
void this.postMessage(msgOrFn);
}
}
}
}
export function replaceWebviewHtmlTokens<SerializedState>(

Carregando…
Cancelar
Salvar