Files
code-server/src/vs/base/common/decorators.ts
Joe Previte be3e823608 Squashed 'lib/vscode/' content from commit e5a624b788
git-subtree-dir: lib/vscode
git-subtree-split: e5a624b788d92b8d34d1392e4c4d9789406efe8f
2020-12-15 15:52:33 -07:00

156 lines
4.3 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function createDecorator(mapFn: (fn: Function, key: string) => Function): Function {
return (target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn) {
throw new Error('not supported');
}
descriptor[fnKey!] = mapFn(fn, key);
};
}
let memoizeId = 0;
export function createMemoizer() {
const memoizeKeyPrefix = `$memoize${memoizeId++}`;
let self: any = undefined;
const result = function memoize(target: any, key: string, descriptor: any) {
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
if (fn!.length !== 0) {
console.warn('Memoize should only be used in functions with zero parameters');
}
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn) {
throw new Error('not supported');
}
const memoizeKey = `${memoizeKeyPrefix}:${key}`;
descriptor[fnKey!] = function (...args: any[]) {
self = this;
if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, {
configurable: true,
enumerable: false,
writable: true,
value: fn!.apply(this, args)
});
}
return this[memoizeKey];
};
};
result.clear = () => {
if (typeof self === 'undefined') {
return;
}
Object.getOwnPropertyNames(self).forEach(property => {
if (property.indexOf(memoizeKeyPrefix) === 0) {
delete self[property];
}
});
};
return result;
}
export function memoize(target: any, key: string, descriptor: any) {
return createMemoizer()(target, key, descriptor);
}
export interface IDebounceReducer<T> {
(previousValue: T, ...args: any[]): T;
}
export function debounce<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$debounce$${key}`;
const resultKey = `$debounce$result$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
clearTimeout(this[timerKey]);
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
args = [this[resultKey]];
}
this[timerKey] = setTimeout(() => {
fn.apply(this, args);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, delay);
};
});
}
export function throttle<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$throttle$timer$${key}`;
const resultKey = `$throttle$result$${key}`;
const lastRunKey = `$throttle$lastRun$${key}`;
const pendingKey = `$throttle$pending$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
if (this[lastRunKey] === null || this[lastRunKey] === undefined) {
this[lastRunKey] = -Number.MAX_VALUE;
}
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
}
if (this[pendingKey]) {
return;
}
const nextTime = this[lastRunKey] + delay;
if (nextTime <= Date.now()) {
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
} else {
this[pendingKey] = true;
this[timerKey] = setTimeout(() => {
this[pendingKey] = false;
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, nextTime - Date.now());
}
};
});
}