UNPKG

monaco-editor

Version:
382 lines (377 loc) • 13.9 kB
/*--------------------------------------------------------------------------------------------- * 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 };