monaco-editor-core
Version:
A browser based code editor
190 lines • 5.98 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { autorun } from '../reactions/autorun.js';
import { DisposableStore, toDisposable } from '../commonFacade/deps.js';
import { derived, derivedOpts } from '../observables/derived.js';
import { observableFromEvent } from '../observables/observableFromEvent.js';
import { observableSignal } from '../observables/observableSignal.js';
import { _setKeepObserved, _setRecomputeInitiallyAndOnChange } from '../observables/baseObservable.js';
/**
* Creates an observable that debounces the input observable.
*/
export function debouncedObservable(observable, debounceMs) {
let hasValue = false;
let lastValue;
let timeout = undefined;
return observableFromEvent(cb => {
const d = autorun(reader => {
const value = observable.read(reader);
if (!hasValue) {
hasValue = true;
lastValue = value;
}
else {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
lastValue = value;
cb();
}, debounceMs);
}
});
return {
dispose() {
d.dispose();
hasValue = false;
lastValue = undefined;
},
};
}, () => {
if (hasValue) {
return lastValue;
}
else {
return observable.get();
}
});
}
/**
* This makes sure the observable is being observed and keeps its cache alive.
*/
export function keepObserved(observable) {
const o = new KeepAliveObserver(false, undefined);
observable.addObserver(o);
return toDisposable(() => {
observable.removeObserver(o);
});
}
_setKeepObserved(keepObserved);
/**
* This converts the given observable into an autorun.
*/
export function recomputeInitiallyAndOnChange(observable, handleValue) {
const o = new KeepAliveObserver(true, handleValue);
observable.addObserver(o);
try {
o.beginUpdate(observable);
}
finally {
o.endUpdate(observable);
}
return toDisposable(() => {
observable.removeObserver(o);
});
}
_setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
export class KeepAliveObserver {
constructor(_forceRecompute, _handleValue) {
this._forceRecompute = _forceRecompute;
this._handleValue = _handleValue;
this._counter = 0;
}
beginUpdate(observable) {
this._counter++;
}
endUpdate(observable) {
if (this._counter === 1 && this._forceRecompute) {
if (this._handleValue) {
this._handleValue(observable.get());
}
else {
observable.reportChanges();
}
}
this._counter--;
}
handlePossibleChange(observable) {
// NO OP
}
handleChange(observable, change) {
// NO OP
}
}
export function derivedObservableWithCache(owner, computeFn) {
let lastValue = undefined;
const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
lastValue = computeFn(reader, lastValue);
return lastValue;
});
return observable;
}
export function derivedObservableWithWritableCache(owner, computeFn) {
let lastValue = undefined;
const onChange = observableSignal('derivedObservableWithWritableCache');
const observable = derived(owner, reader => {
onChange.read(reader);
lastValue = computeFn(reader, lastValue);
return lastValue;
});
return Object.assign(observable, {
clearCache: (tx) => {
lastValue = undefined;
onChange.trigger(tx);
},
setCache: (newValue, tx) => {
lastValue = newValue;
onChange.trigger(tx);
}
});
}
/**
* When the items array changes, referential equal items are not mapped again.
*/
export function mapObservableArrayCached(owner, items, map, keySelector) {
let m = new ArrayMap(map, keySelector);
const self = derivedOpts({
debugReferenceFn: map,
owner,
onLastObserverRemoved: () => {
m.dispose();
m = new ArrayMap(map);
}
}, (reader) => {
m.setItems(items.read(reader));
return m.getItems();
});
return self;
}
class ArrayMap {
constructor(_map, _keySelector) {
this._map = _map;
this._keySelector = _keySelector;
this._cache = new Map();
this._items = [];
}
dispose() {
this._cache.forEach(entry => entry.store.dispose());
this._cache.clear();
}
setItems(items) {
const newItems = [];
const itemsToRemove = new Set(this._cache.keys());
for (const item of items) {
const key = this._keySelector ? this._keySelector(item) : item;
let entry = this._cache.get(key);
if (!entry) {
const store = new DisposableStore();
const out = this._map(item, store);
entry = { out, store };
this._cache.set(key, entry);
}
else {
itemsToRemove.delete(key);
}
newItems.push(entry.out);
}
for (const item of itemsToRemove) {
const entry = this._cache.get(item);
entry.store.dispose();
this._cache.delete(item);
}
this._items = newItems;
}
getItems() {
return this._items;
}
}
//# sourceMappingURL=utils.js.map