@xterm/xterm 5.6.0-beta.7 → 5.6.0-beta.70

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 (118) hide show
  1. package/README.md +6 -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} +135 -146
  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/public/Terminal.ts +25 -19
  17. package/src/browser/renderer/dom/DomRenderer.ts +14 -16
  18. package/src/browser/renderer/shared/CharAtlasUtils.ts +4 -0
  19. package/src/browser/renderer/shared/CustomGlyphs.ts +6 -0
  20. package/src/browser/renderer/shared/DevicePixelObserver.ts +1 -2
  21. package/src/browser/renderer/shared/TextureAtlas.ts +3 -3
  22. package/src/browser/renderer/shared/{Types.d.ts → Types.ts} +4 -4
  23. package/src/browser/services/CharSizeService.ts +6 -6
  24. package/src/browser/services/CoreBrowserService.ts +15 -15
  25. package/src/browser/services/LinkProviderService.ts +2 -2
  26. package/src/browser/services/RenderService.ts +20 -20
  27. package/src/browser/services/SelectionService.ts +8 -8
  28. package/src/browser/services/Services.ts +13 -13
  29. package/src/browser/services/ThemeService.ts +17 -56
  30. package/src/browser/shared/Constants.ts +8 -0
  31. package/src/common/CircularList.ts +5 -5
  32. package/src/common/CoreTerminal.ts +35 -41
  33. package/src/common/InputHandler.ts +34 -28
  34. package/src/common/{Types.d.ts → Types.ts} +11 -17
  35. package/src/common/buffer/Buffer.ts +5 -1
  36. package/src/common/buffer/BufferSet.ts +5 -5
  37. package/src/common/buffer/Marker.ts +4 -4
  38. package/src/common/buffer/{Types.d.ts → Types.ts} +2 -2
  39. package/src/common/input/WriteBuffer.ts +3 -3
  40. package/src/common/parser/EscapeSequenceParser.ts +4 -4
  41. package/src/common/public/BufferNamespaceApi.ts +3 -3
  42. package/src/common/services/BufferService.ts +7 -7
  43. package/src/common/services/CoreMouseService.ts +5 -3
  44. package/src/common/services/CoreService.ts +6 -6
  45. package/src/common/services/DecorationService.ts +8 -9
  46. package/src/common/services/LogService.ts +2 -2
  47. package/src/common/services/OptionsService.ts +5 -5
  48. package/src/common/services/Services.ts +24 -17
  49. package/src/common/services/UnicodeService.ts +2 -2
  50. package/src/vs/base/browser/browser.ts +141 -0
  51. package/src/vs/base/browser/canIUse.ts +49 -0
  52. package/src/vs/base/browser/dom.ts +2369 -0
  53. package/src/vs/base/browser/fastDomNode.ts +316 -0
  54. package/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  55. package/src/vs/base/browser/iframe.ts +135 -0
  56. package/src/vs/base/browser/keyboardEvent.ts +213 -0
  57. package/src/vs/base/browser/mouseEvent.ts +229 -0
  58. package/src/vs/base/browser/touch.ts +372 -0
  59. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  60. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  61. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
  62. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  63. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  64. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  65. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  66. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  67. package/src/vs/base/browser/ui/widget.ts +57 -0
  68. package/src/vs/base/browser/window.ts +14 -0
  69. package/src/vs/base/common/arrays.ts +887 -0
  70. package/src/vs/base/common/arraysFind.ts +202 -0
  71. package/src/vs/base/common/assert.ts +71 -0
  72. package/src/vs/base/common/async.ts +1992 -0
  73. package/src/vs/base/common/cancellation.ts +148 -0
  74. package/src/vs/base/common/charCode.ts +450 -0
  75. package/src/vs/base/common/collections.ts +140 -0
  76. package/src/vs/base/common/decorators.ts +130 -0
  77. package/src/vs/base/common/equals.ts +146 -0
  78. package/src/vs/base/common/errors.ts +303 -0
  79. package/src/vs/base/common/event.ts +1778 -0
  80. package/src/vs/base/common/functional.ts +32 -0
  81. package/src/vs/base/common/hash.ts +316 -0
  82. package/src/vs/base/common/iterator.ts +159 -0
  83. package/src/vs/base/common/keyCodes.ts +526 -0
  84. package/src/vs/base/common/keybindings.ts +284 -0
  85. package/src/vs/base/common/lazy.ts +47 -0
  86. package/src/vs/base/common/lifecycle.ts +801 -0
  87. package/src/vs/base/common/linkedList.ts +142 -0
  88. package/src/vs/base/common/map.ts +202 -0
  89. package/src/vs/base/common/numbers.ts +98 -0
  90. package/src/vs/base/common/observable.ts +76 -0
  91. package/src/vs/base/common/observableInternal/api.ts +31 -0
  92. package/src/vs/base/common/observableInternal/autorun.ts +281 -0
  93. package/src/vs/base/common/observableInternal/base.ts +489 -0
  94. package/src/vs/base/common/observableInternal/debugName.ts +145 -0
  95. package/src/vs/base/common/observableInternal/derived.ts +428 -0
  96. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  97. package/src/vs/base/common/observableInternal/logging.ts +328 -0
  98. package/src/vs/base/common/observableInternal/promise.ts +209 -0
  99. package/src/vs/base/common/observableInternal/utils.ts +610 -0
  100. package/src/vs/base/common/platform.ts +281 -0
  101. package/src/vs/base/common/scrollable.ts +522 -0
  102. package/src/vs/base/common/sequence.ts +34 -0
  103. package/src/vs/base/common/stopwatch.ts +43 -0
  104. package/src/vs/base/common/strings.ts +557 -0
  105. package/src/vs/base/common/symbols.ts +9 -0
  106. package/src/vs/base/common/uint.ts +59 -0
  107. package/src/vs/patches/nls.ts +90 -0
  108. package/src/vs/typings/base-common.d.ts +20 -0
  109. package/src/vs/typings/require.d.ts +42 -0
  110. package/src/vs/typings/thenable.d.ts +12 -0
  111. package/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  112. package/src/vs/typings/vscode-globals-product.d.ts +33 -0
  113. package/typings/xterm.d.ts +59 -15
  114. package/src/browser/Lifecycle.ts +0 -33
  115. package/src/common/EventEmitter.ts +0 -78
  116. package/src/common/Lifecycle.ts +0 -108
  117. /package/src/browser/selection/{Types.d.ts → Types.ts} +0 -0
  118. /package/src/common/parser/{Types.d.ts → Types.ts} +0 -0
@@ -0,0 +1,610 @@
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 { Event } from 'vs/base/common/event';
7
+ import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
8
+ import { autorun, autorunOpts } from 'vs/base/common/observableInternal/autorun';
9
+ import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from 'vs/base/common/observableInternal/base';
10
+ import { DebugNameData, IDebugNameData, DebugOwner, getDebugName, } from 'vs/base/common/observableInternal/debugName';
11
+ import { derived, derivedOpts } from 'vs/base/common/observableInternal/derived';
12
+ import { getLogger } from 'vs/base/common/observableInternal/logging';
13
+ import { IValueWithChangeEvent } from '../event';
14
+ import { BugIndicatingError } from 'vs/base/common/errors';
15
+ import { EqualityComparer, strictEquals } from 'vs/base/common/equals';
16
+
17
+ /**
18
+ * Represents an efficient observable whose value never changes.
19
+ */
20
+ export function constObservable<T>(value: T): IObservable<T> {
21
+ return new ConstObservable(value);
22
+ }
23
+
24
+ class ConstObservable<T> extends ConvenientObservable<T, void> {
25
+ constructor(private readonly value: T) {
26
+ super();
27
+ }
28
+
29
+ public override get debugName(): string {
30
+ return this.toString();
31
+ }
32
+
33
+ public get(): T {
34
+ return this.value;
35
+ }
36
+ public addObserver(observer: IObserver): void {
37
+ // NO OP
38
+ }
39
+ public removeObserver(observer: IObserver): void {
40
+ // NO OP
41
+ }
42
+
43
+ override toString(): string {
44
+ return `Const: ${this.value}`;
45
+ }
46
+ }
47
+
48
+
49
+ export function observableFromPromise<T>(promise: Promise<T>): IObservable<{ value?: T }> {
50
+ const observable = observableValue<{ value?: T }>('promiseValue', {});
51
+ promise.then((value) => {
52
+ observable.set({ value }, undefined);
53
+ });
54
+ return observable;
55
+ }
56
+
57
+
58
+ export function observableFromEvent<T, TArgs = unknown>(
59
+ owner: DebugOwner,
60
+ event: Event<TArgs>,
61
+ getValue: (args: TArgs | undefined) => T,
62
+ ): IObservable<T>;
63
+ export function observableFromEvent<T, TArgs = unknown>(
64
+ event: Event<TArgs>,
65
+ getValue: (args: TArgs | undefined) => T,
66
+ ): IObservable<T>;
67
+ export function observableFromEvent(...args:
68
+ [owner: DebugOwner, event: Event<any>, getValue: (args: any | undefined) => any]
69
+ | [event: Event<any>, getValue: (args: any | undefined) => any]
70
+ ): IObservable<any> {
71
+ let owner;
72
+ let event;
73
+ let getValue;
74
+ if (args.length === 3) {
75
+ [owner, event, getValue] = args;
76
+ } else {
77
+ [event, getValue] = args;
78
+ }
79
+ return new FromEventObservable(
80
+ new DebugNameData(owner, undefined, getValue),
81
+ event,
82
+ getValue,
83
+ () => FromEventObservable.globalTransaction,
84
+ strictEquals
85
+ );
86
+ }
87
+
88
+ export function observableFromEventOpts<T, TArgs = unknown>(
89
+ options: IDebugNameData & {
90
+ equalsFn?: EqualityComparer<T>;
91
+ },
92
+ event: Event<TArgs>,
93
+ getValue: (args: TArgs | undefined) => T,
94
+ ): IObservable<T> {
95
+ return new FromEventObservable(
96
+ new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? getValue),
97
+ event,
98
+ getValue, () => FromEventObservable.globalTransaction, options.equalsFn ?? strictEquals
99
+ );
100
+ }
101
+
102
+ export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
103
+ public static globalTransaction: ITransaction | undefined;
104
+
105
+ private value: T | undefined;
106
+ private hasValue = false;
107
+ private subscription: IDisposable | undefined;
108
+
109
+ constructor(
110
+ private readonly _debugNameData: DebugNameData,
111
+ private readonly event: Event<TArgs>,
112
+ public readonly _getValue: (args: TArgs | undefined) => T,
113
+ private readonly _getTransaction: () => ITransaction | undefined,
114
+ private readonly _equalityComparator: EqualityComparer<T>
115
+ ) {
116
+ super();
117
+ }
118
+
119
+ private getDebugName(): string | undefined {
120
+ return this._debugNameData.getDebugName(this);
121
+ }
122
+
123
+ public get debugName(): string {
124
+ const name = this.getDebugName();
125
+ return 'From Event' + (name ? `: ${name}` : '');
126
+ }
127
+
128
+ protected override onFirstObserverAdded(): void {
129
+ this.subscription = this.event(this.handleEvent);
130
+ }
131
+
132
+ private readonly handleEvent = (args: TArgs | undefined) => {
133
+ const newValue = this._getValue(args);
134
+ const oldValue = this.value;
135
+
136
+ const didChange = !this.hasValue || !(this._equalityComparator(oldValue!, newValue));
137
+ let didRunTransaction = false;
138
+
139
+ if (didChange) {
140
+ this.value = newValue;
141
+
142
+ if (this.hasValue) {
143
+ didRunTransaction = true;
144
+ subtransaction(
145
+ this._getTransaction(),
146
+ (tx) => {
147
+ getLogger()?.handleFromEventObservableTriggered(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue });
148
+
149
+ for (const o of this.observers) {
150
+ tx.updateObserver(o, this);
151
+ o.handleChange(this, undefined);
152
+ }
153
+ },
154
+ () => {
155
+ const name = this.getDebugName();
156
+ return 'Event fired' + (name ? `: ${name}` : '');
157
+ }
158
+ );
159
+ }
160
+ this.hasValue = true;
161
+ }
162
+
163
+ if (!didRunTransaction) {
164
+ getLogger()?.handleFromEventObservableTriggered(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue });
165
+ }
166
+ };
167
+
168
+ protected override onLastObserverRemoved(): void {
169
+ this.subscription!.dispose();
170
+ this.subscription = undefined;
171
+ this.hasValue = false;
172
+ this.value = undefined;
173
+ }
174
+
175
+ public get(): T {
176
+ if (this.subscription) {
177
+ if (!this.hasValue) {
178
+ this.handleEvent(undefined);
179
+ }
180
+ return this.value!;
181
+ } else {
182
+ // no cache, as there are no subscribers to keep it updated
183
+ const value = this._getValue(undefined);
184
+ return value;
185
+ }
186
+ }
187
+ }
188
+
189
+ export namespace observableFromEvent {
190
+ export const Observer = FromEventObservable;
191
+
192
+ export function batchEventsGlobally(tx: ITransaction, fn: () => void): void {
193
+ let didSet = false;
194
+ if (FromEventObservable.globalTransaction === undefined) {
195
+ FromEventObservable.globalTransaction = tx;
196
+ didSet = true;
197
+ }
198
+ try {
199
+ fn();
200
+ } finally {
201
+ if (didSet) {
202
+ FromEventObservable.globalTransaction = undefined;
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ export function observableSignalFromEvent(
209
+ debugName: string,
210
+ event: Event<any>
211
+ ): IObservable<void> {
212
+ return new FromEventObservableSignal(debugName, event);
213
+ }
214
+
215
+ class FromEventObservableSignal extends BaseObservable<void> {
216
+ private subscription: IDisposable | undefined;
217
+
218
+ constructor(
219
+ public readonly debugName: string,
220
+ private readonly event: Event<any>,
221
+ ) {
222
+ super();
223
+ }
224
+
225
+ protected override onFirstObserverAdded(): void {
226
+ this.subscription = this.event(this.handleEvent);
227
+ }
228
+
229
+ private readonly handleEvent = () => {
230
+ transaction(
231
+ (tx) => {
232
+ for (const o of this.observers) {
233
+ tx.updateObserver(o, this);
234
+ o.handleChange(this, undefined);
235
+ }
236
+ },
237
+ () => this.debugName
238
+ );
239
+ };
240
+
241
+ protected override onLastObserverRemoved(): void {
242
+ this.subscription!.dispose();
243
+ this.subscription = undefined;
244
+ }
245
+
246
+ public override get(): void {
247
+ // NO OP
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Creates a signal that can be triggered to invalidate observers.
253
+ * Signals don't have a value - when they are triggered they indicate a change.
254
+ * However, signals can carry a delta that is passed to observers.
255
+ */
256
+ export function observableSignal<TDelta = void>(debugName: string): IObservableSignal<TDelta>;
257
+ export function observableSignal<TDelta = void>(owner: object): IObservableSignal<TDelta>;
258
+ export function observableSignal<TDelta = void>(debugNameOrOwner: string | object): IObservableSignal<TDelta> {
259
+ if (typeof debugNameOrOwner === 'string') {
260
+ return new ObservableSignal<TDelta>(debugNameOrOwner);
261
+ } else {
262
+ return new ObservableSignal<TDelta>(undefined, debugNameOrOwner);
263
+ }
264
+ }
265
+
266
+ export interface IObservableSignal<TChange> extends IObservable<void, TChange> {
267
+ trigger(tx: ITransaction | undefined, change: TChange): void;
268
+ }
269
+
270
+ class ObservableSignal<TChange> extends BaseObservable<void, TChange> implements IObservableSignal<TChange> {
271
+ public get debugName() {
272
+ return new DebugNameData(this._owner, this._debugName, undefined).getDebugName(this) ?? 'Observable Signal';
273
+ }
274
+
275
+ public override toString(): string {
276
+ return this.debugName;
277
+ }
278
+
279
+ constructor(
280
+ private readonly _debugName: string | undefined,
281
+ private readonly _owner?: object,
282
+ ) {
283
+ super();
284
+ }
285
+
286
+ public trigger(tx: ITransaction | undefined, change: TChange): void {
287
+ if (!tx) {
288
+ transaction(tx => {
289
+ this.trigger(tx, change);
290
+ }, () => `Trigger signal ${this.debugName}`);
291
+ return;
292
+ }
293
+
294
+ for (const o of this.observers) {
295
+ tx.updateObserver(o, this);
296
+ o.handleChange(this, change);
297
+ }
298
+ }
299
+
300
+ public override get(): void {
301
+ // NO OP
302
+ }
303
+ }
304
+
305
+ /**
306
+ * @deprecated Use `debouncedObservable2` instead.
307
+ */
308
+ export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number, disposableStore: DisposableStore): IObservable<T | undefined> {
309
+ const debouncedObservable = observableValue<T | undefined>('debounced', undefined);
310
+
311
+ let timeout: any = undefined;
312
+
313
+ disposableStore.add(autorun(reader => {
314
+ /** @description debounce */
315
+ const value = observable.read(reader);
316
+
317
+ if (timeout) {
318
+ clearTimeout(timeout);
319
+ }
320
+ timeout = setTimeout(() => {
321
+ transaction(tx => {
322
+ debouncedObservable.set(value, tx);
323
+ });
324
+ }, debounceMs);
325
+
326
+ }));
327
+
328
+ return debouncedObservable;
329
+ }
330
+
331
+ /**
332
+ * Creates an observable that debounces the input observable.
333
+ */
334
+ export function debouncedObservable2<T>(observable: IObservable<T>, debounceMs: number): IObservable<T> {
335
+ let hasValue = false;
336
+ let lastValue: T | undefined;
337
+
338
+ let timeout: any = undefined;
339
+
340
+ return observableFromEvent<T, void>(cb => {
341
+ const d = autorun(reader => {
342
+ const value = observable.read(reader);
343
+
344
+ if (!hasValue) {
345
+ hasValue = true;
346
+ lastValue = value;
347
+ } else {
348
+ if (timeout) {
349
+ clearTimeout(timeout);
350
+ }
351
+ timeout = setTimeout(() => {
352
+ lastValue = value;
353
+ cb();
354
+ }, debounceMs);
355
+ }
356
+ });
357
+ return {
358
+ dispose() {
359
+ d.dispose();
360
+ hasValue = false;
361
+ lastValue = undefined;
362
+ },
363
+ };
364
+ }, () => {
365
+ if (hasValue) {
366
+ return lastValue!;
367
+ } else {
368
+ return observable.get();
369
+ }
370
+ });
371
+ }
372
+
373
+ export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
374
+ const observable = observableValue('triggeredRecently', false);
375
+
376
+ let timeout: any = undefined;
377
+
378
+ disposableStore.add(event(() => {
379
+ observable.set(true, undefined);
380
+
381
+ if (timeout) {
382
+ clearTimeout(timeout);
383
+ }
384
+ timeout = setTimeout(() => {
385
+ observable.set(false, undefined);
386
+ }, timeoutMs);
387
+ }));
388
+
389
+ return observable;
390
+ }
391
+
392
+ /**
393
+ * This makes sure the observable is being observed and keeps its cache alive.
394
+ */
395
+ export function keepObserved<T>(observable: IObservable<T>): IDisposable {
396
+ const o = new KeepAliveObserver(false, undefined);
397
+ observable.addObserver(o);
398
+ return toDisposable(() => {
399
+ observable.removeObserver(o);
400
+ });
401
+ }
402
+
403
+ _setKeepObserved(keepObserved);
404
+
405
+ /**
406
+ * This converts the given observable into an autorun.
407
+ */
408
+ export function recomputeInitiallyAndOnChange<T>(observable: IObservable<T>, handleValue?: (value: T) => void): IDisposable {
409
+ const o = new KeepAliveObserver(true, handleValue);
410
+ observable.addObserver(o);
411
+ if (handleValue) {
412
+ handleValue(observable.get());
413
+ } else {
414
+ observable.reportChanges();
415
+ }
416
+
417
+ return toDisposable(() => {
418
+ observable.removeObserver(o);
419
+ });
420
+ }
421
+
422
+ _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
423
+
424
+ export class KeepAliveObserver implements IObserver {
425
+ private _counter = 0;
426
+
427
+ constructor(
428
+ private readonly _forceRecompute: boolean,
429
+ private readonly _handleValue: ((value: any) => void) | undefined,
430
+ ) { }
431
+
432
+ beginUpdate<T>(observable: IObservable<T, void>): void {
433
+ this._counter++;
434
+ }
435
+
436
+ endUpdate<T>(observable: IObservable<T, void>): void {
437
+ this._counter--;
438
+ if (this._counter === 0 && this._forceRecompute) {
439
+ if (this._handleValue) {
440
+ this._handleValue(observable.get());
441
+ } else {
442
+ observable.reportChanges();
443
+ }
444
+ }
445
+ }
446
+
447
+ handlePossibleChange<T>(observable: IObservable<T, unknown>): void {
448
+ // NO OP
449
+ }
450
+
451
+ handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void {
452
+ // NO OP
453
+ }
454
+ }
455
+
456
+ export function derivedObservableWithCache<T>(owner: DebugOwner, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T> {
457
+ let lastValue: T | undefined = undefined;
458
+ const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
459
+ lastValue = computeFn(reader, lastValue);
460
+ return lastValue;
461
+ });
462
+ return observable;
463
+ }
464
+
465
+ export function derivedObservableWithWritableCache<T>(owner: object, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T>
466
+ & { clearCache(transaction: ITransaction): void; setCache(newValue: T | undefined, tx: ITransaction | undefined): void } {
467
+ let lastValue: T | undefined = undefined;
468
+ const onChange = observableSignal('derivedObservableWithWritableCache');
469
+ const observable = derived(owner, reader => {
470
+ onChange.read(reader);
471
+ lastValue = computeFn(reader, lastValue);
472
+ return lastValue;
473
+ });
474
+ return Object.assign(observable, {
475
+ clearCache: (tx: ITransaction) => {
476
+ lastValue = undefined;
477
+ onChange.trigger(tx);
478
+ },
479
+ setCache: (newValue: T | undefined, tx: ITransaction | undefined) => {
480
+ lastValue = newValue;
481
+ onChange.trigger(tx);
482
+ }
483
+ });
484
+ }
485
+
486
+ /**
487
+ * When the items array changes, referential equal items are not mapped again.
488
+ */
489
+ export function mapObservableArrayCached<TIn, TOut, TKey = TIn>(owner: DebugOwner, items: IObservable<readonly TIn[]>, map: (input: TIn, store: DisposableStore) => TOut, keySelector?: (input: TIn) => TKey): IObservable<readonly TOut[]> {
490
+ let m = new ArrayMap(map, keySelector);
491
+ const self = derivedOpts({
492
+ debugReferenceFn: map,
493
+ owner,
494
+ onLastObserverRemoved: () => {
495
+ m.dispose();
496
+ m = new ArrayMap(map);
497
+ }
498
+ }, (reader) => {
499
+ m.setItems(items.read(reader));
500
+ return m.getItems();
501
+ });
502
+ return self;
503
+ }
504
+
505
+ class ArrayMap<TIn, TOut, TKey> implements IDisposable {
506
+ private readonly _cache = new Map<TKey, { out: TOut; store: DisposableStore }>();
507
+ private _items: TOut[] = [];
508
+ constructor(
509
+ private readonly _map: (input: TIn, store: DisposableStore) => TOut,
510
+ private readonly _keySelector?: (input: TIn) => TKey,
511
+ ) {
512
+ }
513
+
514
+ public dispose(): void {
515
+ this._cache.forEach(entry => entry.store.dispose());
516
+ this._cache.clear();
517
+ }
518
+
519
+ public setItems(items: readonly TIn[]): void {
520
+ const newItems: TOut[] = [];
521
+ const itemsToRemove = new Set(this._cache.keys());
522
+
523
+ for (const item of items) {
524
+ const key = this._keySelector ? this._keySelector(item) : item as unknown as TKey;
525
+
526
+ let entry = this._cache.get(key);
527
+ if (!entry) {
528
+ const store = new DisposableStore();
529
+ const out = this._map(item, store);
530
+ entry = { out, store };
531
+ this._cache.set(key, entry);
532
+ } else {
533
+ itemsToRemove.delete(key);
534
+ }
535
+ newItems.push(entry.out);
536
+ }
537
+
538
+ for (const item of itemsToRemove) {
539
+ const entry = this._cache.get(item)!;
540
+ entry.store.dispose();
541
+ this._cache.delete(item);
542
+ }
543
+
544
+ this._items = newItems;
545
+ }
546
+
547
+ public getItems(): TOut[] {
548
+ return this._items;
549
+ }
550
+ }
551
+
552
+ export class ValueWithChangeEventFromObservable<T> implements IValueWithChangeEvent<T> {
553
+ constructor(public readonly observable: IObservable<T>) {
554
+ }
555
+
556
+ get onDidChange(): Event<void> {
557
+ return Event.fromObservableLight(this.observable);
558
+ }
559
+
560
+ get value(): T {
561
+ return this.observable.get();
562
+ }
563
+ }
564
+
565
+ export function observableFromValueWithChangeEvent<T>(owner: DebugOwner, value: IValueWithChangeEvent<T>): IObservable<T> {
566
+ if (value instanceof ValueWithChangeEventFromObservable) {
567
+ return value.observable;
568
+ }
569
+ return observableFromEvent(owner, value.onDidChange, () => value.value);
570
+ }
571
+
572
+ /**
573
+ * Creates an observable that has the latest changed value of the given observables.
574
+ * Initially (and when not observed), it has the value of the last observable.
575
+ * When observed and any of the observables change, it has the value of the last changed observable.
576
+ * If multiple observables change in the same transaction, the last observable wins.
577
+ */
578
+ export function latestChangedValue<T extends IObservable<any>[]>(owner: DebugOwner, observables: T): IObservable<ReturnType<T[number]['get']>> {
579
+ if (observables.length === 0) {
580
+ throw new BugIndicatingError();
581
+ }
582
+
583
+ let hasLastChangedValue = false;
584
+ let lastChangedValue: any = undefined;
585
+
586
+ const result = observableFromEvent<any, void>(owner, cb => {
587
+ const store = new DisposableStore();
588
+ for (const o of observables) {
589
+ store.add(autorunOpts({ debugName: () => getDebugName(result, new DebugNameData(owner, undefined, undefined)) + '.updateLastChangedValue' }, reader => {
590
+ hasLastChangedValue = true;
591
+ lastChangedValue = o.read(reader);
592
+ cb();
593
+ }));
594
+ }
595
+ store.add({
596
+ dispose() {
597
+ hasLastChangedValue = false;
598
+ lastChangedValue = undefined;
599
+ },
600
+ });
601
+ return store;
602
+ }, () => {
603
+ if (hasLastChangedValue) {
604
+ return lastChangedValue;
605
+ } else {
606
+ return observables[observables.length - 1].get();
607
+ }
608
+ });
609
+ return result;
610
+ }