Skip to content

Commit 1ecf9fc

Browse files
committed
Fixes view proxy method bindings
Adds proxy & bound method caching
1 parent 0e2ad19 commit 1ecf9fc

File tree

1 file changed

+65
-15
lines changed

1 file changed

+65
-15
lines changed

src/views/views.ts

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -285,29 +285,79 @@ export class Views implements Disposable {
285285
];
286286
}
287287

288+
private readonly _scmGroupedViewProxyCache = new Map<
289+
GroupableTreeViewTypes,
290+
TreeViewByType[GroupableTreeViewTypes]
291+
>();
292+
288293
private getScmGroupedView<T extends GroupableTreeViewTypes>(type: T): TreeViewByType[T] {
289-
// eslint-disable-next-line @typescript-eslint/no-this-alias
290-
const self = this;
294+
let proxy = this._scmGroupedViewProxyCache.get(type) as TreeViewByType[T] | undefined;
295+
if (proxy != null) return proxy;
291296

292-
// Use a proxy to guard against the view not existing or having been disposed
297+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
298+
const methodCache = new Map<string | symbol, Function | undefined>();
299+
300+
// Use a proxy to lazily initialize the view (guard against the view not existing or having been disposed) and cache method bindings for performance
293301

294302
let view: TreeViewByType[T] | undefined;
295-
const proxy = new Proxy<TreeViewByType[T]>(Object.create(null) as TreeViewByType[T], {
296-
get: function (_target, prop) {
297-
if (view == null || view.disposed) {
298-
if (self._scmGroupedView == null) {
299-
// Don't bother creating the view if we are just checking visibility
300-
if (prop === 'visible') return false;
301-
302-
self.updateScmGroupedViewsRegistration(true);
303-
}
304-
view = self._scmGroupedView!.setView(type);
303+
const ensureView = () => {
304+
if (view == null || view.disposed) {
305+
methodCache.clear();
306+
307+
if (this._scmGroupedView == null) {
308+
this.updateScmGroupedViewsRegistration(true);
305309
}
310+
view = this._scmGroupedView!.setView(type);
311+
if (view == null) {
312+
debugger;
313+
throw new Error(`Unable to initialize view: ${type}`);
314+
}
315+
}
316+
return view;
317+
};
318+
319+
proxy = new Proxy<TreeViewByType[T]>(Object.create(null) as TreeViewByType[T], {
320+
get: function (_, prop) {
321+
// Fast path for visibility check
322+
if (prop === 'visible') {
323+
return view != null && !view.disposed && view.visible;
324+
}
325+
326+
const target = ensureView();
327+
const value = Reflect.get(target, prop, target);
306328

307-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
308-
return (view as any)[prop];
329+
// Fast return for non-functions
330+
if (typeof value !== 'function') return value;
331+
332+
// Check method cache
333+
let method = methodCache.get(prop);
334+
if (method == null) {
335+
method = value.bind(target);
336+
methodCache.set(prop, method);
337+
}
338+
return method;
339+
},
340+
set: function (_target, prop, value, receiver) {
341+
return Reflect.set(ensureView(), prop, value, receiver);
342+
},
343+
has: function (_target, prop) {
344+
return Reflect.has(ensureView(), prop);
345+
},
346+
getOwnPropertyDescriptor: function (_target, prop) {
347+
return Reflect.getOwnPropertyDescriptor(ensureView(), prop);
348+
},
349+
defineProperty: function (_target, prop, descriptor) {
350+
return Reflect.defineProperty(ensureView(), prop, descriptor);
351+
},
352+
deleteProperty: function (_target, prop) {
353+
return Reflect.deleteProperty(ensureView(), prop);
354+
},
355+
ownKeys: function (_target) {
356+
return Reflect.ownKeys(ensureView());
309357
},
310358
});
359+
360+
this._scmGroupedViewProxyCache.set(type, proxy);
311361
return proxy;
312362
}
313363

0 commit comments

Comments
 (0)