@xterm/xterm 5.6.0-beta.9 → 5.6.0-beta.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +9 -3
  2. package/css/xterm.css +71 -4
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +53 -0
  6. package/lib/xterm.mjs.map +7 -0
  7. package/package.json +43 -33
  8. package/src/browser/AccessibilityManager.ts +53 -25
  9. package/src/browser/{Terminal.ts → CoreBrowserTerminal.ts} +139 -148
  10. package/src/browser/Linkifier.ts +26 -14
  11. package/src/browser/LocalizableStrings.ts +15 -4
  12. package/src/browser/{Types.d.ts → Types.ts} +67 -15
  13. package/src/browser/Viewport.ts +143 -370
  14. package/src/browser/decorations/BufferDecorationRenderer.ts +14 -9
  15. package/src/browser/decorations/OverviewRulerRenderer.ts +40 -44
  16. package/src/browser/input/CompositionHelper.ts +2 -1
  17. package/src/browser/public/Terminal.ts +25 -19
  18. package/src/browser/renderer/dom/DomRenderer.ts +19 -14
  19. package/src/browser/renderer/dom/DomRendererRowFactory.ts +35 -15
  20. package/src/browser/renderer/shared/CharAtlasCache.ts +3 -2
  21. package/src/browser/renderer/shared/CharAtlasUtils.ts +6 -1
  22. package/src/browser/renderer/shared/CustomGlyphs.ts +6 -0
  23. package/src/browser/renderer/shared/DevicePixelObserver.ts +1 -2
  24. package/src/browser/renderer/shared/TextureAtlas.ts +45 -12
  25. package/src/browser/renderer/shared/{Types.d.ts → Types.ts} +7 -6
  26. package/src/browser/services/CharSizeService.ts +6 -6
  27. package/src/browser/services/CoreBrowserService.ts +15 -15
  28. package/src/browser/services/LinkProviderService.ts +2 -2
  29. package/src/browser/services/RenderService.ts +20 -20
  30. package/src/browser/services/SelectionService.ts +8 -8
  31. package/src/browser/services/Services.ts +13 -13
  32. package/src/browser/services/ThemeService.ts +19 -58
  33. package/src/browser/shared/Constants.ts +8 -0
  34. package/src/common/CircularList.ts +5 -5
  35. package/src/common/CoreTerminal.ts +35 -41
  36. package/src/common/InputHandler.ts +63 -51
  37. package/src/common/{Types.d.ts → Types.ts} +13 -17
  38. package/src/common/buffer/Buffer.ts +15 -7
  39. package/src/common/buffer/BufferReflow.ts +9 -6
  40. package/src/common/buffer/BufferSet.ts +5 -5
  41. package/src/common/buffer/Marker.ts +4 -4
  42. package/src/common/buffer/{Types.d.ts → Types.ts} +2 -2
  43. package/src/common/input/WriteBuffer.ts +3 -3
  44. package/src/common/parser/EscapeSequenceParser.ts +4 -4
  45. package/src/common/public/BufferNamespaceApi.ts +3 -3
  46. package/src/common/services/BufferService.ts +7 -7
  47. package/src/common/services/CoreMouseService.ts +5 -3
  48. package/src/common/services/CoreService.ts +8 -6
  49. package/src/common/services/DecorationService.ts +8 -9
  50. package/src/common/services/InstantiationService.ts +1 -1
  51. package/src/common/services/LogService.ts +2 -2
  52. package/src/common/services/OptionsService.ts +7 -6
  53. package/src/common/services/ServiceRegistry.ts +1 -1
  54. package/src/common/services/Services.ts +26 -17
  55. package/src/common/services/UnicodeService.ts +2 -2
  56. package/src/vs/base/browser/browser.ts +141 -0
  57. package/src/vs/base/browser/canIUse.ts +49 -0
  58. package/src/vs/base/browser/dom.ts +2369 -0
  59. package/src/vs/base/browser/fastDomNode.ts +316 -0
  60. package/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  61. package/src/vs/base/browser/iframe.ts +135 -0
  62. package/src/vs/base/browser/keyboardEvent.ts +213 -0
  63. package/src/vs/base/browser/mouseEvent.ts +229 -0
  64. package/src/vs/base/browser/touch.ts +372 -0
  65. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  66. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  67. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
  68. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  69. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  70. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  71. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  72. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  73. package/src/vs/base/browser/ui/widget.ts +57 -0
  74. package/src/vs/base/browser/window.ts +14 -0
  75. package/src/vs/base/common/arrays.ts +887 -0
  76. package/src/vs/base/common/arraysFind.ts +202 -0
  77. package/src/vs/base/common/assert.ts +71 -0
  78. package/src/vs/base/common/async.ts +1992 -0
  79. package/src/vs/base/common/cancellation.ts +148 -0
  80. package/src/vs/base/common/charCode.ts +450 -0
  81. package/src/vs/base/common/collections.ts +140 -0
  82. package/src/vs/base/common/decorators.ts +130 -0
  83. package/src/vs/base/common/equals.ts +146 -0
  84. package/src/vs/base/common/errors.ts +303 -0
  85. package/src/vs/base/common/event.ts +1778 -0
  86. package/src/vs/base/common/functional.ts +32 -0
  87. package/src/vs/base/common/hash.ts +316 -0
  88. package/src/vs/base/common/iterator.ts +159 -0
  89. package/src/vs/base/common/keyCodes.ts +526 -0
  90. package/src/vs/base/common/keybindings.ts +284 -0
  91. package/src/vs/base/common/lazy.ts +47 -0
  92. package/src/vs/base/common/lifecycle.ts +801 -0
  93. package/src/vs/base/common/linkedList.ts +142 -0
  94. package/src/vs/base/common/map.ts +202 -0
  95. package/src/vs/base/common/numbers.ts +98 -0
  96. package/src/vs/base/common/observable.ts +76 -0
  97. package/src/vs/base/common/observableInternal/api.ts +31 -0
  98. package/src/vs/base/common/observableInternal/autorun.ts +281 -0
  99. package/src/vs/base/common/observableInternal/base.ts +489 -0
  100. package/src/vs/base/common/observableInternal/debugName.ts +145 -0
  101. package/src/vs/base/common/observableInternal/derived.ts +428 -0
  102. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  103. package/src/vs/base/common/observableInternal/logging.ts +328 -0
  104. package/src/vs/base/common/observableInternal/promise.ts +209 -0
  105. package/src/vs/base/common/observableInternal/utils.ts +610 -0
  106. package/src/vs/base/common/platform.ts +281 -0
  107. package/src/vs/base/common/scrollable.ts +522 -0
  108. package/src/vs/base/common/sequence.ts +34 -0
  109. package/src/vs/base/common/stopwatch.ts +43 -0
  110. package/src/vs/base/common/strings.ts +557 -0
  111. package/src/vs/base/common/symbols.ts +9 -0
  112. package/src/vs/base/common/uint.ts +59 -0
  113. package/src/vs/patches/nls.ts +90 -0
  114. package/src/vs/typings/base-common.d.ts +20 -0
  115. package/src/vs/typings/require.d.ts +42 -0
  116. package/src/vs/typings/thenable.d.ts +12 -0
  117. package/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  118. package/src/vs/typings/vscode-globals-product.d.ts +33 -0
  119. package/typings/xterm.d.ts +66 -15
  120. package/src/browser/Lifecycle.ts +0 -33
  121. package/src/common/EventEmitter.ts +0 -78
  122. package/src/common/Lifecycle.ts +0 -108
  123. /package/src/browser/selection/{Types.d.ts → Types.ts} +0 -0
  124. /package/src/common/parser/{Types.d.ts → Types.ts} +0 -0
@@ -0,0 +1,801 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ import { compareBy, numberComparator } from 'vs/base/common/arrays';
7
+ import { groupBy } from 'vs/base/common/collections';
8
+ import { SetMap } from './map';
9
+ import { createSingleCallFunction } from 'vs/base/common/functional';
10
+ import { Iterable } from 'vs/base/common/iterator';
11
+
12
+ // #region Disposable Tracking
13
+
14
+ /**
15
+ * Enables logging of potentially leaked disposables.
16
+ *
17
+ * A disposable is considered leaked if it is not disposed or not registered as the child of
18
+ * another disposable. This tracking is very simple an only works for classes that either
19
+ * extend Disposable or use a DisposableStore. This means there are a lot of false positives.
20
+ */
21
+ const TRACK_DISPOSABLES = false;
22
+ let disposableTracker: IDisposableTracker | null = null;
23
+
24
+ export interface IDisposableTracker {
25
+ /**
26
+ * Is called on construction of a disposable.
27
+ */
28
+ trackDisposable(disposable: IDisposable): void;
29
+
30
+ /**
31
+ * Is called when a disposable is registered as child of another disposable (e.g. {@link DisposableStore}).
32
+ * If parent is `null`, the disposable is removed from its former parent.
33
+ */
34
+ setParent(child: IDisposable, parent: IDisposable | null): void;
35
+
36
+ /**
37
+ * Is called after a disposable is disposed.
38
+ */
39
+ markAsDisposed(disposable: IDisposable): void;
40
+
41
+ /**
42
+ * Indicates that the given object is a singleton which does not need to be disposed.
43
+ */
44
+ markAsSingleton(disposable: IDisposable): void;
45
+ }
46
+
47
+ export interface DisposableInfo {
48
+ value: IDisposable;
49
+ source: string | null;
50
+ parent: IDisposable | null;
51
+ isSingleton: boolean;
52
+ idx: number;
53
+ }
54
+
55
+ export class DisposableTracker implements IDisposableTracker {
56
+ private static idx = 0;
57
+
58
+ private readonly livingDisposables = new Map<IDisposable, DisposableInfo>();
59
+
60
+ private getDisposableData(d: IDisposable): DisposableInfo {
61
+ let val = this.livingDisposables.get(d);
62
+ if (!val) {
63
+ val = { parent: null, source: null, isSingleton: false, value: d, idx: DisposableTracker.idx++ };
64
+ this.livingDisposables.set(d, val);
65
+ }
66
+ return val;
67
+ }
68
+
69
+ trackDisposable(d: IDisposable): void {
70
+ const data = this.getDisposableData(d);
71
+ if (!data.source) {
72
+ data.source =
73
+ new Error().stack!;
74
+ }
75
+ }
76
+
77
+ setParent(child: IDisposable, parent: IDisposable | null): void {
78
+ const data = this.getDisposableData(child);
79
+ data.parent = parent;
80
+ }
81
+
82
+ markAsDisposed(x: IDisposable): void {
83
+ this.livingDisposables.delete(x);
84
+ }
85
+
86
+ markAsSingleton(disposable: IDisposable): void {
87
+ this.getDisposableData(disposable).isSingleton = true;
88
+ }
89
+
90
+ private getRootParent(data: DisposableInfo, cache: Map<DisposableInfo, DisposableInfo>): DisposableInfo {
91
+ const cacheValue = cache.get(data);
92
+ if (cacheValue) {
93
+ return cacheValue;
94
+ }
95
+
96
+ const result = data.parent ? this.getRootParent(this.getDisposableData(data.parent), cache) : data;
97
+ cache.set(data, result);
98
+ return result;
99
+ }
100
+
101
+ getTrackedDisposables(): IDisposable[] {
102
+ const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
103
+
104
+ const leaking = [...this.livingDisposables.entries()]
105
+ .filter(([, v]) => v.source !== null && !this.getRootParent(v, rootParentCache).isSingleton)
106
+ .flatMap(([k]) => k);
107
+
108
+ return leaking;
109
+ }
110
+
111
+ computeLeakingDisposables(maxReported = 10, preComputedLeaks?: DisposableInfo[]): { leaks: DisposableInfo[]; details: string } | undefined {
112
+ let uncoveredLeakingObjs: DisposableInfo[] | undefined;
113
+ if (preComputedLeaks) {
114
+ uncoveredLeakingObjs = preComputedLeaks;
115
+ } else {
116
+ const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
117
+
118
+ const leakingObjects = [...this.livingDisposables.values()]
119
+ .filter((info) => info.source !== null && !this.getRootParent(info, rootParentCache).isSingleton);
120
+
121
+ if (leakingObjects.length === 0) {
122
+ return;
123
+ }
124
+ const leakingObjsSet = new Set(leakingObjects.map(o => o.value));
125
+
126
+ // Remove all objects that are a child of other leaking objects. Assumes there are no cycles.
127
+ uncoveredLeakingObjs = leakingObjects.filter(l => {
128
+ return !(l.parent && leakingObjsSet.has(l.parent));
129
+ });
130
+
131
+ if (uncoveredLeakingObjs.length === 0) {
132
+ throw new Error('There are cyclic diposable chains!');
133
+ }
134
+ }
135
+
136
+ if (!uncoveredLeakingObjs) {
137
+ return undefined;
138
+ }
139
+
140
+ function getStackTracePath(leaking: DisposableInfo): string[] {
141
+ function removePrefix(array: string[], linesToRemove: (string | RegExp)[]) {
142
+ while (array.length > 0 && linesToRemove.some(regexp => typeof regexp === 'string' ? regexp === array[0] : array[0].match(regexp))) {
143
+ array.shift();
144
+ }
145
+ }
146
+
147
+ const lines = leaking.source!.split('\n').map(p => p.trim().replace('at ', '')).filter(l => l !== '');
148
+ removePrefix(lines, ['Error', /^trackDisposable \(.*\)$/, /^DisposableTracker.trackDisposable \(.*\)$/]);
149
+ return lines.reverse();
150
+ }
151
+
152
+ const stackTraceStarts = new SetMap<string, DisposableInfo>();
153
+ for (const leaking of uncoveredLeakingObjs) {
154
+ const stackTracePath = getStackTracePath(leaking);
155
+ for (let i = 0; i <= stackTracePath.length; i++) {
156
+ stackTraceStarts.add(stackTracePath.slice(0, i).join('\n'), leaking);
157
+ }
158
+ }
159
+
160
+ // Put earlier leaks first
161
+ uncoveredLeakingObjs.sort(compareBy(l => l.idx, numberComparator));
162
+
163
+ let message = '';
164
+
165
+ let i = 0;
166
+ for (const leaking of uncoveredLeakingObjs.slice(0, maxReported)) {
167
+ i++;
168
+ const stackTracePath = getStackTracePath(leaking);
169
+ const stackTraceFormattedLines = [];
170
+
171
+ for (let i = 0; i < stackTracePath.length; i++) {
172
+ let line = stackTracePath[i];
173
+ const starts = stackTraceStarts.get(stackTracePath.slice(0, i + 1).join('\n'));
174
+ line = `(shared with ${starts.size}/${uncoveredLeakingObjs.length} leaks) at ${line}`;
175
+
176
+ const prevStarts = stackTraceStarts.get(stackTracePath.slice(0, i).join('\n'));
177
+ const continuations = groupBy([...prevStarts].map(d => getStackTracePath(d)[i]), v => v);
178
+ delete continuations[stackTracePath[i]];
179
+ for (const [cont, set] of Object.entries(continuations)) {
180
+ stackTraceFormattedLines.unshift(` - stacktraces of ${set.length} other leaks continue with ${cont}`);
181
+ }
182
+
183
+ stackTraceFormattedLines.unshift(line);
184
+ }
185
+
186
+ message += `\n\n\n==================== Leaking disposable ${i}/${uncoveredLeakingObjs.length}: ${leaking.value.constructor.name} ====================\n${stackTraceFormattedLines.join('\n')}\n============================================================\n\n`;
187
+ }
188
+
189
+ if (uncoveredLeakingObjs.length > maxReported) {
190
+ message += `\n\n\n... and ${uncoveredLeakingObjs.length - maxReported} more leaking disposables\n\n`;
191
+ }
192
+
193
+ return { leaks: uncoveredLeakingObjs, details: message };
194
+ }
195
+ }
196
+
197
+ export function setDisposableTracker(tracker: IDisposableTracker | null): void {
198
+ disposableTracker = tracker;
199
+ }
200
+
201
+ if (TRACK_DISPOSABLES) {
202
+ const __is_disposable_tracked__ = '__is_disposable_tracked__';
203
+ setDisposableTracker(new class implements IDisposableTracker {
204
+ trackDisposable(x: IDisposable): void {
205
+ const stack = new Error('Potentially leaked disposable').stack!;
206
+ setTimeout(() => {
207
+ if (!(x as any)[__is_disposable_tracked__]) {
208
+ console.log(stack);
209
+ }
210
+ }, 3000);
211
+ }
212
+
213
+ setParent(child: IDisposable, parent: IDisposable | null): void {
214
+ if (child && child !== Disposable.None) {
215
+ try {
216
+ (child as any)[__is_disposable_tracked__] = true;
217
+ } catch {
218
+ // noop
219
+ }
220
+ }
221
+ }
222
+
223
+ markAsDisposed(disposable: IDisposable): void {
224
+ if (disposable && disposable !== Disposable.None) {
225
+ try {
226
+ (disposable as any)[__is_disposable_tracked__] = true;
227
+ } catch {
228
+ // noop
229
+ }
230
+ }
231
+ }
232
+ markAsSingleton(disposable: IDisposable): void { }
233
+ });
234
+ }
235
+
236
+ export function trackDisposable<T extends IDisposable>(x: T): T {
237
+ disposableTracker?.trackDisposable(x);
238
+ return x;
239
+ }
240
+
241
+ export function markAsDisposed(disposable: IDisposable): void {
242
+ disposableTracker?.markAsDisposed(disposable);
243
+ }
244
+
245
+ function setParentOfDisposable(child: IDisposable, parent: IDisposable | null): void {
246
+ disposableTracker?.setParent(child, parent);
247
+ }
248
+
249
+ function setParentOfDisposables(children: IDisposable[], parent: IDisposable | null): void {
250
+ if (!disposableTracker) {
251
+ return;
252
+ }
253
+ for (const child of children) {
254
+ disposableTracker.setParent(child, parent);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Indicates that the given object is a singleton which does not need to be disposed.
260
+ */
261
+ export function markAsSingleton<T extends IDisposable>(singleton: T): T {
262
+ disposableTracker?.markAsSingleton(singleton);
263
+ return singleton;
264
+ }
265
+
266
+ // #endregion
267
+
268
+ /**
269
+ * An object that performs a cleanup operation when `.dispose()` is called.
270
+ *
271
+ * Some examples of how disposables are used:
272
+ *
273
+ * - An event listener that removes itself when `.dispose()` is called.
274
+ * - A resource such as a file system watcher that cleans up the resource when `.dispose()` is called.
275
+ * - The return value from registering a provider. When `.dispose()` is called, the provider is unregistered.
276
+ */
277
+ export interface IDisposable {
278
+ dispose(): void;
279
+ }
280
+
281
+ /**
282
+ * Check if `thing` is {@link IDisposable disposable}.
283
+ */
284
+ export function isDisposable<E extends any>(thing: E): thing is E & IDisposable {
285
+ return typeof thing === 'object' && thing !== null && typeof (<IDisposable><any>thing).dispose === 'function' && (<IDisposable><any>thing).dispose.length === 0;
286
+ }
287
+
288
+ /**
289
+ * Disposes of the value(s) passed in.
290
+ */
291
+ export function dispose<T extends IDisposable>(disposable: T): T;
292
+ export function dispose<T extends IDisposable>(disposable: T | undefined): T | undefined;
293
+ export function dispose<T extends IDisposable, A extends Iterable<T> = Iterable<T>>(disposables: A): A;
294
+ export function dispose<T extends IDisposable>(disposables: Array<T>): Array<T>;
295
+ export function dispose<T extends IDisposable>(disposables: ReadonlyArray<T>): ReadonlyArray<T>;
296
+ export function dispose<T extends IDisposable>(arg: T | Iterable<T> | undefined): any {
297
+ if (Iterable.is(arg)) {
298
+ const errors: any[] = [];
299
+
300
+ for (const d of arg) {
301
+ if (d) {
302
+ try {
303
+ d.dispose();
304
+ } catch (e) {
305
+ errors.push(e);
306
+ }
307
+ }
308
+ }
309
+
310
+ if (errors.length === 1) {
311
+ throw errors[0];
312
+ } else if (errors.length > 1) {
313
+ throw new AggregateError(errors, 'Encountered errors while disposing of store');
314
+ }
315
+
316
+ return Array.isArray(arg) ? [] : arg;
317
+ } else if (arg) {
318
+ arg.dispose();
319
+ return arg;
320
+ }
321
+ }
322
+
323
+ export function disposeIfDisposable<T extends IDisposable | object>(disposables: Array<T>): Array<T> {
324
+ for (const d of disposables) {
325
+ if (isDisposable(d)) {
326
+ d.dispose();
327
+ }
328
+ }
329
+ return [];
330
+ }
331
+
332
+ /**
333
+ * Combine multiple disposable values into a single {@link IDisposable}.
334
+ */
335
+ export function combinedDisposable(...disposables: IDisposable[]): IDisposable {
336
+ const parent = toDisposable(() => dispose(disposables));
337
+ setParentOfDisposables(disposables, parent);
338
+ return parent;
339
+ }
340
+
341
+ /**
342
+ * Turn a function that implements dispose into an {@link IDisposable}.
343
+ *
344
+ * @param fn Clean up function, guaranteed to be called only **once**.
345
+ */
346
+ export function toDisposable(fn: () => void): IDisposable {
347
+ const self = trackDisposable({
348
+ dispose: createSingleCallFunction(() => {
349
+ markAsDisposed(self);
350
+ fn();
351
+ })
352
+ });
353
+ return self;
354
+ }
355
+
356
+ /**
357
+ * Manages a collection of disposable values.
358
+ *
359
+ * This is the preferred way to manage multiple disposables. A `DisposableStore` is safer to work with than an
360
+ * `IDisposable[]` as it considers edge cases, such as registering the same value multiple times or adding an item to a
361
+ * store that has already been disposed of.
362
+ */
363
+ export class DisposableStore implements IDisposable {
364
+
365
+ static DISABLE_DISPOSED_WARNING = false;
366
+
367
+ private readonly _toDispose = new Set<IDisposable>();
368
+ private _isDisposed = false;
369
+
370
+ constructor() {
371
+ trackDisposable(this);
372
+ }
373
+
374
+ /**
375
+ * Dispose of all registered disposables and mark this object as disposed.
376
+ *
377
+ * Any future disposables added to this object will be disposed of on `add`.
378
+ */
379
+ public dispose(): void {
380
+ if (this._isDisposed) {
381
+ return;
382
+ }
383
+
384
+ markAsDisposed(this);
385
+ this._isDisposed = true;
386
+ this.clear();
387
+ }
388
+
389
+ /**
390
+ * @return `true` if this object has been disposed of.
391
+ */
392
+ public get isDisposed(): boolean {
393
+ return this._isDisposed;
394
+ }
395
+
396
+ /**
397
+ * Dispose of all registered disposables but do not mark this object as disposed.
398
+ */
399
+ public clear(): void {
400
+ if (this._toDispose.size === 0) {
401
+ return;
402
+ }
403
+
404
+ try {
405
+ dispose(this._toDispose);
406
+ } finally {
407
+ this._toDispose.clear();
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Add a new {@link IDisposable disposable} to the collection.
413
+ */
414
+ public add<T extends IDisposable>(o: T): T {
415
+ if (!o) {
416
+ return o;
417
+ }
418
+ if ((o as unknown as DisposableStore) === this) {
419
+ throw new Error('Cannot register a disposable on itself!');
420
+ }
421
+
422
+ setParentOfDisposable(o, this);
423
+ if (this._isDisposed) {
424
+ if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
425
+ console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
426
+ }
427
+ } else {
428
+ this._toDispose.add(o);
429
+ }
430
+
431
+ return o;
432
+ }
433
+
434
+ /**
435
+ * Deletes a disposable from store and disposes of it. This will not throw or warn and proceed to dispose the
436
+ * disposable even when the disposable is not part in the store.
437
+ */
438
+ public delete<T extends IDisposable>(o: T): void {
439
+ if (!o) {
440
+ return;
441
+ }
442
+ if ((o as unknown as DisposableStore) === this) {
443
+ throw new Error('Cannot dispose a disposable on itself!');
444
+ }
445
+ this._toDispose.delete(o);
446
+ o.dispose();
447
+ }
448
+
449
+ /**
450
+ * Deletes the value from the store, but does not dispose it.
451
+ */
452
+ public deleteAndLeak<T extends IDisposable>(o: T): void {
453
+ if (!o) {
454
+ return;
455
+ }
456
+ if (this._toDispose.has(o)) {
457
+ this._toDispose.delete(o);
458
+ setParentOfDisposable(o, null);
459
+ }
460
+ }
461
+ }
462
+
463
+ /**
464
+ * Abstract base class for a {@link IDisposable disposable} object.
465
+ *
466
+ * Subclasses can {@linkcode _register} disposables that will be automatically cleaned up when this object is disposed of.
467
+ */
468
+ export abstract class Disposable implements IDisposable {
469
+
470
+ /**
471
+ * A disposable that does nothing when it is disposed of.
472
+ *
473
+ * TODO: This should not be a static property.
474
+ */
475
+ static readonly None = Object.freeze<IDisposable>({ dispose() { } });
476
+
477
+ protected readonly _store = new DisposableStore();
478
+
479
+ constructor() {
480
+ trackDisposable(this);
481
+ setParentOfDisposable(this._store, this);
482
+ }
483
+
484
+ public dispose(): void {
485
+ markAsDisposed(this);
486
+
487
+ this._store.dispose();
488
+ }
489
+
490
+ /**
491
+ * Adds `o` to the collection of disposables managed by this object.
492
+ */
493
+ protected _register<T extends IDisposable>(o: T): T {
494
+ if ((o as unknown as Disposable) === this) {
495
+ throw new Error('Cannot register a disposable on itself!');
496
+ }
497
+ return this._store.add(o);
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Manages the lifecycle of a disposable value that may be changed.
503
+ *
504
+ * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
505
+ * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
506
+ */
507
+ export class MutableDisposable<T extends IDisposable> implements IDisposable {
508
+ private _value?: T;
509
+ private _isDisposed = false;
510
+
511
+ constructor() {
512
+ trackDisposable(this);
513
+ }
514
+
515
+ get value(): T | undefined {
516
+ return this._isDisposed ? undefined : this._value;
517
+ }
518
+
519
+ set value(value: T | undefined) {
520
+ if (this._isDisposed || value === this._value) {
521
+ return;
522
+ }
523
+
524
+ this._value?.dispose();
525
+ if (value) {
526
+ setParentOfDisposable(value, this);
527
+ }
528
+ this._value = value;
529
+ }
530
+
531
+ /**
532
+ * Resets the stored value and disposed of the previously stored value.
533
+ */
534
+ clear(): void {
535
+ this.value = undefined;
536
+ }
537
+
538
+ dispose(): void {
539
+ this._isDisposed = true;
540
+ markAsDisposed(this);
541
+ this._value?.dispose();
542
+ this._value = undefined;
543
+ }
544
+
545
+ /**
546
+ * Clears the value, but does not dispose it.
547
+ * The old value is returned.
548
+ */
549
+ clearAndLeak(): T | undefined {
550
+ const oldValue = this._value;
551
+ this._value = undefined;
552
+ if (oldValue) {
553
+ setParentOfDisposable(oldValue, null);
554
+ }
555
+ return oldValue;
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Manages the lifecycle of a disposable value that may be changed like {@link MutableDisposable}, but the value must
561
+ * exist and cannot be undefined.
562
+ */
563
+ export class MandatoryMutableDisposable<T extends IDisposable> implements IDisposable {
564
+ private readonly _disposable = new MutableDisposable<T>();
565
+ private _isDisposed = false;
566
+
567
+ constructor(initialValue: T) {
568
+ this._disposable.value = initialValue;
569
+ }
570
+
571
+ get value(): T {
572
+ return this._disposable.value!;
573
+ }
574
+
575
+ set value(value: T) {
576
+ if (this._isDisposed || value === this._disposable.value) {
577
+ return;
578
+ }
579
+ this._disposable.value = value;
580
+ }
581
+
582
+ dispose() {
583
+ this._isDisposed = true;
584
+ this._disposable.dispose();
585
+ }
586
+ }
587
+
588
+ export class RefCountedDisposable {
589
+
590
+ private _counter: number = 1;
591
+
592
+ constructor(
593
+ private readonly _disposable: IDisposable,
594
+ ) { }
595
+
596
+ acquire() {
597
+ this._counter++;
598
+ return this;
599
+ }
600
+
601
+ release() {
602
+ if (--this._counter === 0) {
603
+ this._disposable.dispose();
604
+ }
605
+ return this;
606
+ }
607
+ }
608
+
609
+ /**
610
+ * A safe disposable can be `unset` so that a leaked reference (listener)
611
+ * can be cut-off.
612
+ */
613
+ export class SafeDisposable implements IDisposable {
614
+
615
+ dispose: () => void = () => { };
616
+ unset: () => void = () => { };
617
+ isset: () => boolean = () => false;
618
+
619
+ constructor() {
620
+ trackDisposable(this);
621
+ }
622
+
623
+ set(fn: Function) {
624
+ let callback: Function | undefined = fn;
625
+ this.unset = () => callback = undefined;
626
+ this.isset = () => callback !== undefined;
627
+ this.dispose = () => {
628
+ if (callback) {
629
+ callback();
630
+ callback = undefined;
631
+ markAsDisposed(this);
632
+ }
633
+ };
634
+ return this;
635
+ }
636
+ }
637
+
638
+ export interface IReference<T> extends IDisposable {
639
+ readonly object: T;
640
+ }
641
+
642
+ export abstract class ReferenceCollection<T> {
643
+
644
+ private readonly references: Map<string, { readonly object: T; counter: number }> = new Map();
645
+
646
+ acquire(key: string, ...args: any[]): IReference<T> {
647
+ let reference = this.references.get(key);
648
+
649
+ if (!reference) {
650
+ reference = { counter: 0, object: this.createReferencedObject(key, ...args) };
651
+ this.references.set(key, reference);
652
+ }
653
+
654
+ const { object } = reference;
655
+ const dispose = createSingleCallFunction(() => {
656
+ if (--reference.counter === 0) {
657
+ this.destroyReferencedObject(key, reference.object);
658
+ this.references.delete(key);
659
+ }
660
+ });
661
+
662
+ reference.counter++;
663
+
664
+ return { object, dispose };
665
+ }
666
+
667
+ protected abstract createReferencedObject(key: string, ...args: any[]): T;
668
+ protected abstract destroyReferencedObject(key: string, object: T): void;
669
+ }
670
+
671
+ /**
672
+ * Unwraps a reference collection of promised values. Makes sure
673
+ * references are disposed whenever promises get rejected.
674
+ */
675
+ export class AsyncReferenceCollection<T> {
676
+
677
+ constructor(private referenceCollection: ReferenceCollection<Promise<T>>) { }
678
+
679
+ async acquire(key: string, ...args: any[]): Promise<IReference<T>> {
680
+ const ref = this.referenceCollection.acquire(key, ...args);
681
+
682
+ try {
683
+ const object = await ref.object;
684
+
685
+ return {
686
+ object,
687
+ dispose: () => ref.dispose()
688
+ };
689
+ } catch (error) {
690
+ ref.dispose();
691
+ throw error;
692
+ }
693
+ }
694
+ }
695
+
696
+ export class ImmortalReference<T> implements IReference<T> {
697
+ constructor(public object: T) { }
698
+ dispose(): void { /* noop */ }
699
+ }
700
+
701
+ export function disposeOnReturn(fn: (store: DisposableStore) => void): void {
702
+ const store = new DisposableStore();
703
+ try {
704
+ fn(store);
705
+ } finally {
706
+ store.dispose();
707
+ }
708
+ }
709
+
710
+ /**
711
+ * A map the manages the lifecycle of the values that it stores.
712
+ */
713
+ export class DisposableMap<K, V extends IDisposable = IDisposable> implements IDisposable {
714
+
715
+ private readonly _store = new Map<K, V>();
716
+ private _isDisposed = false;
717
+
718
+ constructor() {
719
+ trackDisposable(this);
720
+ }
721
+
722
+ /**
723
+ * Disposes of all stored values and mark this object as disposed.
724
+ *
725
+ * Trying to use this object after it has been disposed of is an error.
726
+ */
727
+ dispose(): void {
728
+ markAsDisposed(this);
729
+ this._isDisposed = true;
730
+ this.clearAndDisposeAll();
731
+ }
732
+
733
+ /**
734
+ * Disposes of all stored values and clear the map, but DO NOT mark this object as disposed.
735
+ */
736
+ clearAndDisposeAll(): void {
737
+ if (!this._store.size) {
738
+ return;
739
+ }
740
+
741
+ try {
742
+ dispose(this._store.values());
743
+ } finally {
744
+ this._store.clear();
745
+ }
746
+ }
747
+
748
+ has(key: K): boolean {
749
+ return this._store.has(key);
750
+ }
751
+
752
+ get size(): number {
753
+ return this._store.size;
754
+ }
755
+
756
+ get(key: K): V | undefined {
757
+ return this._store.get(key);
758
+ }
759
+
760
+ set(key: K, value: V, skipDisposeOnOverwrite = false): void {
761
+ if (this._isDisposed) {
762
+ console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack);
763
+ }
764
+
765
+ if (!skipDisposeOnOverwrite) {
766
+ this._store.get(key)?.dispose();
767
+ }
768
+
769
+ this._store.set(key, value);
770
+ }
771
+
772
+ /**
773
+ * Delete the value stored for `key` from this map and also dispose of it.
774
+ */
775
+ deleteAndDispose(key: K): void {
776
+ this._store.get(key)?.dispose();
777
+ this._store.delete(key);
778
+ }
779
+
780
+ /**
781
+ * Delete the value stored for `key` from this map but return it. The caller is
782
+ * responsible for disposing of the value.
783
+ */
784
+ deleteAndLeak(key: K): V | undefined {
785
+ const value = this._store.get(key);
786
+ this._store.delete(key);
787
+ return value;
788
+ }
789
+
790
+ keys(): IterableIterator<K> {
791
+ return this._store.keys();
792
+ }
793
+
794
+ values(): IterableIterator<V> {
795
+ return this._store.values();
796
+ }
797
+
798
+ [Symbol.iterator](): IterableIterator<[K, V]> {
799
+ return this._store[Symbol.iterator]();
800
+ }
801
+ }