1
>
/*---------------------------------------------------------------------------------------------
base.ts
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 { DisposableStore, onUnexpectedError } from './commonFacade/deps.js';
7
>
8
>
/**
9
>
* Represents an observable value.
10
>
*
11
>
* @template T The type of the values the observable can hold.
12
>
*/
13
>
// This interface exists so that, for example for string observables,
14
>
// typescript renders the type as `IObservable<string>` instead of `IObservable<string, unknown>`.
15
>
export interface IObservable<T> extends IObservableWithChange<T, unknown> { }
16
>
17
>
/**
18
>
* Represents an observable value.
19
>
*
20
>
* @template T The type of the values the observable can hold.
21
>
* @template TChange The type used to describe value changes
22
>
* (usually `void` and only used in advanced scenarios).
23
>
* While observers can miss temporary values of an observable,
24
>
* they will receive all change values (as long as they are subscribed)!
25
>
*/
26
>
export interface IObservableWithChange<T, TChange = unknown> {
27
>
/**
28
>
* Returns the current value.
29
>
*
30
>
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
31
>
* Must not be called from {@link IObserver.handleChange}!
32
>
*/
33
>
get(): T;
34
>
35
>
/**
36
>
* Forces the observable to check for changes and report them.
37
>
*
38
>
* Has the same effect as calling {@link IObservable.get}, but does not force the observable
39
>
* to actually construct the value, e.g. if change deltas are used.
40
>
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
41
>
* Must not be called from {@link IObserver.handleChange}!
42
>
*/
43
>
reportChanges(): void;
44
>
45
>
/**
46
>
* Adds the observer to the set of subscribed observers.
47
>
* This method is idempotent.
48
>
*/
49
>
addObserver(observer: IObserver): void;
50
>
51
>
/**
52
>
* Removes the observer from the set of subscribed observers.
53
>
* This method is idempotent.
54
>
*/
55
>
removeObserver(observer: IObserver): void;
56
>
57
>
// #region These members have a standard implementation and are only part of the interface for convenience.
58
>
59
>
/**
60
>
* Reads the current value and subscribes the reader to this observable.
61
>
*
62
>
* Calls {@link IReader.readObservable} if a reader is given, otherwise {@link IObservable.get}
63
>
* (see {@link ConvenientObservable.read} for the implementation).
64
>
*/
65
>
read(reader: IReader | undefined): T;
66
>
67
>
/**
68
>
* Makes sure this value is computed eagerly.
69
>
*/
70
>
recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
71
>
72
>
/**
73
>
* Makes sure this value is cached.
74
>
*/
75
>
keepObserved(store: DisposableStore): IObservable<T>;
76
>
77
>
/**
78
>
* Creates a derived observable that depends on this observable.
79
>
* Use the reader to read other observables
80
>
* (see {@link ConvenientObservable.map} for the implementation).
81
>
*/
82
>
map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
83
>
map<TNew>(owner: object, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
84
>
85
>
flatten<TNew>(this: IObservable<IObservable<TNew>>): IObservable<TNew>;
86
>
87
>
/**
88
>
* ONLY FOR DEBUGGING!
89
>
* Logs computations of this derived.
90
>
*/
91
>
log(): IObservableWithChange<T, TChange>;
92
>
93
>
/**
94
>
* A human-readable name for debugging purposes.
95
>
*/
96
>
readonly debugName: string;
97
>
98
>
/**
99
>
* This property captures the type of the change object. Do not use it at runtime!
100
>
*/
101
>
readonly TChange: TChange;
102
>
103
>
// #endregion
104
>
}
105
>
106
>
/**
107
>
* Represents an observer that can be subscribed to an observable.
108
>
*
109
>
* If an observer is subscribed to an observable and that observable didn't signal
110
>
* a change through one of the observer methods, the observer can assume that the
111
>
* observable didn't change.
112
>
* If an observable reported a possible change, {@link IObservable.reportChanges} forces
113
>
* the observable to report an actual change if there was one.
114
>
*/
115
>
export interface IObserver {
116
>
/**
117
>
* Signals that the given observable might have changed and a transaction potentially modifying that observable started.
118
>
* Before the given observable can call this method again, is must call {@link IObserver.endUpdate}.
119
>
*
120
>
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
121
>
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
122
>
*/
123
>
beginUpdate<T>(observable: IObservable<T>): void;
124
>
125
>
/**
126
>
* Signals that the transaction that potentially modified the given observable ended.
127
>
* This is a good place to react to (potential) changes.
128
>
*/
129
>
endUpdate<T>(observable: IObservable<T>): void;
130
>
131
>
/**
132
>
* Signals that the given observable might have changed.
133
>
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
134
>
*
135
>
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
136
>
* The change should be processed lazily or in {@link IObserver.endUpdate}.
137
>
*/
138
>
handlePossibleChange<T>(observable: IObservable<T>): void;
139
>
140
>
/**
141
>
* Signals that the given {@link observable} changed.
142
>
*
143
>
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
144
>
* The change should be processed lazily or in {@link IObserver.endUpdate}.
145
>
*
146
>
* @param change Indicates how or why the value changed.
147
>
*/
148
>
handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void;
149
>
}
150
>
151
>
/**
152
>
* A reader allows code to track what it depends on, so the caller knows when the computed value or produced side-effect is no longer valid.
153
>
* Use `derived(reader => ...)` to turn code that needs a reader into an observable value.
154
>
*/
155
>
export interface IReader {
156
>
/**
157
>
* Reads the value of an observable and subscribes to it.
158
>
*/
159
>
readObservable<T>(observable: IObservableWithChange<T, any>): T;
160
>
}
161
>
162
>
export interface ISettable<T, TChange = void> {
163
>
/**
164
>
* Sets the value of the observable.
165
>
* Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction).
166
>
*
167
>
* @param transaction When given, value changes are handled on demand or when the transaction ends.
168
>
* @param change Describes how or why the value changed.
169
>
*/
170
>
set(value: T, transaction: ITransaction | undefined, change: TChange): void;
171
>
}
172
>
173
>
export interface ITransaction {
174
>
/**
175
>
* Calls {@link Observer.beginUpdate} immediately
176
>
* and {@link Observer.endUpdate} when the transaction ends.
177
>
*/
178
>
updateObserver(observer: IObserver, observable: IObservableWithChange<any, any>): void;
179
>
}
180
>
181
>
/**
182
>
* This function is used to indicate that the caller recovered from an error that indicates a bug.
183
>
*/
184
>
export function handleBugIndicatingErrorRecovery(message: string) {
185
const err = new Error('BugIndicatingErrorRecovery: ' + message);
186
onUnexpectedError(err);
187
console.error('recovered from an error that indicates a bug', err);
188
}
190
>
/**
191
>
* A settable observable.
192
>
*/
193
>
export interface ISettableObservable<T, TChange = void> extends IObservableWithChange<T, TChange>, ISettable<T, TChange> {
194
>
}
195
>
196
>
export interface IReaderWithStore extends IReader {
197
>
/**
198
>
* Items in this store get disposed just before the observable recomputes/reruns or when it becomes unobserved.
199
>
*/
200
>
get store(): DisposableStore;
201
>
202
>
/**
203
>
* Items in this store get disposed just after the observable recomputes/reruns or when it becomes unobserved.
204
>
* This is important if the current run needs the undisposed result from the last run.
205
>
*
206
>
* Warning: Items in this store might still get disposed before dependents (that read the now disposed value in the past) are recomputed with the new (undisposed) value!
207
>
* A clean solution for this is ref counting.
208
>
*/
209
>
get delayedStore(): DisposableStore;
210
>
}