event.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. import { onUnexpectedError } from './errors.js';
  2. import { combinedDisposable, Disposable, DisposableStore, SafeDisposable, toDisposable } from './lifecycle.js';
  3. import { LinkedList } from './linkedList.js';
  4. import { StopWatch } from './stopwatch.js';
  5. // -----------------------------------------------------------------------------------------------------------------------
  6. // Uncomment the next line to print warnings whenever an emitter with listeners is disposed. That is a sign of code smell.
  7. // -----------------------------------------------------------------------------------------------------------------------
  8. const _enableDisposeWithListenerWarning = false;
  9. // _enableDisposeWithListenerWarning = Boolean("TRUE"); // causes a linter warning so that it cannot be pushed
  10. // -----------------------------------------------------------------------------------------------------------------------
  11. // Uncomment the next line to print warnings whenever a snapshotted event is used repeatedly without cleanup.
  12. // See https://github.com/microsoft/vscode/issues/142851
  13. // -----------------------------------------------------------------------------------------------------------------------
  14. const _enableSnapshotPotentialLeakWarning = false;
  15. export var Event;
  16. (function (Event) {
  17. Event.None = () => Disposable.None;
  18. function _addLeakageTraceLogic(options) {
  19. if (_enableSnapshotPotentialLeakWarning) {
  20. const { onListenerDidAdd: origListenerDidAdd } = options;
  21. const stack = Stacktrace.create();
  22. let count = 0;
  23. options.onListenerDidAdd = () => {
  24. if (++count === 2) {
  25. console.warn('snapshotted emitter LIKELY used public and SHOULD HAVE BEEN created with DisposableStore. snapshotted here');
  26. stack.print();
  27. }
  28. origListenerDidAdd === null || origListenerDidAdd === void 0 ? void 0 : origListenerDidAdd();
  29. };
  30. }
  31. }
  32. /**
  33. * Given an event, returns another event which only fires once.
  34. */
  35. function once(event) {
  36. return (listener, thisArgs = null, disposables) => {
  37. // we need this, in case the event fires during the listener call
  38. let didFire = false;
  39. let result = undefined;
  40. result = event(e => {
  41. if (didFire) {
  42. return;
  43. }
  44. else if (result) {
  45. result.dispose();
  46. }
  47. else {
  48. didFire = true;
  49. }
  50. return listener.call(thisArgs, e);
  51. }, null, disposables);
  52. if (didFire) {
  53. result.dispose();
  54. }
  55. return result;
  56. };
  57. }
  58. Event.once = once;
  59. /**
  60. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  61. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  62. * returned event causes this utility to leak a listener on the original event.
  63. */
  64. function map(event, map, disposable) {
  65. return snapshot((listener, thisArgs = null, disposables) => event(i => listener.call(thisArgs, map(i)), null, disposables), disposable);
  66. }
  67. Event.map = map;
  68. /**
  69. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  70. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  71. * returned event causes this utility to leak a listener on the original event.
  72. */
  73. function forEach(event, each, disposable) {
  74. return snapshot((listener, thisArgs = null, disposables) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables), disposable);
  75. }
  76. Event.forEach = forEach;
  77. function filter(event, filter, disposable) {
  78. return snapshot((listener, thisArgs = null, disposables) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables), disposable);
  79. }
  80. Event.filter = filter;
  81. /**
  82. * Given an event, returns the same event but typed as `Event<void>`.
  83. */
  84. function signal(event) {
  85. return event;
  86. }
  87. Event.signal = signal;
  88. function any(...events) {
  89. return (listener, thisArgs = null, disposables) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
  90. }
  91. Event.any = any;
  92. /**
  93. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  94. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  95. * returned event causes this utility to leak a listener on the original event.
  96. */
  97. function reduce(event, merge, initial, disposable) {
  98. let output = initial;
  99. return map(event, e => {
  100. output = merge(output, e);
  101. return output;
  102. }, disposable);
  103. }
  104. Event.reduce = reduce;
  105. function snapshot(event, disposable) {
  106. let listener;
  107. const options = {
  108. onFirstListenerAdd() {
  109. listener = event(emitter.fire, emitter);
  110. },
  111. onLastListenerRemove() {
  112. listener === null || listener === void 0 ? void 0 : listener.dispose();
  113. }
  114. };
  115. if (!disposable) {
  116. _addLeakageTraceLogic(options);
  117. }
  118. const emitter = new Emitter(options);
  119. disposable === null || disposable === void 0 ? void 0 : disposable.add(emitter);
  120. return emitter.event;
  121. }
  122. function debounce(event, merge, delay = 100, leading = false, leakWarningThreshold, disposable) {
  123. let subscription;
  124. let output = undefined;
  125. let handle = undefined;
  126. let numDebouncedCalls = 0;
  127. const options = {
  128. leakWarningThreshold,
  129. onFirstListenerAdd() {
  130. subscription = event(cur => {
  131. numDebouncedCalls++;
  132. output = merge(output, cur);
  133. if (leading && !handle) {
  134. emitter.fire(output);
  135. output = undefined;
  136. }
  137. clearTimeout(handle);
  138. handle = setTimeout(() => {
  139. const _output = output;
  140. output = undefined;
  141. handle = undefined;
  142. if (!leading || numDebouncedCalls > 1) {
  143. emitter.fire(_output);
  144. }
  145. numDebouncedCalls = 0;
  146. }, delay);
  147. });
  148. },
  149. onLastListenerRemove() {
  150. subscription.dispose();
  151. }
  152. };
  153. if (!disposable) {
  154. _addLeakageTraceLogic(options);
  155. }
  156. const emitter = new Emitter(options);
  157. disposable === null || disposable === void 0 ? void 0 : disposable.add(emitter);
  158. return emitter.event;
  159. }
  160. Event.debounce = debounce;
  161. /**
  162. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  163. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  164. * returned event causes this utility to leak a listener on the original event.
  165. */
  166. function latch(event, equals = (a, b) => a === b, disposable) {
  167. let firstCall = true;
  168. let cache;
  169. return filter(event, value => {
  170. const shouldEmit = firstCall || !equals(value, cache);
  171. firstCall = false;
  172. cache = value;
  173. return shouldEmit;
  174. }, disposable);
  175. }
  176. Event.latch = latch;
  177. /**
  178. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  179. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  180. * returned event causes this utility to leak a listener on the original event.
  181. */
  182. function split(event, isT, disposable) {
  183. return [
  184. Event.filter(event, isT, disposable),
  185. Event.filter(event, e => !isT(e), disposable),
  186. ];
  187. }
  188. Event.split = split;
  189. /**
  190. * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
  191. * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
  192. * returned event causes this utility to leak a listener on the original event.
  193. */
  194. function buffer(event, flushAfterTimeout = false, _buffer = []) {
  195. let buffer = _buffer.slice();
  196. let listener = event(e => {
  197. if (buffer) {
  198. buffer.push(e);
  199. }
  200. else {
  201. emitter.fire(e);
  202. }
  203. });
  204. const flush = () => {
  205. buffer === null || buffer === void 0 ? void 0 : buffer.forEach(e => emitter.fire(e));
  206. buffer = null;
  207. };
  208. const emitter = new Emitter({
  209. onFirstListenerAdd() {
  210. if (!listener) {
  211. listener = event(e => emitter.fire(e));
  212. }
  213. },
  214. onFirstListenerDidAdd() {
  215. if (buffer) {
  216. if (flushAfterTimeout) {
  217. setTimeout(flush);
  218. }
  219. else {
  220. flush();
  221. }
  222. }
  223. },
  224. onLastListenerRemove() {
  225. if (listener) {
  226. listener.dispose();
  227. }
  228. listener = null;
  229. }
  230. });
  231. return emitter.event;
  232. }
  233. Event.buffer = buffer;
  234. class ChainableEvent {
  235. constructor(event) {
  236. this.event = event;
  237. this.disposables = new DisposableStore();
  238. }
  239. map(fn) {
  240. return new ChainableEvent(map(this.event, fn, this.disposables));
  241. }
  242. forEach(fn) {
  243. return new ChainableEvent(forEach(this.event, fn, this.disposables));
  244. }
  245. filter(fn) {
  246. return new ChainableEvent(filter(this.event, fn, this.disposables));
  247. }
  248. reduce(merge, initial) {
  249. return new ChainableEvent(reduce(this.event, merge, initial, this.disposables));
  250. }
  251. latch() {
  252. return new ChainableEvent(latch(this.event, undefined, this.disposables));
  253. }
  254. debounce(merge, delay = 100, leading = false, leakWarningThreshold) {
  255. return new ChainableEvent(debounce(this.event, merge, delay, leading, leakWarningThreshold, this.disposables));
  256. }
  257. on(listener, thisArgs, disposables) {
  258. return this.event(listener, thisArgs, disposables);
  259. }
  260. once(listener, thisArgs, disposables) {
  261. return once(this.event)(listener, thisArgs, disposables);
  262. }
  263. dispose() {
  264. this.disposables.dispose();
  265. }
  266. }
  267. function chain(event) {
  268. return new ChainableEvent(event);
  269. }
  270. Event.chain = chain;
  271. function fromNodeEventEmitter(emitter, eventName, map = id => id) {
  272. const fn = (...args) => result.fire(map(...args));
  273. const onFirstListenerAdd = () => emitter.on(eventName, fn);
  274. const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
  275. const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove });
  276. return result.event;
  277. }
  278. Event.fromNodeEventEmitter = fromNodeEventEmitter;
  279. function fromDOMEventEmitter(emitter, eventName, map = id => id) {
  280. const fn = (...args) => result.fire(map(...args));
  281. const onFirstListenerAdd = () => emitter.addEventListener(eventName, fn);
  282. const onLastListenerRemove = () => emitter.removeEventListener(eventName, fn);
  283. const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove });
  284. return result.event;
  285. }
  286. Event.fromDOMEventEmitter = fromDOMEventEmitter;
  287. function toPromise(event) {
  288. return new Promise(resolve => once(event)(resolve));
  289. }
  290. Event.toPromise = toPromise;
  291. function runAndSubscribe(event, handler) {
  292. handler(undefined);
  293. return event(e => handler(e));
  294. }
  295. Event.runAndSubscribe = runAndSubscribe;
  296. function runAndSubscribeWithStore(event, handler) {
  297. let store = null;
  298. function run(e) {
  299. store === null || store === void 0 ? void 0 : store.dispose();
  300. store = new DisposableStore();
  301. handler(e, store);
  302. }
  303. run(undefined);
  304. const disposable = event(e => run(e));
  305. return toDisposable(() => {
  306. disposable.dispose();
  307. store === null || store === void 0 ? void 0 : store.dispose();
  308. });
  309. }
  310. Event.runAndSubscribeWithStore = runAndSubscribeWithStore;
  311. class EmitterObserver {
  312. constructor(obs, store) {
  313. this.obs = obs;
  314. this._counter = 0;
  315. this._hasChanged = false;
  316. const options = {
  317. onFirstListenerAdd: () => {
  318. obs.addObserver(this);
  319. },
  320. onLastListenerRemove: () => {
  321. obs.removeObserver(this);
  322. }
  323. };
  324. if (!store) {
  325. _addLeakageTraceLogic(options);
  326. }
  327. this.emitter = new Emitter(options);
  328. if (store) {
  329. store.add(this.emitter);
  330. }
  331. }
  332. beginUpdate(_observable) {
  333. // console.assert(_observable === this.obs);
  334. this._counter++;
  335. }
  336. handleChange(_observable, _change) {
  337. this._hasChanged = true;
  338. }
  339. endUpdate(_observable) {
  340. if (--this._counter === 0) {
  341. if (this._hasChanged) {
  342. this._hasChanged = false;
  343. this.emitter.fire(this.obs.get());
  344. }
  345. }
  346. }
  347. }
  348. function fromObservable(obs, store) {
  349. const observer = new EmitterObserver(obs, store);
  350. return observer.emitter.event;
  351. }
  352. Event.fromObservable = fromObservable;
  353. })(Event || (Event = {}));
  354. class EventProfiling {
  355. constructor(name) {
  356. this._listenerCount = 0;
  357. this._invocationCount = 0;
  358. this._elapsedOverall = 0;
  359. this._name = `${name}_${EventProfiling._idPool++}`;
  360. }
  361. start(listenerCount) {
  362. this._stopWatch = new StopWatch(true);
  363. this._listenerCount = listenerCount;
  364. }
  365. stop() {
  366. if (this._stopWatch) {
  367. const elapsed = this._stopWatch.elapsed();
  368. this._elapsedOverall += elapsed;
  369. this._invocationCount += 1;
  370. console.info(`did FIRE ${this._name}: elapsed_ms: ${elapsed.toFixed(5)}, listener: ${this._listenerCount} (elapsed_overall: ${this._elapsedOverall.toFixed(2)}, invocations: ${this._invocationCount})`);
  371. this._stopWatch = undefined;
  372. }
  373. }
  374. }
  375. EventProfiling._idPool = 0;
  376. let _globalLeakWarningThreshold = -1;
  377. class LeakageMonitor {
  378. constructor(customThreshold, name = Math.random().toString(18).slice(2, 5)) {
  379. this.customThreshold = customThreshold;
  380. this.name = name;
  381. this._warnCountdown = 0;
  382. }
  383. dispose() {
  384. if (this._stacks) {
  385. this._stacks.clear();
  386. }
  387. }
  388. check(stack, listenerCount) {
  389. let threshold = _globalLeakWarningThreshold;
  390. if (typeof this.customThreshold === 'number') {
  391. threshold = this.customThreshold;
  392. }
  393. if (threshold <= 0 || listenerCount < threshold) {
  394. return undefined;
  395. }
  396. if (!this._stacks) {
  397. this._stacks = new Map();
  398. }
  399. const count = (this._stacks.get(stack.value) || 0);
  400. this._stacks.set(stack.value, count + 1);
  401. this._warnCountdown -= 1;
  402. if (this._warnCountdown <= 0) {
  403. // only warn on first exceed and then every time the limit
  404. // is exceeded by 50% again
  405. this._warnCountdown = threshold * 0.5;
  406. // find most frequent listener and print warning
  407. let topStack;
  408. let topCount = 0;
  409. for (const [stack, count] of this._stacks) {
  410. if (!topStack || topCount < count) {
  411. topStack = stack;
  412. topCount = count;
  413. }
  414. }
  415. console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`);
  416. console.warn(topStack);
  417. }
  418. return () => {
  419. const count = (this._stacks.get(stack.value) || 0);
  420. this._stacks.set(stack.value, count - 1);
  421. };
  422. }
  423. }
  424. class Stacktrace {
  425. constructor(value) {
  426. this.value = value;
  427. }
  428. static create() {
  429. var _a;
  430. return new Stacktrace((_a = new Error().stack) !== null && _a !== void 0 ? _a : '');
  431. }
  432. print() {
  433. console.warn(this.value.split('\n').slice(2).join('\n'));
  434. }
  435. }
  436. class Listener {
  437. constructor(callback, callbackThis, stack) {
  438. this.callback = callback;
  439. this.callbackThis = callbackThis;
  440. this.stack = stack;
  441. this.subscription = new SafeDisposable();
  442. }
  443. invoke(e) {
  444. this.callback.call(this.callbackThis, e);
  445. }
  446. }
  447. /**
  448. * The Emitter can be used to expose an Event to the public
  449. * to fire it from the insides.
  450. * Sample:
  451. class Document {
  452. private readonly _onDidChange = new Emitter<(value:string)=>any>();
  453. public onDidChange = this._onDidChange.event;
  454. // getter-style
  455. // get onDidChange(): Event<(value:string)=>any> {
  456. // return this._onDidChange.event;
  457. // }
  458. private _doIt() {
  459. //...
  460. this._onDidChange.fire(value);
  461. }
  462. }
  463. */
  464. export class Emitter {
  465. constructor(options) {
  466. var _a, _b;
  467. this._disposed = false;
  468. this._options = options;
  469. this._leakageMon = _globalLeakWarningThreshold > 0 ? new LeakageMonitor(this._options && this._options.leakWarningThreshold) : undefined;
  470. this._perfMon = ((_a = this._options) === null || _a === void 0 ? void 0 : _a._profName) ? new EventProfiling(this._options._profName) : undefined;
  471. this._deliveryQueue = (_b = this._options) === null || _b === void 0 ? void 0 : _b.deliveryQueue;
  472. }
  473. dispose() {
  474. var _a, _b, _c, _d;
  475. if (!this._disposed) {
  476. this._disposed = true;
  477. // It is bad to have listeners at the time of disposing an emitter, it is worst to have listeners keep the emitter
  478. // alive via the reference that's embedded in their disposables. Therefore we loop over all remaining listeners and
  479. // unset their subscriptions/disposables. Looping and blaming remaining listeners is done on next tick because the
  480. // the following programming pattern is very popular:
  481. //
  482. // const someModel = this._disposables.add(new ModelObject()); // (1) create and register model
  483. // this._disposables.add(someModel.onDidChange(() => { ... }); // (2) subscribe and register model-event listener
  484. // ...later...
  485. // this._disposables.dispose(); disposes (1) then (2): don't warn after (1) but after the "overall dispose" is done
  486. if (this._listeners) {
  487. if (_enableDisposeWithListenerWarning) {
  488. const listeners = Array.from(this._listeners);
  489. queueMicrotask(() => {
  490. var _a;
  491. for (const listener of listeners) {
  492. if (listener.subscription.isset()) {
  493. listener.subscription.unset();
  494. (_a = listener.stack) === null || _a === void 0 ? void 0 : _a.print();
  495. }
  496. }
  497. });
  498. }
  499. this._listeners.clear();
  500. }
  501. (_a = this._deliveryQueue) === null || _a === void 0 ? void 0 : _a.clear(this);
  502. (_c = (_b = this._options) === null || _b === void 0 ? void 0 : _b.onLastListenerRemove) === null || _c === void 0 ? void 0 : _c.call(_b);
  503. (_d = this._leakageMon) === null || _d === void 0 ? void 0 : _d.dispose();
  504. }
  505. }
  506. /**
  507. * For the public to allow to subscribe
  508. * to events from this Emitter
  509. */
  510. get event() {
  511. if (!this._event) {
  512. this._event = (callback, thisArgs, disposables) => {
  513. var _a, _b, _c;
  514. if (!this._listeners) {
  515. this._listeners = new LinkedList();
  516. }
  517. const firstListener = this._listeners.isEmpty();
  518. if (firstListener && ((_a = this._options) === null || _a === void 0 ? void 0 : _a.onFirstListenerAdd)) {
  519. this._options.onFirstListenerAdd(this);
  520. }
  521. let removeMonitor;
  522. let stack;
  523. if (this._leakageMon && this._listeners.size >= 30) {
  524. // check and record this emitter for potential leakage
  525. stack = Stacktrace.create();
  526. removeMonitor = this._leakageMon.check(stack, this._listeners.size + 1);
  527. }
  528. if (_enableDisposeWithListenerWarning) {
  529. stack = stack !== null && stack !== void 0 ? stack : Stacktrace.create();
  530. }
  531. const listener = new Listener(callback, thisArgs, stack);
  532. const removeListener = this._listeners.push(listener);
  533. if (firstListener && ((_b = this._options) === null || _b === void 0 ? void 0 : _b.onFirstListenerDidAdd)) {
  534. this._options.onFirstListenerDidAdd(this);
  535. }
  536. if ((_c = this._options) === null || _c === void 0 ? void 0 : _c.onListenerDidAdd) {
  537. this._options.onListenerDidAdd(this, callback, thisArgs);
  538. }
  539. const result = listener.subscription.set(() => {
  540. removeMonitor === null || removeMonitor === void 0 ? void 0 : removeMonitor();
  541. if (!this._disposed) {
  542. removeListener();
  543. if (this._options && this._options.onLastListenerRemove) {
  544. const hasListeners = (this._listeners && !this._listeners.isEmpty());
  545. if (!hasListeners) {
  546. this._options.onLastListenerRemove(this);
  547. }
  548. }
  549. }
  550. });
  551. if (disposables instanceof DisposableStore) {
  552. disposables.add(result);
  553. }
  554. else if (Array.isArray(disposables)) {
  555. disposables.push(result);
  556. }
  557. return result;
  558. };
  559. }
  560. return this._event;
  561. }
  562. /**
  563. * To be kept private to fire an event to
  564. * subscribers
  565. */
  566. fire(event) {
  567. var _a, _b;
  568. if (this._listeners) {
  569. // put all [listener,event]-pairs into delivery queue
  570. // then emit all event. an inner/nested event might be
  571. // the driver of this
  572. if (!this._deliveryQueue) {
  573. this._deliveryQueue = new PrivateEventDeliveryQueue();
  574. }
  575. for (const listener of this._listeners) {
  576. this._deliveryQueue.push(this, listener, event);
  577. }
  578. // start/stop performance insight collection
  579. (_a = this._perfMon) === null || _a === void 0 ? void 0 : _a.start(this._deliveryQueue.size);
  580. this._deliveryQueue.deliver();
  581. (_b = this._perfMon) === null || _b === void 0 ? void 0 : _b.stop();
  582. }
  583. }
  584. }
  585. export class EventDeliveryQueue {
  586. constructor() {
  587. this._queue = new LinkedList();
  588. }
  589. get size() {
  590. return this._queue.size;
  591. }
  592. push(emitter, listener, event) {
  593. this._queue.push(new EventDeliveryQueueElement(emitter, listener, event));
  594. }
  595. clear(emitter) {
  596. const newQueue = new LinkedList();
  597. for (const element of this._queue) {
  598. if (element.emitter !== emitter) {
  599. newQueue.push(element);
  600. }
  601. }
  602. this._queue = newQueue;
  603. }
  604. deliver() {
  605. while (this._queue.size > 0) {
  606. const element = this._queue.shift();
  607. try {
  608. element.listener.invoke(element.event);
  609. }
  610. catch (e) {
  611. onUnexpectedError(e);
  612. }
  613. }
  614. }
  615. }
  616. /**
  617. * An `EventDeliveryQueue` that is guaranteed to be used by a single `Emitter`.
  618. */
  619. class PrivateEventDeliveryQueue extends EventDeliveryQueue {
  620. clear(emitter) {
  621. // Here we can just clear the entire linked list because
  622. // all elements are guaranteed to belong to this emitter
  623. this._queue.clear();
  624. }
  625. }
  626. class EventDeliveryQueueElement {
  627. constructor(emitter, listener, event) {
  628. this.emitter = emitter;
  629. this.listener = listener;
  630. this.event = event;
  631. }
  632. }
  633. export class PauseableEmitter extends Emitter {
  634. constructor(options) {
  635. super(options);
  636. this._isPaused = 0;
  637. this._eventQueue = new LinkedList();
  638. this._mergeFn = options === null || options === void 0 ? void 0 : options.merge;
  639. }
  640. pause() {
  641. this._isPaused++;
  642. }
  643. resume() {
  644. if (this._isPaused !== 0 && --this._isPaused === 0) {
  645. if (this._mergeFn) {
  646. // use the merge function to create a single composite
  647. // event. make a copy in case firing pauses this emitter
  648. const events = Array.from(this._eventQueue);
  649. this._eventQueue.clear();
  650. super.fire(this._mergeFn(events));
  651. }
  652. else {
  653. // no merging, fire each event individually and test
  654. // that this emitter isn't paused halfway through
  655. while (!this._isPaused && this._eventQueue.size !== 0) {
  656. super.fire(this._eventQueue.shift());
  657. }
  658. }
  659. }
  660. }
  661. fire(event) {
  662. if (this._listeners) {
  663. if (this._isPaused !== 0) {
  664. this._eventQueue.push(event);
  665. }
  666. else {
  667. super.fire(event);
  668. }
  669. }
  670. }
  671. }
  672. export class DebounceEmitter extends PauseableEmitter {
  673. constructor(options) {
  674. var _a;
  675. super(options);
  676. this._delay = (_a = options.delay) !== null && _a !== void 0 ? _a : 100;
  677. }
  678. fire(event) {
  679. if (!this._handle) {
  680. this.pause();
  681. this._handle = setTimeout(() => {
  682. this._handle = undefined;
  683. this.resume();
  684. }, this._delay);
  685. }
  686. super.fire(event);
  687. }
  688. }
  689. /**
  690. * The EventBufferer is useful in situations in which you want
  691. * to delay firing your events during some code.
  692. * You can wrap that code and be sure that the event will not
  693. * be fired during that wrap.
  694. *
  695. * ```
  696. * const emitter: Emitter;
  697. * const delayer = new EventDelayer();
  698. * const delayedEvent = delayer.wrapEvent(emitter.event);
  699. *
  700. * delayedEvent(console.log);
  701. *
  702. * delayer.bufferEvents(() => {
  703. * emitter.fire(); // event will not be fired yet
  704. * });
  705. *
  706. * // event will only be fired at this point
  707. * ```
  708. */
  709. export class EventBufferer {
  710. constructor() {
  711. this.buffers = [];
  712. }
  713. wrapEvent(event) {
  714. return (listener, thisArgs, disposables) => {
  715. return event(i => {
  716. const buffer = this.buffers[this.buffers.length - 1];
  717. if (buffer) {
  718. buffer.push(() => listener.call(thisArgs, i));
  719. }
  720. else {
  721. listener.call(thisArgs, i);
  722. }
  723. }, undefined, disposables);
  724. };
  725. }
  726. bufferEvents(fn) {
  727. const buffer = [];
  728. this.buffers.push(buffer);
  729. const r = fn();
  730. this.buffers.pop();
  731. buffer.forEach(flush => flush());
  732. return r;
  733. }
  734. }
  735. /**
  736. * A Relay is an event forwarder which functions as a replugabble event pipe.
  737. * Once created, you can connect an input event to it and it will simply forward
  738. * events from that input event through its own `event` property. The `input`
  739. * can be changed at any point in time.
  740. */
  741. export class Relay {
  742. constructor() {
  743. this.listening = false;
  744. this.inputEvent = Event.None;
  745. this.inputEventListener = Disposable.None;
  746. this.emitter = new Emitter({
  747. onFirstListenerDidAdd: () => {
  748. this.listening = true;
  749. this.inputEventListener = this.inputEvent(this.emitter.fire, this.emitter);
  750. },
  751. onLastListenerRemove: () => {
  752. this.listening = false;
  753. this.inputEventListener.dispose();
  754. }
  755. });
  756. this.event = this.emitter.event;
  757. }
  758. set input(event) {
  759. this.inputEvent = event;
  760. if (this.listening) {
  761. this.inputEventListener.dispose();
  762. this.inputEventListener = event(this.emitter.fire, this.emitter);
  763. }
  764. }
  765. dispose() {
  766. this.inputEventListener.dispose();
  767. this.emitter.dispose();
  768. }
  769. }