@xterm/xterm 5.6.0-beta.6 → 5.6.0-beta.60
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.
- package/README.md +7 -3
- package/css/xterm.css +71 -4
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +53 -0
- package/lib/xterm.mjs.map +7 -0
- package/package.json +43 -33
- package/src/browser/AccessibilityManager.ts +53 -25
- package/src/browser/{Terminal.ts → CoreBrowserTerminal.ts} +132 -144
- package/src/browser/Linkifier.ts +15 -13
- package/src/browser/LocalizableStrings.ts +15 -4
- package/src/browser/{Types.d.ts → Types.ts} +67 -15
- package/src/browser/Viewport.ts +142 -370
- package/src/browser/decorations/BufferDecorationRenderer.ts +14 -9
- package/src/browser/decorations/OverviewRulerRenderer.ts +40 -44
- package/src/browser/public/Terminal.ts +25 -19
- package/src/browser/renderer/dom/DomRenderer.ts +14 -16
- package/src/browser/renderer/shared/CharAtlasUtils.ts +4 -0
- package/src/browser/renderer/shared/CustomGlyphs.ts +6 -0
- package/src/browser/renderer/shared/DevicePixelObserver.ts +1 -2
- package/src/browser/renderer/shared/TextureAtlas.ts +3 -3
- package/src/browser/renderer/shared/{Types.d.ts → Types.ts} +4 -4
- package/src/browser/services/CharSizeService.ts +6 -6
- package/src/browser/services/CoreBrowserService.ts +15 -15
- package/src/browser/services/LinkProviderService.ts +2 -2
- package/src/browser/services/RenderService.ts +20 -20
- package/src/browser/services/SelectionService.ts +8 -8
- package/src/browser/services/Services.ts +13 -13
- package/src/browser/services/ThemeService.ts +17 -56
- package/src/browser/shared/Constants.ts +8 -0
- package/src/common/CircularList.ts +5 -5
- package/src/common/CoreTerminal.ts +35 -41
- package/src/common/InputHandler.ts +34 -28
- package/src/common/{Types.d.ts → Types.ts} +11 -17
- package/src/common/buffer/Buffer.ts +5 -1
- package/src/common/buffer/BufferSet.ts +5 -5
- package/src/common/buffer/Marker.ts +4 -4
- package/src/common/buffer/{Types.d.ts → Types.ts} +2 -2
- package/src/common/input/WriteBuffer.ts +3 -3
- package/src/common/parser/EscapeSequenceParser.ts +4 -4
- package/src/common/public/BufferNamespaceApi.ts +3 -3
- package/src/common/services/BufferService.ts +7 -7
- package/src/common/services/CoreMouseService.ts +5 -3
- package/src/common/services/CoreService.ts +6 -6
- package/src/common/services/DecorationService.ts +8 -9
- package/src/common/services/LogService.ts +2 -2
- package/src/common/services/OptionsService.ts +5 -5
- package/src/common/services/Services.ts +24 -17
- package/src/common/services/UnicodeService.ts +2 -2
- package/src/vs/base/browser/browser.ts +141 -0
- package/src/vs/base/browser/canIUse.ts +49 -0
- package/src/vs/base/browser/dom.ts +2369 -0
- package/src/vs/base/browser/fastDomNode.ts +316 -0
- package/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
- package/src/vs/base/browser/iframe.ts +135 -0
- package/src/vs/base/browser/keyboardEvent.ts +213 -0
- package/src/vs/base/browser/mouseEvent.ts +229 -0
- package/src/vs/base/browser/touch.ts +372 -0
- package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
- package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
- package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
- package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
- package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
- package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
- package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
- package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
- package/src/vs/base/browser/ui/widget.ts +57 -0
- package/src/vs/base/browser/window.ts +14 -0
- package/src/vs/base/common/arrays.ts +887 -0
- package/src/vs/base/common/arraysFind.ts +202 -0
- package/src/vs/base/common/assert.ts +71 -0
- package/src/vs/base/common/async.ts +1992 -0
- package/src/vs/base/common/cancellation.ts +148 -0
- package/src/vs/base/common/charCode.ts +450 -0
- package/src/vs/base/common/collections.ts +140 -0
- package/src/vs/base/common/decorators.ts +130 -0
- package/src/vs/base/common/equals.ts +146 -0
- package/src/vs/base/common/errors.ts +303 -0
- package/src/vs/base/common/event.ts +1778 -0
- package/src/vs/base/common/functional.ts +32 -0
- package/src/vs/base/common/hash.ts +316 -0
- package/src/vs/base/common/iterator.ts +159 -0
- package/src/vs/base/common/keyCodes.ts +526 -0
- package/src/vs/base/common/keybindings.ts +284 -0
- package/src/vs/base/common/lazy.ts +47 -0
- package/src/vs/base/common/lifecycle.ts +801 -0
- package/src/vs/base/common/linkedList.ts +142 -0
- package/src/vs/base/common/map.ts +202 -0
- package/src/vs/base/common/numbers.ts +98 -0
- package/src/vs/base/common/observable.ts +76 -0
- package/src/vs/base/common/observableInternal/api.ts +31 -0
- package/src/vs/base/common/observableInternal/autorun.ts +281 -0
- package/src/vs/base/common/observableInternal/base.ts +489 -0
- package/src/vs/base/common/observableInternal/debugName.ts +145 -0
- package/src/vs/base/common/observableInternal/derived.ts +428 -0
- package/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
- package/src/vs/base/common/observableInternal/logging.ts +328 -0
- package/src/vs/base/common/observableInternal/promise.ts +209 -0
- package/src/vs/base/common/observableInternal/utils.ts +610 -0
- package/src/vs/base/common/platform.ts +281 -0
- package/src/vs/base/common/scrollable.ts +522 -0
- package/src/vs/base/common/sequence.ts +34 -0
- package/src/vs/base/common/stopwatch.ts +43 -0
- package/src/vs/base/common/strings.ts +557 -0
- package/src/vs/base/common/symbols.ts +9 -0
- package/src/vs/base/common/uint.ts +59 -0
- package/src/vs/patches/nls.ts +90 -0
- package/src/vs/typings/base-common.d.ts +20 -0
- package/src/vs/typings/require.d.ts +42 -0
- package/src/vs/typings/thenable.d.ts +12 -0
- package/src/vs/typings/vscode-globals-nls.d.ts +36 -0
- package/src/vs/typings/vscode-globals-product.d.ts +33 -0
- package/typings/xterm.d.ts +59 -15
- package/src/browser/Lifecycle.ts +0 -33
- package/src/common/EventEmitter.ts +0 -78
- package/src/common/Lifecycle.ts +0 -108
- /package/src/browser/selection/{Types.d.ts → Types.ts} +0 -0
- /package/src/common/parser/{Types.d.ts → Types.ts} +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
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 { strictEquals, EqualityComparer } from 'vs/base/common/equals';
|
|
7
|
+
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
|
8
|
+
import { keepObserved, recomputeInitiallyAndOnChange } from 'vs/base/common/observable';
|
|
9
|
+
import { DebugNameData, DebugOwner, getFunctionName } from 'vs/base/common/observableInternal/debugName';
|
|
10
|
+
import type { derivedOpts } from 'vs/base/common/observableInternal/derived';
|
|
11
|
+
import { getLogger } from 'vs/base/common/observableInternal/logging';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents an observable value.
|
|
15
|
+
*
|
|
16
|
+
* @template T The type of the values the observable can hold.
|
|
17
|
+
* @template TChange The type used to describe value changes
|
|
18
|
+
* (usually `void` and only used in advanced scenarios).
|
|
19
|
+
* While observers can miss temporary values of an observable,
|
|
20
|
+
* they will receive all change values (as long as they are subscribed)!
|
|
21
|
+
*/
|
|
22
|
+
export interface IObservable<T, TChange = unknown> {
|
|
23
|
+
/**
|
|
24
|
+
* Returns the current value.
|
|
25
|
+
*
|
|
26
|
+
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
|
|
27
|
+
* Must not be called from {@link IObserver.handleChange}!
|
|
28
|
+
*/
|
|
29
|
+
get(): T;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Forces the observable to check for changes and report them.
|
|
33
|
+
*
|
|
34
|
+
* Has the same effect as calling {@link IObservable.get}, but does not force the observable
|
|
35
|
+
* to actually construct the value, e.g. if change deltas are used.
|
|
36
|
+
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
|
|
37
|
+
* Must not be called from {@link IObserver.handleChange}!
|
|
38
|
+
*/
|
|
39
|
+
reportChanges(): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Adds the observer to the set of subscribed observers.
|
|
43
|
+
* This method is idempotent.
|
|
44
|
+
*/
|
|
45
|
+
addObserver(observer: IObserver): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Removes the observer from the set of subscribed observers.
|
|
49
|
+
* This method is idempotent.
|
|
50
|
+
*/
|
|
51
|
+
removeObserver(observer: IObserver): void;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reads the current value and subscribes the reader to this observable.
|
|
55
|
+
*
|
|
56
|
+
* Calls {@link IReader.readObservable} if a reader is given, otherwise {@link IObservable.get}
|
|
57
|
+
* (see {@link ConvenientObservable.read} for the implementation).
|
|
58
|
+
*/
|
|
59
|
+
read(reader: IReader | undefined): T;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Creates a derived observable that depends on this observable.
|
|
63
|
+
* Use the reader to read other observables
|
|
64
|
+
* (see {@link ConvenientObservable.map} for the implementation).
|
|
65
|
+
*/
|
|
66
|
+
map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
|
|
67
|
+
map<TNew>(owner: object, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Makes sure this value is computed eagerly.
|
|
71
|
+
*/
|
|
72
|
+
recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Makes sure this value is cached.
|
|
76
|
+
*/
|
|
77
|
+
keepObserved(store: DisposableStore): IObservable<T>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A human-readable name for debugging purposes.
|
|
81
|
+
*/
|
|
82
|
+
readonly debugName: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* This property captures the type of the change object. Do not use it at runtime!
|
|
86
|
+
*/
|
|
87
|
+
readonly TChange: TChange;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface IReader {
|
|
91
|
+
/**
|
|
92
|
+
* Reads the value of an observable and subscribes to it.
|
|
93
|
+
*/
|
|
94
|
+
readObservable<T>(observable: IObservable<T, any>): T;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Represents an observer that can be subscribed to an observable.
|
|
99
|
+
*
|
|
100
|
+
* If an observer is subscribed to an observable and that observable didn't signal
|
|
101
|
+
* a change through one of the observer methods, the observer can assume that the
|
|
102
|
+
* observable didn't change.
|
|
103
|
+
* If an observable reported a possible change, {@link IObservable.reportChanges} forces
|
|
104
|
+
* the observable to report an actual change if there was one.
|
|
105
|
+
*/
|
|
106
|
+
export interface IObserver {
|
|
107
|
+
/**
|
|
108
|
+
* Signals that the given observable might have changed and a transaction potentially modifying that observable started.
|
|
109
|
+
* Before the given observable can call this method again, is must call {@link IObserver.endUpdate}.
|
|
110
|
+
*
|
|
111
|
+
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
|
|
112
|
+
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
|
|
113
|
+
*/
|
|
114
|
+
beginUpdate<T>(observable: IObservable<T>): void;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Signals that the transaction that potentially modified the given observable ended.
|
|
118
|
+
* This is a good place to react to (potential) changes.
|
|
119
|
+
*/
|
|
120
|
+
endUpdate<T>(observable: IObservable<T>): void;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Signals that the given observable might have changed.
|
|
124
|
+
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
|
|
125
|
+
*
|
|
126
|
+
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
|
|
127
|
+
* The change should be processed lazily or in {@link IObserver.endUpdate}.
|
|
128
|
+
*/
|
|
129
|
+
handlePossibleChange<T>(observable: IObservable<T>): void;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Signals that the given {@link observable} changed.
|
|
133
|
+
*
|
|
134
|
+
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
|
|
135
|
+
* The change should be processed lazily or in {@link IObserver.endUpdate}.
|
|
136
|
+
*
|
|
137
|
+
* @param change Indicates how or why the value changed.
|
|
138
|
+
*/
|
|
139
|
+
handleChange<T, TChange>(observable: IObservable<T, TChange>, change: TChange): void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ISettable<T, TChange = void> {
|
|
143
|
+
/**
|
|
144
|
+
* Sets the value of the observable.
|
|
145
|
+
* Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction).
|
|
146
|
+
*
|
|
147
|
+
* @param transaction When given, value changes are handled on demand or when the transaction ends.
|
|
148
|
+
* @param change Describes how or why the value changed.
|
|
149
|
+
*/
|
|
150
|
+
set(value: T, transaction: ITransaction | undefined, change: TChange): void;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface ITransaction {
|
|
154
|
+
/**
|
|
155
|
+
* Calls {@link Observer.beginUpdate} immediately
|
|
156
|
+
* and {@link Observer.endUpdate} when the transaction ends.
|
|
157
|
+
*/
|
|
158
|
+
updateObserver(observer: IObserver, observable: IObservable<any, any>): void;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
|
|
162
|
+
export function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange: typeof _recomputeInitiallyAndOnChange) {
|
|
163
|
+
_recomputeInitiallyAndOnChange = recomputeInitiallyAndOnChange;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let _keepObserved: typeof keepObserved;
|
|
167
|
+
export function _setKeepObserved(keepObserved: typeof _keepObserved) {
|
|
168
|
+
_keepObserved = keepObserved;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
let _derived: typeof derivedOpts;
|
|
173
|
+
/**
|
|
174
|
+
* @internal
|
|
175
|
+
* This is to allow splitting files.
|
|
176
|
+
*/
|
|
177
|
+
export function _setDerivedOpts(derived: typeof _derived) {
|
|
178
|
+
_derived = derived;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export abstract class ConvenientObservable<T, TChange> implements IObservable<T, TChange> {
|
|
182
|
+
get TChange(): TChange { return null!; }
|
|
183
|
+
|
|
184
|
+
public abstract get(): T;
|
|
185
|
+
|
|
186
|
+
public reportChanges(): void {
|
|
187
|
+
this.get();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public abstract addObserver(observer: IObserver): void;
|
|
191
|
+
public abstract removeObserver(observer: IObserver): void;
|
|
192
|
+
|
|
193
|
+
/** @sealed */
|
|
194
|
+
public read(reader: IReader | undefined): T {
|
|
195
|
+
if (reader) {
|
|
196
|
+
return reader.readObservable(this);
|
|
197
|
+
} else {
|
|
198
|
+
return this.get();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** @sealed */
|
|
203
|
+
public map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
|
|
204
|
+
public map<TNew>(owner: DebugOwner, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
|
|
205
|
+
public map<TNew>(fnOrOwner: DebugOwner | ((value: T, reader: IReader) => TNew), fnOrUndefined?: (value: T, reader: IReader) => TNew): IObservable<TNew> {
|
|
206
|
+
const owner = fnOrUndefined === undefined ? undefined : fnOrOwner as DebugOwner;
|
|
207
|
+
const fn = fnOrUndefined === undefined ? fnOrOwner as (value: T, reader: IReader) => TNew : fnOrUndefined;
|
|
208
|
+
|
|
209
|
+
return _derived(
|
|
210
|
+
{
|
|
211
|
+
owner,
|
|
212
|
+
debugName: () => {
|
|
213
|
+
const name = getFunctionName(fn);
|
|
214
|
+
if (name !== undefined) {
|
|
215
|
+
return name;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// regexp to match `x => x.y` or `x => x?.y` where x and y can be arbitrary identifiers (uses backref):
|
|
219
|
+
const regexp = /^\s*\(?\s*([a-zA-Z_$][a-zA-Z_$0-9]*)\s*\)?\s*=>\s*\1(?:\??)\.([a-zA-Z_$][a-zA-Z_$0-9]*)\s*$/;
|
|
220
|
+
const match = regexp.exec(fn.toString());
|
|
221
|
+
if (match) {
|
|
222
|
+
return `${this.debugName}.${match[2]}`;
|
|
223
|
+
}
|
|
224
|
+
if (!owner) {
|
|
225
|
+
return `${this.debugName} (mapped)`;
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
},
|
|
229
|
+
debugReferenceFn: fn,
|
|
230
|
+
},
|
|
231
|
+
(reader) => fn(this.read(reader), reader),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T> {
|
|
236
|
+
store.add(_recomputeInitiallyAndOnChange!(this, handleValue));
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Ensures that this observable is observed. This keeps the cache alive.
|
|
242
|
+
* However, in case of deriveds, it does not force eager evaluation (only when the value is read/get).
|
|
243
|
+
* Use `recomputeInitiallyAndOnChange` for eager evaluation.
|
|
244
|
+
*/
|
|
245
|
+
public keepObserved(store: DisposableStore): IObservable<T> {
|
|
246
|
+
store.add(_keepObserved!(this));
|
|
247
|
+
return this;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public abstract get debugName(): string;
|
|
251
|
+
|
|
252
|
+
protected get debugValue() {
|
|
253
|
+
return this.get();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
|
|
258
|
+
protected readonly observers = new Set<IObserver>();
|
|
259
|
+
|
|
260
|
+
public addObserver(observer: IObserver): void {
|
|
261
|
+
const len = this.observers.size;
|
|
262
|
+
this.observers.add(observer);
|
|
263
|
+
if (len === 0) {
|
|
264
|
+
this.onFirstObserverAdded();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public removeObserver(observer: IObserver): void {
|
|
269
|
+
const deleted = this.observers.delete(observer);
|
|
270
|
+
if (deleted && this.observers.size === 0) {
|
|
271
|
+
this.onLastObserverRemoved();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
protected onFirstObserverAdded(): void { }
|
|
276
|
+
protected onLastObserverRemoved(): void { }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Starts a transaction in which many observables can be changed at once.
|
|
281
|
+
* {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
|
|
282
|
+
* Reaction run on demand or when the transaction ends.
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
|
|
286
|
+
const tx = new TransactionImpl(fn, getDebugName);
|
|
287
|
+
try {
|
|
288
|
+
fn(tx);
|
|
289
|
+
} finally {
|
|
290
|
+
tx.finish();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let _globalTransaction: ITransaction | undefined = undefined;
|
|
295
|
+
|
|
296
|
+
export function globalTransaction(fn: (tx: ITransaction) => void) {
|
|
297
|
+
if (_globalTransaction) {
|
|
298
|
+
fn(_globalTransaction);
|
|
299
|
+
} else {
|
|
300
|
+
const tx = new TransactionImpl(fn, undefined);
|
|
301
|
+
_globalTransaction = tx;
|
|
302
|
+
try {
|
|
303
|
+
fn(tx);
|
|
304
|
+
} finally {
|
|
305
|
+
tx.finish(); // During finish, more actions might be added to the transaction.
|
|
306
|
+
// Which is why we only clear the global transaction after finish.
|
|
307
|
+
_globalTransaction = undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void> {
|
|
313
|
+
const tx = new TransactionImpl(fn, getDebugName);
|
|
314
|
+
try {
|
|
315
|
+
await fn(tx);
|
|
316
|
+
} finally {
|
|
317
|
+
tx.finish();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Allows to chain transactions.
|
|
323
|
+
*/
|
|
324
|
+
export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
|
|
325
|
+
if (!tx) {
|
|
326
|
+
transaction(fn, getDebugName);
|
|
327
|
+
} else {
|
|
328
|
+
fn(tx);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export class TransactionImpl implements ITransaction {
|
|
333
|
+
private updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
|
|
334
|
+
|
|
335
|
+
constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) {
|
|
336
|
+
getLogger()?.handleBeginTransaction(this);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
public getDebugName(): string | undefined {
|
|
340
|
+
if (this._getDebugName) {
|
|
341
|
+
return this._getDebugName();
|
|
342
|
+
}
|
|
343
|
+
return getFunctionName(this._fn);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
public updateObserver(observer: IObserver, observable: IObservable<any>): void {
|
|
347
|
+
// When this gets called while finish is active, they will still get considered
|
|
348
|
+
this.updatingObservers!.push({ observer, observable });
|
|
349
|
+
observer.beginUpdate(observable);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
public finish(): void {
|
|
353
|
+
const updatingObservers = this.updatingObservers!;
|
|
354
|
+
for (let i = 0; i < updatingObservers.length; i++) {
|
|
355
|
+
const { observer, observable } = updatingObservers[i];
|
|
356
|
+
observer.endUpdate(observable);
|
|
357
|
+
}
|
|
358
|
+
// Prevent anyone from updating observers from now on.
|
|
359
|
+
this.updatingObservers = null;
|
|
360
|
+
getLogger()?.handleEndTransaction();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* A settable observable.
|
|
366
|
+
*/
|
|
367
|
+
export interface ISettableObservable<T, TChange = void> extends IObservable<T, TChange>, ISettable<T, TChange> {
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Creates an observable value.
|
|
372
|
+
* Observers get informed when the value changes.
|
|
373
|
+
* @template TChange An arbitrary type to describe how or why the value changed. Defaults to `void`.
|
|
374
|
+
* Observers will receive every single change value.
|
|
375
|
+
*/
|
|
376
|
+
export function observableValue<T, TChange = void>(name: string, initialValue: T): ISettableObservable<T, TChange>;
|
|
377
|
+
export function observableValue<T, TChange = void>(owner: object, initialValue: T): ISettableObservable<T, TChange>;
|
|
378
|
+
export function observableValue<T, TChange = void>(nameOrOwner: string | object, initialValue: T): ISettableObservable<T, TChange> {
|
|
379
|
+
let debugNameData: DebugNameData;
|
|
380
|
+
if (typeof nameOrOwner === 'string') {
|
|
381
|
+
debugNameData = new DebugNameData(undefined, nameOrOwner, undefined);
|
|
382
|
+
} else {
|
|
383
|
+
debugNameData = new DebugNameData(nameOrOwner, undefined, undefined);
|
|
384
|
+
}
|
|
385
|
+
return new ObservableValue(debugNameData, initialValue, strictEquals);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export class ObservableValue<T, TChange = void>
|
|
389
|
+
extends BaseObservable<T, TChange>
|
|
390
|
+
implements ISettableObservable<T, TChange> {
|
|
391
|
+
protected _value: T;
|
|
392
|
+
|
|
393
|
+
get debugName() {
|
|
394
|
+
return this._debugNameData.getDebugName(this) ?? 'ObservableValue';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
constructor(
|
|
398
|
+
private readonly _debugNameData: DebugNameData,
|
|
399
|
+
initialValue: T,
|
|
400
|
+
private readonly _equalityComparator: EqualityComparer<T>,
|
|
401
|
+
) {
|
|
402
|
+
super();
|
|
403
|
+
this._value = initialValue;
|
|
404
|
+
}
|
|
405
|
+
public override get(): T {
|
|
406
|
+
return this._value;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
public set(value: T, tx: ITransaction | undefined, change: TChange): void {
|
|
410
|
+
if (change === undefined && this._equalityComparator(this._value, value)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
let _tx: TransactionImpl | undefined;
|
|
415
|
+
if (!tx) {
|
|
416
|
+
tx = _tx = new TransactionImpl(() => { }, () => `Setting ${this.debugName}`);
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const oldValue = this._value;
|
|
420
|
+
this._setValue(value);
|
|
421
|
+
getLogger()?.handleObservableChanged(this, { oldValue, newValue: value, change, didChange: true, hadValue: true });
|
|
422
|
+
|
|
423
|
+
for (const observer of this.observers) {
|
|
424
|
+
tx.updateObserver(observer, this);
|
|
425
|
+
observer.handleChange(this, change);
|
|
426
|
+
}
|
|
427
|
+
} finally {
|
|
428
|
+
if (_tx) {
|
|
429
|
+
_tx.finish();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
override toString(): string {
|
|
435
|
+
return `${this.debugName}: ${this._value}`;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
protected _setValue(newValue: T): void {
|
|
439
|
+
this._value = newValue;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* A disposable observable. When disposed, its value is also disposed.
|
|
445
|
+
* When a new value is set, the previous value is disposed.
|
|
446
|
+
*/
|
|
447
|
+
export function disposableObservableValue<T extends IDisposable | undefined, TChange = void>(nameOrOwner: string | object, initialValue: T): ISettableObservable<T, TChange> & IDisposable {
|
|
448
|
+
let debugNameData: DebugNameData;
|
|
449
|
+
if (typeof nameOrOwner === 'string') {
|
|
450
|
+
debugNameData = new DebugNameData(undefined, nameOrOwner, undefined);
|
|
451
|
+
} else {
|
|
452
|
+
debugNameData = new DebugNameData(nameOrOwner, undefined, undefined);
|
|
453
|
+
}
|
|
454
|
+
return new DisposableObservableValue(debugNameData, initialValue, strictEquals);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export class DisposableObservableValue<T extends IDisposable | undefined, TChange = void> extends ObservableValue<T, TChange> implements IDisposable {
|
|
458
|
+
protected override _setValue(newValue: T): void {
|
|
459
|
+
if (this._value === newValue) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (this._value) {
|
|
463
|
+
this._value.dispose();
|
|
464
|
+
}
|
|
465
|
+
this._value = newValue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
public dispose(): void {
|
|
469
|
+
this._value?.dispose();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export interface IChangeTracker {
|
|
474
|
+
/**
|
|
475
|
+
* Returns if this change should cause an invalidation.
|
|
476
|
+
* Implementations can record changes.
|
|
477
|
+
*/
|
|
478
|
+
handleChange(context: IChangeContext): boolean;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export interface IChangeContext {
|
|
482
|
+
readonly changedObservable: IObservable<any, any>;
|
|
483
|
+
readonly change: unknown;
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Returns if the given observable caused the change.
|
|
487
|
+
*/
|
|
488
|
+
didChange<T, TChange>(observable: IObservable<T, TChange>): this is { change: TChange };
|
|
489
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
export interface IDebugNameData {
|
|
7
|
+
/**
|
|
8
|
+
* The owner object of an observable.
|
|
9
|
+
* Used for debugging only, such as computing a name for the observable by iterating over the fields of the owner.
|
|
10
|
+
*/
|
|
11
|
+
readonly owner?: DebugOwner | undefined;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A string or function that returns a string that represents the name of the observable.
|
|
15
|
+
* Used for debugging only.
|
|
16
|
+
*/
|
|
17
|
+
readonly debugName?: DebugNameSource | undefined;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A function that points to the defining function of the object.
|
|
21
|
+
* Used for debugging only.
|
|
22
|
+
*/
|
|
23
|
+
readonly debugReferenceFn?: Function | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class DebugNameData {
|
|
27
|
+
constructor(
|
|
28
|
+
public readonly owner: DebugOwner | undefined,
|
|
29
|
+
public readonly debugNameSource: DebugNameSource | undefined,
|
|
30
|
+
public readonly referenceFn: Function | undefined,
|
|
31
|
+
) { }
|
|
32
|
+
|
|
33
|
+
public getDebugName(target: object): string | undefined {
|
|
34
|
+
return getDebugName(target, this);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The owning object of an observable.
|
|
40
|
+
* Is only used for debugging purposes, such as computing a name for the observable by iterating over the fields of the owner.
|
|
41
|
+
*/
|
|
42
|
+
export type DebugOwner = object | undefined;
|
|
43
|
+
export type DebugNameSource = string | (() => string | undefined);
|
|
44
|
+
|
|
45
|
+
const countPerName = new Map<string, number>();
|
|
46
|
+
const cachedDebugName = new WeakMap<object, string>();
|
|
47
|
+
|
|
48
|
+
export function getDebugName(target: object, data: DebugNameData): string | undefined {
|
|
49
|
+
const cached = cachedDebugName.get(target);
|
|
50
|
+
if (cached) {
|
|
51
|
+
return cached;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const dbgName = computeDebugName(target, data);
|
|
55
|
+
if (dbgName) {
|
|
56
|
+
let count = countPerName.get(dbgName) ?? 0;
|
|
57
|
+
count++;
|
|
58
|
+
countPerName.set(dbgName, count);
|
|
59
|
+
const result = count === 1 ? dbgName : `${dbgName}#${count}`;
|
|
60
|
+
cachedDebugName.set(target, result);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function computeDebugName(self: object, data: DebugNameData): string | undefined {
|
|
67
|
+
const cached = cachedDebugName.get(self);
|
|
68
|
+
if (cached) {
|
|
69
|
+
return cached;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ownerStr = data.owner ? formatOwner(data.owner) + `.` : '';
|
|
73
|
+
|
|
74
|
+
let result: string | undefined;
|
|
75
|
+
const debugNameSource = data.debugNameSource;
|
|
76
|
+
if (debugNameSource !== undefined) {
|
|
77
|
+
if (typeof debugNameSource === 'function') {
|
|
78
|
+
result = debugNameSource();
|
|
79
|
+
if (result !== undefined) {
|
|
80
|
+
return ownerStr + result;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
return ownerStr + debugNameSource;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const referenceFn = data.referenceFn;
|
|
88
|
+
if (referenceFn !== undefined) {
|
|
89
|
+
result = getFunctionName(referenceFn);
|
|
90
|
+
if (result !== undefined) {
|
|
91
|
+
return ownerStr + result;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (data.owner !== undefined) {
|
|
96
|
+
const key = findKey(data.owner, self);
|
|
97
|
+
if (key !== undefined) {
|
|
98
|
+
return ownerStr + key;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function findKey(obj: object, value: object): string | undefined {
|
|
105
|
+
for (const key in obj) {
|
|
106
|
+
if ((obj as any)[key] === value) {
|
|
107
|
+
return key;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const countPerClassName = new Map<string, number>();
|
|
114
|
+
const ownerId = new WeakMap<object, string>();
|
|
115
|
+
|
|
116
|
+
function formatOwner(owner: object): string {
|
|
117
|
+
const id = ownerId.get(owner);
|
|
118
|
+
if (id) {
|
|
119
|
+
return id;
|
|
120
|
+
}
|
|
121
|
+
const className = getClassName(owner);
|
|
122
|
+
let count = countPerClassName.get(className) ?? 0;
|
|
123
|
+
count++;
|
|
124
|
+
countPerClassName.set(className, count);
|
|
125
|
+
const result = count === 1 ? className : `${className}#${count}`;
|
|
126
|
+
ownerId.set(owner, result);
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getClassName(obj: object): string {
|
|
131
|
+
const ctor = obj.constructor;
|
|
132
|
+
if (ctor) {
|
|
133
|
+
return ctor.name;
|
|
134
|
+
}
|
|
135
|
+
return 'Object';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function getFunctionName(fn: Function): string | undefined {
|
|
139
|
+
const fnSrc = fn.toString();
|
|
140
|
+
// Pattern: /** @description ... */
|
|
141
|
+
const regexp = /\/\*\*\s*@description\s*([^*]*)\*\//;
|
|
142
|
+
const match = regexp.exec(fnSrc);
|
|
143
|
+
const result = match ? match[1] : undefined;
|
|
144
|
+
return result?.trim();
|
|
145
|
+
}
|