monaco-editor
Version:
A browser based code editor
382 lines (377 loc) • 13.9 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 { onUnexpectedError } from './errors.js';
import { once as onceFn } from './functional.js';
import { combinedDisposable, Disposable, toDisposable } from './lifecycle.js';
import { LinkedList } from './linkedList.js';
export var Event;
(function (Event) {
var _disposable = { dispose: function () { } };
Event.None = function () { return _disposable; };
})(Event || (Event = {}));
/**
* The Emitter can be used to expose an Event to the public
* to fire it from the insides.
* Sample:
class Document {
private _onDidChange = new Emitter<(value:string)=>any>();
public onDidChange = this._onDidChange.event;
// getter-style
// get onDidChange(): Event<(value:string)=>any> {
// return this._onDidChange.event;
// }
private _doIt() {
//...
this._onDidChange.fire(value);
}
}
*/
var Emitter = /** @class */ (function () {
function Emitter(_options) {
if (_options === void 0) { _options = null; }
this._options = _options;
this._event = null;
this._disposed = false;
this._deliveryQueue = null;
this._listeners = null;
}
Object.defineProperty(Emitter.prototype, "event", {
/**
* For the public to allow to subscribe
* to events from this Emitter
*/
get: function () {
var _this = this;
if (!this._event) {
this._event = function (listener, thisArgs, disposables) {
if (!_this._listeners) {
_this._listeners = new LinkedList();
}
var firstListener = _this._listeners.isEmpty();
if (firstListener && _this._options && _this._options.onFirstListenerAdd) {
_this._options.onFirstListenerAdd(_this);
}
var remove = _this._listeners.push(!thisArgs ? listener : [listener, thisArgs]);
if (firstListener && _this._options && _this._options.onFirstListenerDidAdd) {
_this._options.onFirstListenerDidAdd(_this);
}
if (_this._options && _this._options.onListenerDidAdd) {
_this._options.onListenerDidAdd(_this, listener, thisArgs);
}
var result;
result = {
dispose: function () {
result.dispose = Emitter._noop;
if (!_this._disposed) {
remove();
if (_this._options && _this._options.onLastListenerRemove) {
var hasListeners = (_this._listeners && !_this._listeners.isEmpty());
if (!hasListeners) {
_this._options.onLastListenerRemove(_this);
}
}
}
}
};
if (Array.isArray(disposables)) {
disposables.push(result);
}
return result;
};
}
return this._event;
},
enumerable: true,
configurable: true
});
/**
* To be kept private to fire an event to
* subscribers
*/
Emitter.prototype.fire = function (event) {
if (this._listeners) {
// put all [listener,event]-pairs into delivery queue
// then emit all event. an inner/nested event might be
// the driver of this
if (!this._deliveryQueue) {
this._deliveryQueue = [];
}
for (var iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
this._deliveryQueue.push([e.value, event]);
}
while (this._deliveryQueue.length > 0) {
var _a = this._deliveryQueue.shift(), listener = _a[0], event_1 = _a[1];
try {
if (typeof listener === 'function') {
listener.call(undefined, event_1);
}
else {
listener[0].call(listener[1], event_1);
}
}
catch (e) {
onUnexpectedError(e);
}
}
}
};
Emitter.prototype.dispose = function () {
if (this._listeners) {
this._listeners = null;
}
if (this._deliveryQueue) {
this._deliveryQueue.length = 0;
}
this._disposed = true;
};
Emitter._noop = function () { };
return Emitter;
}());
export { Emitter };
var EventMultiplexer = /** @class */ (function () {
function EventMultiplexer() {
var _this = this;
this.hasListeners = false;
this.events = [];
this.emitter = new Emitter({
onFirstListenerAdd: function () { return _this.onFirstListenerAdd(); },
onLastListenerRemove: function () { return _this.onLastListenerRemove(); }
});
}
Object.defineProperty(EventMultiplexer.prototype, "event", {
get: function () {
return this.emitter.event;
},
enumerable: true,
configurable: true
});
EventMultiplexer.prototype.add = function (event) {
var _this = this;
var e = { event: event, listener: null };
this.events.push(e);
if (this.hasListeners) {
this.hook(e);
}
var dispose = function () {
if (_this.hasListeners) {
_this.unhook(e);
}
var idx = _this.events.indexOf(e);
_this.events.splice(idx, 1);
};
return toDisposable(onceFn(dispose));
};
EventMultiplexer.prototype.onFirstListenerAdd = function () {
var _this = this;
this.hasListeners = true;
this.events.forEach(function (e) { return _this.hook(e); });
};
EventMultiplexer.prototype.onLastListenerRemove = function () {
var _this = this;
this.hasListeners = false;
this.events.forEach(function (e) { return _this.unhook(e); });
};
EventMultiplexer.prototype.hook = function (e) {
var _this = this;
e.listener = e.event(function (r) { return _this.emitter.fire(r); });
};
EventMultiplexer.prototype.unhook = function (e) {
if (e.listener) {
e.listener.dispose();
}
e.listener = null;
};
EventMultiplexer.prototype.dispose = function () {
this.emitter.dispose();
};
return EventMultiplexer;
}());
export { EventMultiplexer };
export function once(event) {
return function (listener, thisArgs, disposables) {
if (thisArgs === void 0) { thisArgs = null; }
// we need this, in case the event fires during the listener call
var didFire = false;
var result = event(function (e) {
if (didFire) {
return;
}
else if (result) {
result.dispose();
}
else {
didFire = true;
}
return listener.call(thisArgs, e);
}, null, disposables);
if (didFire) {
result.dispose();
}
return result;
};
}
export function anyEvent() {
var events = [];
for (var _i = 0; _i < arguments.length; _i++) {
events[_i] = arguments[_i];
}
return function (listener, thisArgs, disposables) {
if (thisArgs === void 0) { thisArgs = null; }
return combinedDisposable(events.map(function (event) { return event(function (e) { return listener.call(thisArgs, e); }, null, disposables); }));
};
}
export function debounceEvent(event, merger, delay, leading) {
if (delay === void 0) { delay = 100; }
if (leading === void 0) { leading = false; }
var subscription;
var output = undefined;
var handle = undefined;
var numDebouncedCalls = 0;
var emitter = new Emitter({
onFirstListenerAdd: function () {
subscription = event(function (cur) {
numDebouncedCalls++;
output = merger(output, cur);
if (leading && !handle) {
emitter.fire(output);
}
clearTimeout(handle);
handle = setTimeout(function () {
var _output = output;
output = undefined;
handle = undefined;
if (!leading || numDebouncedCalls > 1) {
emitter.fire(_output);
}
numDebouncedCalls = 0;
}, delay);
});
},
onLastListenerRemove: function () {
subscription.dispose();
}
});
return emitter.event;
}
/**
* The EventDelayer is useful in situations in which you want
* to delay firing your events during some code.
* You can wrap that code and be sure that the event will not
* be fired during that wrap.
*
* ```
* const emitter: Emitter;
* const delayer = new EventDelayer();
* const delayedEvent = delayer.wrapEvent(emitter.event);
*
* delayedEvent(console.log);
*
* delayer.bufferEvents(() => {
* emitter.fire(); // event will not be fired yet
* });
*
* // event will only be fired at this point
* ```
*/
var EventBufferer = /** @class */ (function () {
function EventBufferer() {
this.buffers = [];
}
EventBufferer.prototype.wrapEvent = function (event) {
var _this = this;
return function (listener, thisArgs, disposables) {
return event(function (i) {
var buffer = _this.buffers[_this.buffers.length - 1];
if (buffer) {
buffer.push(function () { return listener.call(thisArgs, i); });
}
else {
listener.call(thisArgs, i);
}
}, void 0, disposables);
};
};
EventBufferer.prototype.bufferEvents = function (fn) {
var buffer = [];
this.buffers.push(buffer);
var r = fn();
this.buffers.pop();
buffer.forEach(function (flush) { return flush(); });
return r;
};
return EventBufferer;
}());
export { EventBufferer };
export function mapEvent(event, map) {
return function (listener, thisArgs, disposables) {
if (thisArgs === void 0) { thisArgs = null; }
return event(function (i) { return listener.call(thisArgs, map(i)); }, null, disposables);
};
}
export function filterEvent(event, filter) {
return function (listener, thisArgs, disposables) {
if (thisArgs === void 0) { thisArgs = null; }
return event(function (e) { return filter(e) && listener.call(thisArgs, e); }, null, disposables);
};
}
var ChainableEvent = /** @class */ (function () {
function ChainableEvent(_event) {
this._event = _event;
}
Object.defineProperty(ChainableEvent.prototype, "event", {
get: function () { return this._event; },
enumerable: true,
configurable: true
});
ChainableEvent.prototype.map = function (fn) {
return new ChainableEvent(mapEvent(this._event, fn));
};
ChainableEvent.prototype.filter = function (fn) {
return new ChainableEvent(filterEvent(this._event, fn));
};
ChainableEvent.prototype.on = function (listener, thisArgs, disposables) {
return this._event(listener, thisArgs, disposables);
};
return ChainableEvent;
}());
export function chain(event) {
return new ChainableEvent(event);
}
var Relay = /** @class */ (function () {
function Relay() {
var _this = this;
this.listening = false;
this.inputEvent = Event.None;
this.inputEventListener = Disposable.None;
this.emitter = new Emitter({
onFirstListenerDidAdd: function () {
_this.listening = true;
_this.inputEventListener = _this.inputEvent(_this.emitter.fire, _this.emitter);
},
onLastListenerRemove: function () {
_this.listening = false;
_this.inputEventListener.dispose();
}
});
this.event = this.emitter.event;
}
Object.defineProperty(Relay.prototype, "input", {
set: function (event) {
this.inputEvent = event;
if (this.listening) {
this.inputEventListener.dispose();
this.inputEventListener = event(this.emitter.fire, this.emitter);
}
},
enumerable: true,
configurable: true
});
Relay.prototype.dispose = function () {
this.inputEventListener.dispose();
this.emitter.dispose();
};
return Relay;
}());
export { Relay };