devToolsLogger.ts ×24

Frontier kind: Code frontier

unlabeled · c_812323fb97f0

3467 tests · 6426 LOC · 59 files · introduces 0 tests · 1971 LOC · 36 files

Introduces — evidence that enters the hierarchy at this concept

Code
312 ranges1971 lines · 36 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
925 ranges6426 lines · 59 files · Browse complete extent
All tests (intent)
3467 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

Showing the top 20 of 36 files by introduced lines: 1613 of 1971 introduced LOC and 236 of 312 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/base/common/observableInternal/base.ts 206 introduced LOC · 2 ranges

Open complete file

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 }
189 > base.ts
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 > }
src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts 204 introduced LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- devToolsLogger.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 { AutorunObserver, AutorunState } from '../../reactions/autorunImpl.js';
7 > import { TransactionImpl } from '../../transaction.js';
8 > import { IChangeInformation, IObservableLogger } from '../logging.js';
9 > import { formatValue } from '../consoleObservableLogger.js';
10 > import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransactionState, ObserverInstanceState } from './debuggerApi.js';
11 > import { registerDebugChannel } from './debuggerRpc.js';
12 > import { deepAssign, deepAssignDeleteNulls, Throttler } from './utils.js';
13 > import { isDefined } from '../../../types.js';
14 > import { FromEventObservable } from '../../observables/observableFromEvent.js';
15 > import { BugIndicatingError, onUnexpectedError } from '../../../errors.js';
16 > import { IObservable, IObserver } from '../../base.js';
17 > import { BaseObservable } from '../../observables/baseObservable.js';
18 > import { Derived, DerivedState } from '../../observables/derivedImpl.js';
19 > import { ObservableValue } from '../../observables/observableValue.js';
20 > import { DebugLocation } from '../../debugLocation.js';
21 >
22 > interface IInstanceInfo {
23 > declarationId: number;
24 > instanceId: number;
25 > }
26 >
27 > interface IObservableInfo extends IInstanceInfo {
28 > listenerCount: number;
29 > lastValue: string | undefined;
30 > updateCount: number;
31 > changedObservables: Set<IObservable<any>>;
32 > }
33 >
34 > interface IAutorunInfo extends IInstanceInfo {
35 > updateCount: number;
36 > changedObservables: Set<IObservable<any>>;
37 > }
38 >
39 > export class DevToolsLogger implements IObservableLogger {
40 > private static _instance: DevToolsLogger | undefined = undefined;
41 > public static getInstance(): DevToolsLogger {
42 if (DevToolsLogger._instance === undefined) {
43 DevToolsLogger._instance = new DevToolsLogger();
45 return DevToolsLogger._instance;
46 }
48 > private _declarationId = 0;
49 > private _instanceId = 0;
50 >
51 > private readonly _declarations = new Map</* declarationId + type */string, IObsDeclaration>();
52 > private readonly _instanceInfos = new WeakMap<object, IObservableInfo | IAutorunInfo>();
53 > private readonly _aliveInstances = new Map<ObsInstanceId, IObservable<any> | AutorunObserver>();
54 > private readonly _activeTransactions = new Set<TransactionImpl>();
55 >
56 > private readonly _channel = registerDebugChannel<ObsDebuggerApi>('observableDevTools', () => {
57 > return {
58 > notifications: {
59 > setDeclarationIdFilter: declarationIds => {
60 >
61 > },
62 > logObservableValue: (observableId) => {
63 > console.log('logObservableValue', observableId);
64 > },
65 > flushUpdates: () => {
66 > this._flushUpdates();
67 > },
68 > resetUpdates: () => {
69 > this._pendingChanges = null;
70 > this._channel.api.notifications.handleChange(this._fullState, true);
71 > },
72 > },
73 > requests: {
74 > getDeclarations: () => {
75 > const result: Record<string, IObsDeclaration> = {};
76 > for (const decl of this._declarations.values()) {
77 > result[decl.id] = decl;
78 > }
79 > return { decls: result };
80 > },
81 > getSummarizedInstances: () => {
82 > return null!;
83 > },
84 > getObservableValueInfo: instanceId => {
85 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
86 > return {
87 > observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
88 > };
89 > },
90 > getDerivedInfo: instanceId => {
91 > const d = this._aliveInstances.get(instanceId) as Derived<any>;
92 > return {
93 > dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
94 > observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
95 > };
96 > },
97 > getAutorunInfo: instanceId => {
98 > const obs = this._aliveInstances.get(instanceId) as AutorunObserver;
99 > return {
100 > dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
101 > };
102 > },
103 > getTransactionState: () => {
104 > return this.getTransactionState();
105 > },
106 > setValue: (instanceId, jsonValue) => {
107 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
108 >
109 > if (obs instanceof Derived) {
110 > obs.debugSetValue(jsonValue);
111 > } else if (obs instanceof ObservableValue) {
112 > obs.debugSetValue(jsonValue);
113 > } else if (obs instanceof FromEventObservable) {
114 > obs.debugSetValue(jsonValue);
115 > } else {
116 > throw new BugIndicatingError('Observable is not supported');
117 > }
118 >
119 > const observers = [...obs.debugGetObservers()];
120 > for (const d of observers) {
121 > d.beginUpdate(obs);
122 > }
123 > for (const d of observers) {
124 > d.handleChange(obs, undefined);
125 > }
126 > for (const d of observers) {
127 > d.endUpdate(obs);
128 > }
129 > },
130 > getValue: instanceId => {
131 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
132 > if (obs instanceof Derived) {
133 > return formatValue(obs.debugGetState().value, 200);
134 > } else if (obs instanceof ObservableValue) {
135 > return formatValue(obs.debugGetState().value, 200);
136 > }
137 >
138 > return undefined;
139 > },
140 > logValue: (instanceId) => {
141 > const obs = this._aliveInstances.get(instanceId);
142 > if (obs && 'get' in obs) {
143 > console.log('Logged Value:', obs.get());
144 > } else {
145 > throw new BugIndicatingError('Observable is not supported');
146 > }
147 > },
148 > rerun: (instanceId) => {
149 > const obs = this._aliveInstances.get(instanceId);
150 > if (obs instanceof Derived) {
151 > obs.debugRecompute();
152 > } else if (obs instanceof AutorunObserver) {
153 > obs.debugRerun();
154 > } else {
155 > throw new BugIndicatingError('Observable is not supported');
156 > }
157 > },
158 > }
159 > };
160 > });
161 >
162 > private getTransactionState(): ITransactionState | undefined {
163 const affected: ObserverInstanceState[] = [];
164 const txs = [...this._activeTransactions];
188 return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected };
189 }
191 > private _getObservableInfo(observable: IObservable<any>): IObservableInfo | undefined {
192 const info = this._instanceInfos.get(observable);
193 if (!info) {
197 return info as IObservableInfo;
198 }
200 > private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo | undefined {
201 const info = this._instanceInfos.get(autorun);
202 if (!info) {
206 return info as IAutorunInfo;
207 }
209 > private _getInfo(observer: IObserver, queue: (observer: IObserver) => void): ObserverInstanceState | undefined {
210 if (observer instanceof Derived) {
211 const observersToUpdate = [...observer.debugGetObservers()];
255 return undefined;
256 }
258 > private _formatObservable(obs: IObservable<any>): { name: string; instanceId: ObsInstanceId } | undefined {
259 const info = this._getObservableInfo(obs);
260 if (!info) { return undefined; }
261 return { name: obs.debugName, instanceId: info.instanceId };
262 }
264 > private _formatObserver(obs: IObserver): { name: string; instanceId: ObsInstanceId } | undefined {
265 if (obs instanceof Derived) {
266 return { name: obs.toString(), instanceId: this._getObservableInfo(obs)?.instanceId! };
273 return undefined;
274 }
276 > private constructor() {
277 DebugLocation.enable();
278 }
280 > private _pendingChanges: ObsStateUpdate | null = null;
281 > private readonly _changeThrottler = new Throttler();
282 >
283 > private readonly _fullState = {};
284 >
285 > private _handleChange(update: ObsStateUpdate): void {
286 deepAssignDeleteNulls(this._fullState, update);
287
294 this._changeThrottler.throttle(this._flushUpdates, 10);
295 }
297 > private readonly _flushUpdates = () => {
298 > if (this._pendingChanges !== null) {
299 > this._channel.api.notifications.handleChange(this._pendingChanges, false);
300 > this._pendingChanges = null;
301 > }
302 > };
303 >
304 > private _getDeclarationId(type: IObsDeclaration['type'], location: DebugLocation): number {
305 if (!location) {
306 return -1;
322 return decInfo.id;
323 }
325 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
326 const declarationId = this._getDeclarationId('observable/value', location);
327
336 this._instanceInfos.set(observable, info);
337 }
339 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void {
340 const info = this._getObservableInfo(observable);
341 if (!info) { return; }
364 info.listenerCount = newCount;
365 }
367 > handleObservableUpdated(observable: IObservable<any>, changeInfo: IChangeInformation): void {
368 if (observable instanceof Derived) {
369 this._handleDerivedRecomputed(observable, changeInfo);
383 }
384 }
386 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void {
387 const declarationId = this._getDeclarationId('autorun', location);
388 const info: IAutorunInfo = {
408 }
409 }
410 > handleAutorunDisposed(autorun: AutorunObserver): void { devToolsLogger.ts
411 const info = this._getAutorunInfo(autorun);
412 if (!info) { return; }
418 this._aliveInstances.delete(info.instanceId);
419 }
420 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { devToolsLogger.ts
421 const info = this._getAutorunInfo(autorun);
422 if (!info) { return; }
424 info.changedObservables.add(observable);
425 }
426 > handleAutorunStarted(autorun: AutorunObserver): void { devToolsLogger.ts
427
428 }
429 > handleAutorunFinished(autorun: AutorunObserver): void { devToolsLogger.ts
430 const info = this._getAutorunInfo(autorun);
431 if (!info) { return; }
437 });
438 }
440 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
441 const info = this._getObservableInfo(derived);
442 if (info) {
444 }
445 }
446 > _handleDerivedRecomputed(observable: Derived<any>, changeInfo: IChangeInformation): void { devToolsLogger.ts
447 const info = this._getObservableInfo(observable);
448 if (!info) { return; }
459 }
460 }
461 > handleDerivedCleared(observable: Derived<any>): void { devToolsLogger.ts
462 const info = this._getObservableInfo(observable);
463 if (!info) { return; }
475 }
476 }
477 > handleBeginTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
478 this._activeTransactions.add(transaction);
479 }
480 > handleEndTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
481 this._activeTransactions.delete(transaction);
482 }
src/vs/base/common/observableInternal/observables/derivedImpl.ts 112 introduced LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derivedImpl.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 { IObservable, IObservableWithChange, IObserver, IReaderWithStore, ISettableObservable, ITransaction, } from '../base.js';
7 > import { BaseObservable } from './baseObservable.js';
8 > import { DebugNameData } from '../debugName.js';
9 > import { BugIndicatingError, DisposableStore, EqualityComparer, assertFn, onBugIndicatingError } from '../commonFacade/deps.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { IChangeTracker } from '../changeTracker.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > export interface IDerivedReader<TChange = void> extends IReaderWithStore {
15 > /**
16 > * Call this to report a change delta or to force report a change, even if the new value is the same as the old value.
17 > */
18 > reportChange(change: TChange): void;
19 > }
20 >
21 > export const enum DerivedState {
22 > /** Initial state, no previous value, recomputation needed */
23 > initial = 0,
24 >
25 > /**
26 > * A dependency could have changed.
27 > * We need to explicitly ask them if at least one dependency changed.
28 > */
29 > dependenciesMightHaveChanged = 1,
30 >
31 > /**
32 > * A dependency changed and we need to recompute.
33 > * After recomputation, we need to check the previous value to see if we changed as well.
34 > */
35 > stale = 2,
36 >
37 > /**
38 > * No change reported, our cached value is up to date.
39 > */
40 > upToDate = 3,
41 > }
42 >
43 function derivedStateToString(state: DerivedState): string {
44 switch (state) {
50 }
51 }
53 > export class Derived<T, TChangeSummary = any, TChange = void> extends BaseObservable<T, TChange> implements IDerivedReader<TChange>, IObserver {
54 > private _state = DerivedState.initial;
55 > private _value: T | undefined = undefined;
56 > private _updateCount = 0;
57 > private _dependencies = new Set<IObservable<any>>();
58 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
59 > private _changeSummary: TChangeSummary | undefined = undefined;
60 > private _isUpdating = false;
61 > private _isComputing = false;
62 > private _didReportChange = false;
63 > private _isInBeforeUpdate = false;
64 > private _isReaderValid = false;
65 > private _store: DisposableStore | undefined = undefined;
66 > private _delayedStore: DisposableStore | undefined = undefined;
67 > private _removedObserverToCallEndUpdateOn: Set<IObserver> | null = null;
68 >
69 > public override get debugName(): string {
70 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
71 > }
72 >
73 > constructor(
74 public readonly _debugNameData: DebugNameData,
75 public readonly _computeFn: (reader: IDerivedReader<TChange>, changeSummary: TChangeSummary) => T,
82 this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
83 }
85 > protected override onLastObserverRemoved(): void {
86 /**
87 * We are not tracking changes anymore, thus we have to assume
107 this._handleLastObserverRemoved?.();
108 }
110 > public override get(): T {
111 const checkEnabled = false; // TODO set to true
112 if (this._isComputing && checkEnabled) {
164 }
165 }
167 > private _recompute() {
168 let didChange = false;
169 this._isComputing = true;
238 }
239 }
241 > public override toString(): string {
242 return `LazyDerived<${this.debugName}>`;
243 }
245 > // IObserver Implementation
246 >
247 > public beginUpdate<T>(_observable: IObservable<T>): void {
248 if (this._isUpdating) {
249 throw new BugIndicatingError('Cyclic deriveds are not supported yet!');
272 }
273 }
275 > public endUpdate<T>(_observable: IObservable<T>): void {
276 this._updateCount--;
277 if (this._updateCount === 0) {
291 assertFn(() => this._updateCount >= 0);
292 }
294 > public handlePossibleChange<T>(observable: IObservable<T>): void {
295 // In all other states, observers already know that we might have changed.
296 if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
301 }
302 }
304 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
305 if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable) || this._isInBeforeUpdate) {
306 getLogger()?.handleDerivedDependencyChanged(this, observable, change);
329 }
330 }
332 > // IReader Implementation
333 >
334 > private _ensureReaderValid(): void {
335 if (!this._isReaderValid) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
336 }
338 > public readObservable<T>(observable: IObservable<T>): T {
339 this._ensureReaderValid();
340
348 return value;
349 }
351 > public reportChange(change: TChange): void {
352 this._ensureReaderValid();
353
358 }
359 }
361 > get store(): DisposableStore {
362 this._ensureReaderValid();
363
367 return this._store;
368 }
370 > get delayedStore(): DisposableStore {
371 this._ensureReaderValid();
372
376 return this._delayedStore;
377 }
379 > public override addObserver(observer: IObserver): void {
380 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCount > 0;
381 super.addObserver(observer);
387 }
388 }
390 > public override removeObserver(observer: IObserver): void {
391 if (this._observers.has(observer) && this._updateCount > 0) {
392 if (!this._removedObserverToCallEndUpdateOn) {
397 super.removeObserver(observer);
398 }
400 > public debugGetState() {
401 return {
402 state: this._state,
408 };
409 }
411 > public debugSetValue(newValue: unknown) {
412 // eslint-disable-next-line local/code-no-any-casts
413 this._value = newValue as any;
414 }
416 > public debugRecompute(): void {
417 this.beginUpdate(this);
418 try {
426 }
427 }
429 > public setValue(newValue: T, tx: ITransaction, change: TChange): void {
430 this._value = newValue;
431 const observers = this._observers;
435 }
436 }
437 > } derivedImpl.ts
438 >
439 >
440 > export class DerivedWithSetter<T, TChangeSummary = any, TOutChanges = any> extends Derived<T, TChangeSummary, TOutChanges> implements ISettableObservable<T, TOutChanges> {
441 > constructor(
442 debugNameData: DebugNameData,
443 computeFn: (reader: IDerivedReader<TOutChanges>, changeSummary: TChangeSummary) => T,
src/vs/base/common/observableInternal/observables/baseObservable.ts 104 introduced LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- baseObservable.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 { IObservableWithChange, IObserver, IReader, IObservable } from '../base.js';
7 > import { DisposableStore } from '../commonFacade/deps.js';
8 > import { DebugLocation } from '../debugLocation.js';
9 > import { DebugOwner, getFunctionName } from '../debugName.js';
10 > import { debugGetObservableGraph } from '../logging/debugGetDependencyGraph.js';
11 > import { getLogger, logObservable } from '../logging/logging.js';
12 > import type { keepObserved, recomputeInitiallyAndOnChange } from '../utils/utils.js';
13 > import { derivedOpts } from './derived.js';
14 >
15 > let _derived: typeof derivedOpts;
16 > /**
17 > * @internal
18 > * This is to allow splitting files.
19 > */
20 > export function _setDerivedOpts(derived: typeof _derived) {
21 > _derived = derived;
22 > }
23 >
24 > let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
25 > export function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange: typeof _recomputeInitiallyAndOnChange) {
26 > _recomputeInitiallyAndOnChange = recomputeInitiallyAndOnChange;
27 > }
28 >
29 > let _keepObserved: typeof keepObserved;
30 > export function _setKeepObserved(keepObserved: typeof _keepObserved) {
31 > _keepObserved = keepObserved;
32 > }
33 >
34 > let _debugGetObservableGraph: typeof debugGetObservableGraph;
35 > export function _setDebugGetObservableGraph(debugGetObservableGraph: typeof _debugGetObservableGraph) {
36 > _debugGetObservableGraph = debugGetObservableGraph;
37 > }
38 >
39 > export abstract class ConvenientObservable<T, TChange> implements IObservableWithChange<T, TChange> {
40 > get TChange(): TChange { return null!; }
41 >
42 > public abstract get(): T;
43 >
44 > public reportChanges(): void {
45 this.get();
46 }
48 > public abstract addObserver(observer: IObserver): void;
49 > public abstract removeObserver(observer: IObserver): void;
50 >
51 > /** @sealed */
52 > public read(reader: IReader | undefined): T {
53 if (reader) {
54 return reader.readObservable(this);
57 }
58 }
60 > /** @sealed */
61 > public map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
62 > public map<TNew>(owner: DebugOwner, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
63 > public map<TNew>(fnOrOwner: DebugOwner | ((value: T, reader: IReader) => TNew), fnOrUndefined?: (value: T, reader: IReader) => TNew, debugLocation: DebugLocation = DebugLocation.ofCaller()): IObservable<TNew> {
64 const owner = fnOrUndefined === undefined ? undefined : fnOrOwner as DebugOwner;
65 const fn = fnOrUndefined === undefined ? fnOrOwner as (value: T, reader: IReader) => TNew : fnOrUndefined;
91 );
92 }
94 > public abstract log(): IObservableWithChange<T, TChange>;
95 >
96 > /**
97 > * @sealed
98 > * Converts an observable of an observable value into a direct observable of the value.
99 > */
100 > public flatten<TNew>(this: IObservable<IObservableWithChange<TNew, any>>): IObservable<TNew> {
101 return _derived(
102 {
107 );
108 }
110 > public recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T> {
111 store.add(_recomputeInitiallyAndOnChange!(this, handleValue));
112 return this;
113 }
115 > /**
116 > * Ensures that this observable is observed. This keeps the cache alive.
117 > * However, in case of deriveds, it does not force eager evaluation (only when the value is read/get).
118 > * Use `recomputeInitiallyAndOnChange` for eager evaluation.
119 > */
120 > public keepObserved(store: DisposableStore): IObservable<T> {
121 store.add(_keepObserved!(this));
122 return this;
123 }
125 > public abstract get debugName(): string;
126 >
127 > protected get debugValue() {
128 return this.get();
129 }
131 > get debug(): DebugHelper {
132 return new DebugHelper(this);
133 }
135 >
136 > class DebugHelper {
137 > constructor(public readonly observable: IObservableWithChange<any, any>) {
138 }
140 > getDependencyGraph(): string {
141 return _debugGetObservableGraph(this.observable, { type: 'dependencies' });
142 }
144 > getObserverGraph(): string {
145 return _debugGetObservableGraph(this.observable, { type: 'observers' });
146 }
148 >
149 > export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
150 > protected readonly _observers = new Set<IObserver>();
151 >
152 > constructor(debugLocation: DebugLocation) {
153 super();
154 getLogger()?.handleObservableCreated(this, debugLocation);
155 }
157 > public addObserver(observer: IObserver): void {
158 const len = this._observers.size;
159 this._observers.add(observer);
165 }
166 }
168 > public removeObserver(observer: IObserver): void {
169 const deleted = this._observers.delete(observer);
170 if (deleted && this._observers.size === 0) {
175 }
176 }
178 > protected onFirstObserverAdded(): void { }
179 > protected onLastObserverRemoved(): void { }
180 >
181 > public override log(): IObservableWithChange<T, TChange> {
182 const hadLogger = !!getLogger();
183 logObservable(this);
src/vs/base/common/observableInternal/utils/promise.ts 99 introduced LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- promise.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 > import { DisposableStore } from '../../lifecycle.js';
6 > import { IObservable, ISettableObservable } from '../base.js';
7 > import { autorun } from '../reactions/autorun.js';
8 > import { transaction } from '../transaction.js';
9 > import { derived } from '../observables/derived.js';
10 > import { observableValue } from '../observables/observableValue.js';
11 >
12 > export class ObservableLazy<T> {
13 > private readonly _value = observableValue<T | undefined>(this, undefined);
14 >
15 > /**
16 > * The cached value.
17 > * Does not force a computation of the value.
18 > */
19 > public get cachedValue(): IObservable<T | undefined> { return this._value; }
20 >
21 > constructor(private readonly _computeValue: () => T) {
22 }
23 > promise.ts
24 > /**
25 > * Returns the cached value.
26 > * Computes the value if the value has not been cached yet.
27 > */
28 > public getValue(): T {
29 let v = this._value.get();
30 if (!v) {
34 return v;
35 }
36 > } promise.ts
37 >
38 > /**
39 > * A promise whose state is observable.
40 > */
41 > export class ObservablePromise<T> {
42 > public static fromFn<T>(fn: () => Promise<T>): ObservablePromise<T> {
43 > return new ObservablePromise(fn());
44 > }
45 >
46 > public static resolved<T>(value: T): ObservablePromise<T> {
47 return new ObservablePromise(Promise.resolve(value));
48 }
49 > promise.ts
50 > private readonly _value = observableValue<PromiseResult<T> | undefined>(this, undefined);
51 >
52 > /**
53 > * The promise that this object wraps.
54 > */
55 > public readonly promise: Promise<T>;
56 >
57 > /**
58 > * The current state of the promise.
59 > * Is `undefined` if the promise didn't resolve yet.
60 > */
61 > public readonly promiseResult: IObservable<PromiseResult<T> | undefined> = this._value;
62 >
63 > constructor(promise: Promise<T>) {
64 this.promise = promise.then(value => {
65 transaction(tx => {
76 });
77 }
78 > promise.ts
79 > public readonly resolvedValue = derived(this, reader => {
80 const result = this.promiseResult.read(reader);
81 if (!result) {
83 }
84 return result.getDataOrThrow();
85 > }); promise.ts
86 > }
87 >
88 > export class PromiseResult<T> {
89 > constructor(
90 /**
91 * The value of the resolved promise.
101 ) {
102 }
103 > promise.ts
104 > /**
105 > * Returns the value if the promise resolved, otherwise throws the error.
106 > */
107 > public getDataOrThrow(): T {
108 if (this.error) {
109 throw this.error;
111 return this.data!;
112 }
113 > } promise.ts
114 >
115 > /**
116 > * Tracks a changing {@link ObservablePromise}, exposing the last resolved value
117 > * and whether a newer promise is still pending.
118 > */
119 > export class ObservableResolvedPromise<T> {
120 > private readonly _lastResolved: ISettableObservable<T>;
121 > public readonly lastResolved: IObservable<T>;
122 >
123 > private readonly _isResolving = observableValue<boolean>(this, false);
124 > public readonly isResolving: IObservable<boolean> = this._isResolving;
125 >
126 > private _runningPromise: ObservablePromise<T> | undefined;
127 >
128 > constructor(
129 source: IObservable<ObservablePromise<T>>,
130 initialValue: T,
149 }));
150 }
151 > } promise.ts
152 >
153 > /**
154 > * A lazy promise whose state is observable.
155 > */
156 > export class ObservableLazyPromise<T> {
157 > private readonly _lazyValue = new ObservableLazy(() => new ObservablePromise(this._computePromise()));
158 >
159 > /**
160 > * Does not enforce evaluation of the promise compute function.
161 > * Is undefined if the promise has not been computed yet.
162 > */
163 > public readonly cachedPromiseResult = derived(this, reader => this._lazyValue.cachedValue.read(reader)?.promiseResult.read(reader));
164 >
165 > constructor(private readonly _computePromise: () => Promise<T>) {
166 }
167 > promise.ts
168 > public getPromise(): Promise<T> {
169 return this._lazyValue.getValue().promise;
170 }
171 > } promise.ts
src/vs/base/common/observableInternal/reactions/autorun.ts 91 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorun.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 { IReaderWithStore, IReader, IObservable, ISettableObservable } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, IDisposable, toDisposable } from '../commonFacade/deps.js';
9 > import { DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { AutorunObserver } from './autorunImpl.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 > import { observableValue } from '../observables/observableValue.js';
13 > import { transaction } from '../transaction.js';
14 >
15 > /**
16 > * Runs immediately and whenever a transaction ends and an observed observable changed.
17 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
18 > */
19 > export function autorun(fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
20 return new AutorunObserver(
21 new DebugNameData(undefined, undefined, fn),
25 );
26 }
27 > autorun.ts
28 > /**
29 > * Runs immediately and whenever a transaction ends and an observed observable changed.
30 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
31 > */
32 > export function autorunOpts(options: IDebugNameData & {}, fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
33 return new AutorunObserver(
34 new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? fn),
38 );
39 }
40 > autorun.ts
41 > /**
42 > * Runs immediately and whenever a transaction ends and an observed observable changed.
43 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
44 > *
45 > * Use `changeTracker.createChangeSummary` to create a "change summary" that can collect the changes.
46 > * Use `changeTracker.handleChange` to add a reported change to the change summary.
47 > * The run function is given the last change summary.
48 > * The change summary is discarded after the run function was called.
49 > *
50 > * @see autorun
51 > */
52 > export function autorunHandleChanges<TChangeSummary>(
53 options: IDebugNameData & {
54 changeTracker: IChangeTracker<TChangeSummary>;
64 );
65 }
66 > autorun.ts
67 > /**
68 > * @see autorunHandleChanges (but with a disposable store that is cleared before the next run or on dispose)
69 > */
70 > export function autorunWithStoreHandleChanges<TChangeSummary>(
71 options: IDebugNameData & {
72 changeTracker: IChangeTracker<TChangeSummary>;
92 });
93 }
94 > autorun.ts
95 > /**
96 > * @see autorun (but with a disposable store that is cleared before the next run or on dispose)
97 > *
98 > * @deprecated Use `autorun(reader => { reader.store.add(...) })` instead!
99 > */
100 > export function autorunWithStore(fn: (reader: IReader, store: DisposableStore) => void): IDisposable {
101 const store = new DisposableStore();
102 const disposable = autorunOpts(
116 });
117 }
118 > autorun.ts
119 > export function autorunDelta<T>(
120 observable: IObservable<T>,
121 handler: (args: { lastValue: T | undefined; newValue: T }) => void
129 });
130 }
131 > autorun.ts
132 > export function autorunIterableDelta<T>(
133 getValue: (reader: IReader) => Iterable<T>,
134 handler: (args: { addedValues: T[]; removedValues: T[] }) => void,
157 });
158 }
159 > autorun.ts
160 > /**
161 > * For each key-stable item in {@link items}, runs {@link setup} once when the
162 > * key is first observed and disposes the per-key {@link DisposableStore} when
163 > * the key is no longer present in the array (or when the returned disposable
164 > * is disposed).
165 > *
166 > * The {@link IObservable} handed to {@link setup} fires whenever the array
167 > * still contains an item with the same key but the item value itself has
168 > * changed (e.g. because the upstream state is immutable and produced a new
169 > * object with the same id). All per-key value updates triggered by a single
170 > * change to {@link items} are batched into one transaction, so dependent
171 > * autoruns observe a consistent snapshot.
172 > *
173 > * Per-key state should be stored in closures or in disposables registered
174 > * against the per-key {@link DisposableStore}. {@link setup} should not call
175 > * `.read()` on the outer {@link items} observable from its body (use the
176 > * provided per-key value observable, or create inner autoruns).
177 > */
178 > export function autorunPerKeyedItem<TIn, TKey>(
179 items: IObservable<readonly TIn[]>,
180 keyFn: (input: TIn) => TKey,
227 });
228 }
229 > autorun.ts
230 > export interface IReaderWithDispose extends IReaderWithStore, IDisposable { }
231 >
232 > /**
233 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
234 > * It it safe to call `dispose()` synchronously.
235 > * @deprecated Use autorunSelfDisposable2
236 > */
237 > export function autorunSelfDisposable(fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
238 let ar: IDisposable | undefined;
239 let disposed = false;
258 return ar;
259 }
260 > autorun.ts
261 >
262 > /**
263 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
264 > * It it safe to call `dispose()` synchronously.
265 > * TODO@hediet/copilot: rename to delete autorunSelfDisposable, and rename autorunSelfDisposable2 to autorunSelfDisposable.
266 > */
267 > export function registerAutorunSelfDisposable(store: DisposableStore, fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): void {
268 let ar: IDisposable | undefined;
269 let disposeSync = false;
src/vs/base/common/observableInternal/utils/utils.ts 89 introduced LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.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 { autorun } from '../reactions/autorun.js';
7 > import { IObservable, IObservableWithChange, IObserver, IReader, ITransaction } from '../base.js';
8 > import { observableValue } from '../observables/observableValue.js';
9 > import { DebugOwner } from '../debugName.js';
10 > import { DisposableStore, Event, IDisposable, toDisposable } from '../commonFacade/deps.js';
11 > import { derived, derivedOpts } from '../observables/derived.js';
12 > import { observableFromEvent } from '../observables/observableFromEvent.js';
13 > import { observableSignal } from '../observables/observableSignal.js';
14 > import { _setKeepObserved, _setRecomputeInitiallyAndOnChange } from '../observables/baseObservable.js';
15 > import { DebugLocation } from '../debugLocation.js';
16 >
17 > export function observableFromPromise<T>(promise: Promise<T>): IObservable<{ value?: T }> {
18 const observable = observableValue<{ value?: T }>('promiseValue', {});
19 promise.then((value) => {
22 return observable;
23 }
24 > utils.ts
25 > export function signalFromObservable<T>(owner: DebugOwner | undefined, observable: IObservable<T>): IObservable<void> {
26 return derivedOpts({
27 owner,
31 });
32 }
33 > utils.ts
34 > /**
35 > * Creates an observable that debounces the input observable.
36 > */
37 > export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number | ((lastValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
38 let hasValue = false;
39 let lastValue: T | undefined;
79 }, debugLocation);
80 }
81 > utils.ts
82 > /**
83 > * Creates an observable that throttles the input observable.
84 > * Unlike {@link debouncedObservable}, the timer starts on the first change
85 > * and is not reset by subsequent changes, preventing starvation.
86 > */
87 > export function throttledObservable<T>(observable: IObservable<T>, throttleMs: number, debugLocation = DebugLocation.ofCaller()): IObservable<T> {
88 let hasValue = false;
89 let lastValue: T | undefined;
126 }, debugLocation);
127 }
128 > utils.ts
129 > /**
130 > * Creates an observable that debounces the input observable.
131 > */
132 > export function debouncedObservable2<T>(observable: IObservable<T>, debounceMs: number | ((currentValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
133 const s = observableSignal('handleTimeout');
134
167 return d;
168 }
169 > utils.ts
170 > export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
171 const observable = observableValue('triggeredRecently', false);
172
186 return observable;
187 }
188 > utils.ts
189 > /**
190 > * This makes sure the observable is being observed and keeps its cache alive.
191 > */
192 > export function keepObserved<T>(observable: IObservable<T>): IDisposable {
193 const o = new KeepAliveObserver(false, undefined);
194 observable.addObserver(o);
197 });
198 }
199 > utils.ts
200 > _setKeepObserved(keepObserved);
201 >
202 > /**
203 > * This converts the given observable into an autorun.
204 > */
205 > export function recomputeInitiallyAndOnChange<T>(observable: IObservable<T>, handleValue?: (value: T) => void): IDisposable {
206 const o = new KeepAliveObserver(true, handleValue);
207 observable.addObserver(o);
216 });
217 }
218 > utils.ts
219 > _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
220 >
221 > export class KeepAliveObserver implements IObserver {
222 > private _counter = 0;
223 >
224 > constructor(
225 private readonly _forceRecompute: boolean,
226 private readonly _handleValue: ((value: any) => void) | undefined,
227 ) { }
228 > utils.ts
229 > beginUpdate<T>(observable: IObservable<T>): void {
230 this._counter++;
231 }
232 > utils.ts
233 > endUpdate<T>(observable: IObservable<T>): void {
234 if (this._counter === 1 && this._forceRecompute) {
235 if (this._handleValue) {
241 this._counter--;
242 }
243 > utils.ts
244 > handlePossibleChange<T>(observable: IObservable<T>): void {
245 // NO OP
246 }
247 > utils.ts
248 > handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
249 // NO OP
250 }
251 > } utils.ts
252 >
253 > export function derivedObservableWithCache<T>(owner: DebugOwner, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T> {
254 let lastValue: T | undefined = undefined;
255 const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
259 return observable;
260 }
261 > utils.ts
262 > export function derivedObservableWithWritableCache<T>(owner: object, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T>
263 & { clearCache(transaction: ITransaction): void; setCache(newValue: T | undefined, tx: ITransaction | undefined): void } {
264 let lastValue: T | undefined = undefined;
280 });
281 }
282 > utils.ts
283 > /**
284 > * When the items array changes, referential equal items are not mapped again.
285 > */
286 > 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[]> {
287 let m = new ArrayMap(map, keySelector);
288 const self = derivedOpts({
300 return self;
301 }
302 > utils.ts
303 > class ArrayMap<TIn, TOut, TKey> implements IDisposable {
304 > private readonly _cache = new Map<TKey, { out: TOut; store: DisposableStore }>();
305 > private _items: TOut[] = [];
306 > constructor(
307 private readonly _map: (input: TIn, store: DisposableStore) => TOut,
308 private readonly _keySelector?: (input: TIn) => TKey,
309 ) {
310 }
311 > utils.ts
312 > public dispose(): void {
313 this._cache.forEach(entry => entry.store.dispose());
314 this._cache.clear();
315 }
316 > utils.ts
317 > public setItems(items: readonly TIn[]): void {
318 const newItems: TOut[] = [];
319 const itemsToRemove = new Set(this._cache.keys());
342 this._items = newItems;
343 }
344 > utils.ts
345 > public getItems(): TOut[] {
346 return this._items;
347 }
348 > } utils.ts
349 >
350 > export function isObservable<T>(obj: unknown): obj is IObservable<T> {
351 return !!obj && (<IObservable<T>>obj).read !== undefined && (<IObservable<T>>obj).reportChanges !== undefined;
352 }
src/vs/base/common/observableInternal/reactions/autorunImpl.ts 78 introduced LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorunImpl.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 { IObservable, IObservableWithChange, IObserver, IReaderWithStore } from '../base.js';
7 > import { DebugNameData } from '../debugName.js';
8 > import { assertFn, BugIndicatingError, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, trackDisposable } from '../commonFacade/deps.js';
9 > import { getLogger } from '../logging/logging.js';
10 > import { IChangeTracker } from '../changeTracker.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export const enum AutorunState {
14 > /**
15 > * A dependency could have changed.
16 > * We need to explicitly ask them if at least one dependency changed.
17 > */
18 > dependenciesMightHaveChanged = 1,
19 >
20 > /**
21 > * A dependency changed and we need to recompute.
22 > */
23 > stale = 2,
24 > upToDate = 3,
25 > }
26 >
27 function autorunStateToString(state: AutorunState): string {
28 switch (state) {
33 }
34 }
36 > export class AutorunObserver<TChangeSummary = any> implements IObserver, IReaderWithStore, IDisposable {
37 > private _state = AutorunState.stale;
38 > private _updateCount = 0;
39 > private _disposed = false;
40 > private _dependencies = new Set<IObservable<any>>();
41 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
42 > private _changeSummary: TChangeSummary | undefined;
43 > private _isRunning = false;
44 > private _iteration = 0;
45 >
46 > public get debugName(): string {
47 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
48 > }
49 >
50 > constructor(
51 public readonly _debugNameData: DebugNameData,
52 public readonly _runFn: (reader: IReaderWithStore, changeSummary: TChangeSummary) => void,
60 trackDisposable(this);
61 }
63 > public dispose(): void {
64 if (this._disposed) {
65 return;
81 markAsDisposed(this);
82 }
84 > private _run() {
85 const emptySet = this._dependenciesToBeRemoved;
86 this._dependenciesToBeRemoved = this._dependencies;
130 }
131 }
133 > public toString(): string {
134 return `Autorun<${this.debugName}>`;
135 }
137 > // IObserver implementation
138 > public beginUpdate(_observable: IObservable<any>): void {
139 if (this._state === AutorunState.upToDate) {
140 this._checkIterations();
143 this._updateCount++;
144 }
146 > public endUpdate(_observable: IObservable<any>): void {
147 try {
148 if (this._updateCount === 1) {
175 assertFn(() => this._updateCount >= 0);
176 }
178 > public handlePossibleChange(observable: IObservable<any>): void {
179 if (this._state === AutorunState.upToDate && this._isDependency(observable)) {
180 this._checkIterations();
182 }
183 }
185 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
186 if (this._isDependency(observable)) {
187 getLogger()?.handleAutorunDependencyChanged(this, observable, change);
203 }
204 }
206 > private _isDependency(observable: IObservableWithChange<any, any>): boolean {
207 return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable);
208 }
210 > // IReader implementation
211 >
212 > private _ensureNoRunning(): void {
213 if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
214 }
216 > public readObservable<T>(observable: IObservable<T>): T {
217 this._ensureNoRunning();
218
228 return value;
229 }
231 > private _store: DisposableStore | undefined = undefined;
232 > get store(): DisposableStore {
233 this._ensureNoRunning();
234 if (this._disposed) {
241 return this._store;
242 }
244 > private _delayedStore: DisposableStore | undefined = undefined;
245 > get delayedStore(): DisposableStore {
246 this._ensureNoRunning();
247 if (this._disposed) {
254 return this._delayedStore;
255 }
257 > public debugGetState() {
258 return {
259 isRunning: this._isRunning,
264 };
265 }
267 > public debugRerun(): void {
268 if (!this._isRunning) {
269 this._run();
272 }
273 }
275 > private _checkIterations(): boolean {
276 if (this._iteration > 100) {
277 onBugIndicatingError(new BugIndicatingError(`Autorun '${this.debugName}' is stuck in an infinite update loop.`));
src/vs/base/common/observableInternal/logging/logging.ts 67 introduced LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- logging.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 { AutorunObserver } from '../reactions/autorunImpl.js';
7 > import { IObservable } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import type { Derived } from '../observables/derivedImpl.js';
10 > import { DebugLocation } from '../debugLocation.js';
11 >
12 > let globalObservableLogger: IObservableLogger | undefined;
13 >
14 > export function addLogger(logger: IObservableLogger): void {
15 if (!globalObservableLogger) {
16 globalObservableLogger = logger;
21 }
22 }
23 > logging.ts
24 > export function getLogger(): IObservableLogger | undefined {
25 return globalObservableLogger;
26 }
27 > logging.ts
28 > let globalObservableLoggerFn: ((obs: IObservable<any>) => void) | undefined = undefined;
29 > export function setLogObservableFn(fn: (obs: IObservable<any>) => void): void {
30 > globalObservableLoggerFn = fn;
31 > }
32 >
33 > export function logObservable(obs: IObservable<any>): void {
34 if (globalObservableLoggerFn) {
35 globalObservableLoggerFn(obs);
36 }
37 }
38 > logging.ts
39 > export interface IChangeInformation {
40 > oldValue: unknown;
41 > newValue: unknown;
42 > change: unknown;
43 > didChange: boolean;
44 > hadValue: boolean;
45 > }
46 >
47 > export interface IObservableLogger {
48 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void;
49 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void;
50 >
51 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void;
52 >
53 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void;
54 > handleAutorunDisposed(autorun: AutorunObserver): void;
55 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void;
56 > handleAutorunStarted(autorun: AutorunObserver): void;
57 > handleAutorunFinished(autorun: AutorunObserver): void;
58 >
59 > handleDerivedDependencyChanged(derived: Derived<any, any, any>, observable: IObservable<any>, change: unknown): void;
60 > handleDerivedCleared(observable: Derived<any, any, any>): void;
61 >
62 > handleBeginTransaction(transaction: TransactionImpl): void;
63 > handleEndTransaction(transaction: TransactionImpl): void;
64 > }
65 >
66 > class ComposedLogger implements IObservableLogger {
67 > constructor(
68 public readonly loggers: IObservableLogger[],
69 ) { }
70 > logging.ts
71 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
72 for (const logger of this.loggers) {
73 logger.handleObservableCreated(observable, location);
74 }
75 }
76 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void { logging.ts
77 for (const logger of this.loggers) {
78 logger.handleOnListenerCountChanged(observable, newCount);
79 }
80 }
81 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void { logging.ts
82 for (const logger of this.loggers) {
83 logger.handleObservableUpdated(observable, info);
84 }
85 }
86 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void { logging.ts
87 for (const logger of this.loggers) {
88 logger.handleAutorunCreated(autorun, location);
89 }
90 }
91 > handleAutorunDisposed(autorun: AutorunObserver): void { logging.ts
92 for (const logger of this.loggers) {
93 logger.handleAutorunDisposed(autorun);
94 }
95 }
96 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { logging.ts
97 for (const logger of this.loggers) {
98 logger.handleAutorunDependencyChanged(autorun, observable, change);
99 }
100 }
101 > handleAutorunStarted(autorun: AutorunObserver): void { logging.ts
102 for (const logger of this.loggers) {
103 logger.handleAutorunStarted(autorun);
104 }
105 }
106 > handleAutorunFinished(autorun: AutorunObserver): void { logging.ts
107 for (const logger of this.loggers) {
108 logger.handleAutorunFinished(autorun);
109 }
110 }
111 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void { logging.ts
112 for (const logger of this.loggers) {
113 logger.handleDerivedDependencyChanged(derived, observable, change);
114 }
115 }
116 > handleDerivedCleared(observable: Derived<any>): void { logging.ts
117 for (const logger of this.loggers) {
118 logger.handleDerivedCleared(observable);
119 }
120 }
121 > handleBeginTransaction(transaction: TransactionImpl): void { logging.ts
122 for (const logger of this.loggers) {
123 logger.handleBeginTransaction(transaction);
124 }
125 }
126 > handleEndTransaction(transaction: TransactionImpl): void { logging.ts
127 for (const logger of this.loggers) {
128 logger.handleEndTransaction(transaction);
129 }
130 }
131 > } logging.ts
src/vs/base/common/observableInternal/observables/observableValue.ts 58 introduced LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableValue.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 { ISettableObservable, ITransaction } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { BaseObservable } from './baseObservable.js';
9 > import { EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
10 > import { DebugNameData } from '../debugName.js';
11 > import { getLogger } from '../logging/logging.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Creates an observable value.
16 > * Observers get informed when the value changes.
17 > * @template TChange An arbitrary type to describe how or why the value changed. Defaults to `void`.
18 > * Observers will receive every single change value.
19 > */
20 >
21 > export function observableValue<T, TChange = void>(name: string, initialValue: T): ISettableObservable<T, TChange>;
22 > export function observableValue<T, TChange = void>(owner: object, initialValue: T): ISettableObservable<T, TChange>;
23 > export function observableValue<T, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> {
24 let debugNameData: DebugNameData;
25 if (typeof nameOrOwner === 'string') {
30 return new ObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
31 }
33 > export class ObservableValue<T, TChange = void>
34 > extends BaseObservable<T, TChange>
35 > implements ISettableObservable<T, TChange> {
36 > protected _value: T;
37 >
38 > get debugName() {
39 > return this._debugNameData.getDebugName(this) ?? 'ObservableValue';
40 > }
41 >
42 > constructor(
43 private readonly _debugNameData: DebugNameData,
44 initialValue: T,
51 getLogger()?.handleObservableUpdated(this, { hadValue: false, newValue: initialValue, change: undefined, didChange: true, oldValue: undefined });
52 }
53 > public override get(): T { observableValue.ts
54 return this._value;
55 }
57 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
58 if (change === undefined && this._equalityComparator(this._value, value)) {
59 return;
79 }
80 }
82 > override toString(): string {
83 return `${this.debugName}: ${this._value}`;
84 }
86 > protected _setValue(newValue: T): void {
87 this._value = newValue;
88 }
90 > public debugGetState() {
91 return {
92 value: this._value,
93 };
94 }
96 > public debugSetValue(value: unknown) {
97 this._value = value as T;
98 }
100 > /**
101 > * A disposable observable. When disposed, its value is also disposed.
102 > * When a new value is set, the previous value is disposed.
103 > */
104 >
105 > export function disposableObservableValue<T extends IDisposable | undefined, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> & IDisposable {
106 let debugNameData: DebugNameData;
107 if (typeof nameOrOwner === 'string') {
112 return new DisposableObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
113 }
115 > export class DisposableObservableValue<T extends IDisposable | undefined, TChange = void> extends ObservableValue<T, TChange> implements IDisposable {
116 > protected override _setValue(newValue: T): void {
117 if (this._value === newValue) {
118 return;
src/vs/base/common/observableInternal/index.ts 57 introduced LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- index.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 > // This is a facade for the observable implementation. Only import from here!
7 >
8 > export { observableValueOpts } from './observables/observableValueOpts.js';
9 > export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges, autorunIterableDelta, autorunPerKeyedItem, autorunSelfDisposable, registerAutorunSelfDisposable } from './reactions/autorun.js';
10 > export { type IObservable, type IObservableWithChange, type IObserver, type IReader, type ISettable, type IReaderWithStore, type ISettableObservable, type ITransaction } from './base.js';
11 > export { disposableObservableValue } from './observables/observableValue.js';
12 > export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './observables/derived.js';
13 > export { type IDerivedReader } from './observables/derivedImpl.js';
14 > export { ObservableLazy, ObservableLazyPromise, ObservablePromise, ObservableResolvedPromise, PromiseResult, } from './utils/promise.js';
15 > export { derivedWithCancellationToken, waitForState } from './utils/utilsCancellation.js';
16 > export {
17 > debouncedObservable, debouncedObservable2, derivedObservableWithCache,
18 > derivedObservableWithWritableCache, keepObserved, mapObservableArrayCached, observableFromPromise,
19 > recomputeInitiallyAndOnChange,
20 > signalFromObservable, throttledObservable, wasEventTriggeredRecently,
21 > isObservable,
22 > } from './utils/utils.js';
23 > export { type DebugOwner } from './debugName.js';
24 > export { type IChangeContext, type IChangeTracker, recordChanges, recordChangesLazy } from './changeTracker.js';
25 > export { constObservable } from './observables/constObservable.js';
26 > export { type IObservableSignal, observableSignal } from './observables/observableSignal.js';
27 > export { observableFromEventOpts } from './observables/observableFromEvent.js';
28 > export { observableSignalFromEvent } from './observables/observableSignalFromEvent.js';
29 > export { asyncTransaction, globalTransaction, subtransaction, transaction, TransactionImpl } from './transaction.js';
30 > export { observableFromValueWithChangeEvent, ValueWithChangeEventFromObservable } from './utils/valueWithChangeEvent.js';
31 > export { runOnChange, runOnChangeWithCancellationToken, runOnChangeWithStore, type RemoveUndefined } from './utils/runOnChange.js';
32 > export { derivedConstOnceDefined, latestChangedValue } from './experimental/utils.js';
33 > export { observableFromEvent } from './observables/observableFromEvent.js';
34 > export { observableValue } from './observables/observableValue.js';
35 >
36 > export { ObservableSet } from './set.js';
37 > export { ObservableMap } from './map.js';
38 > export { DebugLocation } from './debugLocation.js';
39 >
40 > import { addLogger, setLogObservableFn } from './logging/logging.js';
41 > import { ConsoleObservableLogger, logObservableToConsole } from './logging/consoleObservableLogger.js';
42 > import { DevToolsLogger } from './logging/debugger/devToolsLogger.js';
43 > import { env } from '../process.js';
44 > import { _setDebugGetObservableGraph } from './observables/baseObservable.js';
45 > import { debugGetObservableGraph } from './logging/debugGetDependencyGraph.js';
46 >
47 > _setDebugGetObservableGraph(debugGetObservableGraph);
48 > setLogObservableFn(logObservableToConsole);
49 >
50 > // Remove "//" in the next line to enable logging
51 > const enableLogging = false
52 > // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this
53 > ;
54 >
55 > if (enableLogging) {
56 addLogger(new ConsoleObservableLogger());
57 }
58 > index.ts
59 > if (env && env['VSCODE_DEV_DEBUG_OBSERVABLES']) {
60 // To debug observables you also need the extension "ms-vscode.debug-value-editor"
61 addLogger(DevToolsLogger.getInstance());
src/vs/base/common/observableInternal/observables/derived.ts 57 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derived.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 { IObservable, IReader, ITransaction, ISettableObservable, IObservableWithChange } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugLocation } from '../debugLocation.js';
10 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
11 > import { _setDerivedOpts } from './baseObservable.js';
12 > import { IDerivedReader, Derived, DerivedWithSetter } from './derivedImpl.js';
13 >
14 > /**
15 > * Creates an observable that is derived from other observables.
16 > * The value is only recomputed when absolutely needed.
17 > *
18 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
19 > */
20 > export function derived<T, TChange = void>(computeFn: (reader: IDerivedReader<TChange>, debugLocation?: DebugLocation) => T): IObservableWithChange<T, TChange>;
21 > export function derived<T, TChange = void>(owner: DebugOwner, computeFn: (reader: IDerivedReader<TChange>) => T, debugLocation?: DebugLocation): IObservableWithChange<T, TChange>;
22 > export function derived<T, TChange = void>(
23 computeFnOrOwner: ((reader: IDerivedReader<TChange>) => T) | DebugOwner,
24 computeFn?: ((reader: IDerivedReader<TChange>) => T) | undefined,
46 );
47 }
48 > derived.ts
49 > export function derivedWithSetter<T>(owner: DebugOwner | undefined, computeFn: (reader: IReader) => T, setter: (value: T, transaction: ITransaction | undefined) => void, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T> {
50 return new DerivedWithSetter(
51 new DebugNameData(owner, undefined, computeFn),
58 );
59 }
60 > derived.ts
61 > export function derivedOpts<T>(
62 options: IDebugNameData & {
63 equalsFn?: EqualityComparer<T>;
76 );
77 }
78 > _setDerivedOpts(derivedOpts); derived.ts
79 >
80 > /**
81 > * Represents an observable that is derived from other observables.
82 > * The value is only recomputed when absolutely needed.
83 > *
84 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
85 > *
86 > * Use `createEmptyChangeSummary` to create a "change summary" that can collect the changes.
87 > * Use `handleChange` to add a reported change to the change summary.
88 > * The compute function is given the last change summary.
89 > * The change summary is discarded after the compute function was called.
90 > *
91 > * @see derived
92 > */
93 > export function derivedHandleChanges<T, TDelta, TChangeSummary>(
94 options: IDebugNameData & {
95 changeTracker: IChangeTracker<TChangeSummary>;
108 );
109 }
110 > derived.ts
111 > /**
112 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
113 > */
114 > export function derivedWithStore<T>(computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
115 >
116 > /**
117 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
118 > */
119 > export function derivedWithStore<T>(owner: DebugOwner, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
120 > export function derivedWithStore<T>(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
121 let computeFn: (reader: IReader, store: DisposableStore) => T;
122 let owner: DebugOwner;
151 );
152 }
153 > derived.ts
154 > export function derivedDisposable<T extends IDisposable | undefined>(computeFn: (reader: IReader) => T): IObservable<T>;
155 > export function derivedDisposable<T extends IDisposable | undefined>(owner: DebugOwner, computeFn: (reader: IReader) => T): IObservable<T>;
156 > export function derivedDisposable<T extends IDisposable | undefined>(computeFnOrOwner: ((reader: IReader) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
157 let computeFn: (reader: IReader) => T;
158 let owner: DebugOwner;
src/vs/base/common/observableInternal/logging/debugGetDependencyGraph.ts 53 introduced LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugGetDependencyGraph.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 { IObservable, IObserver } from '../base.js';
7 > import { Derived } from '../observables/derivedImpl.js';
8 > import { FromEventObservable } from '../observables/observableFromEvent.js';
9 > import { ObservableValue } from '../observables/observableValue.js';
10 > import { AutorunObserver } from '../reactions/autorunImpl.js';
11 > import { formatValue } from './consoleObservableLogger.js';
12 >
13 > interface IOptions {
14 > type: 'dependencies' | 'observers';
15 > debugNamePostProcessor?: (name: string) => string;
16 > }
17 >
18 > export function debugGetObservableGraph(obs: IObservable<any> | IObserver, options: IOptions): string {
19 const debugNamePostProcessor = options?.debugNamePostProcessor ?? ((str: string) => str);
20 const info = Info.from(obs, debugNamePostProcessor);
31 }
32 }
34 function formatObservableInfoWithDependencies(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
35 const indent = '\t\t'.repeat(indentLevel);
58 return lines.join('\n');
59 }
61 function formatObservableInfoWithObservers(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
62 const indent = '\t\t'.repeat(indentLevel);
85 return lines.join('\n');
86 }
88 > class Info {
89 > public static from(obs: IObservable<any> | IObserver, debugNamePostProcessor: (name: string) => string): Info | undefined {
90 > if (obs instanceof AutorunObserver) {
91 const state = obs.debugGetState();
92 return new Info(
99 []
100 );
101 > } else if (obs instanceof Derived) { debugGetDependencyGraph.ts
102 > const state = obs.debugGetState();
103 > return new Info(
104 > obs,
105 > debugNamePostProcessor(obs.debugName),
106 > 'derived',
107 > state.value,
108 > state.stateStr,
109 > Array.from(state.dependencies),
110 > Array.from(obs.debugGetObservers())
111 > );
112 > } else if (obs instanceof ObservableValue) {
113 > const state = obs.debugGetState();
114 > return new Info(
115 > obs,
116 > debugNamePostProcessor(obs.debugName),
117 > 'observableValue',
118 > state.value,
119 > 'upToDate',
120 > [],
121 > Array.from(obs.debugGetObservers())
122 > );
123 > } else if (obs instanceof FromEventObservable) {
124 const state = obs.debugGetState();
125 return new Info(
134 }
135 return undefined;
137 >
138 > public static unknown(obs: IObservable<any> | IObserver): Info {
139 return new Info(
140 obs,
147 );
148 }
150 > constructor(
151 public readonly sourceObj: IObservable<any> | IObserver,
152 public readonly name: string,
src/vs/base/common/observableInternal/logging/debugger/rpc.ts 53 introduced LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- rpc.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 > export type ChannelFactory = (handler: IChannelHandler) => IChannel;
7 >
8 > export interface IChannel {
9 > sendNotification(data: unknown): void;
10 > sendRequest(data: unknown): Promise<RpcRequestResult>;
11 > }
12 >
13 > export interface IChannelHandler {
14 > handleNotification(notificationData: unknown): void;
15 > handleRequest(requestData: unknown): Promise<RpcRequestResult> | RpcRequestResult;
16 > }
17 >
18 > export type RpcRequestResult = { type: 'result'; value: unknown } | { type: 'error'; value: unknown };
19 >
20 > export type API = {
21 > host: Side;
22 > client: Side;
23 > };
24 >
25 > export type Side = {
26 > notifications: Record<string, (...args: any[]) => void>;
27 > requests: Record<string, (...args: any[]) => Promise<unknown> | unknown>;
28 > };
29 >
30 > type MakeAsyncIfNot<TFn> = TFn extends (...args: infer TArgs) => infer TResult ? TResult extends Promise<unknown> ? TFn : (...args: TArgs) => Promise<TResult> : never;
31 >
32 > export type MakeSideAsync<T extends Side> = {
33 > notifications: T['notifications'];
34 > requests: { [K in keyof T['requests']]: MakeAsyncIfNot<T['requests'][K]> };
35 > };
36 >
37 > export class SimpleTypedRpcConnection<T extends Side> {
38 > public static createHost<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['host']): SimpleTypedRpcConnection<MakeSideAsync<T['client']>> {
39 > return new SimpleTypedRpcConnection(channelFactory, getHandler);
40 > }
41 >
42 > public static createClient<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['client']): SimpleTypedRpcConnection<MakeSideAsync<T['host']>> {
43 return new SimpleTypedRpcConnection(channelFactory, getHandler);
44 }
45 > rpc.ts
46 > public readonly api: T;
47 > private readonly _channel: IChannel;
48 >
49 > private constructor(
50 private readonly _channelFactory: ChannelFactory,
51 private readonly _getHandler: () => Side,
95 this.api = { notifications: notifications, requests: requests } as any;
96 }
97 > } rpc.ts
98 >
99 > type OutgoingMessage = [
100 > method: string,
101 > args: unknown[],
102 > ];
src/vs/base/common/observableInternal/debugName.ts 52 introduced LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugName.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 > 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 ) { }
33 > public getDebugName(target: object): string | undefined {
34 return getDebugName(target, this);
35 }
36 > } debugName.ts
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) {
63 return undefined;
64 }
66 function computeDebugName(self: object, data: DebugNameData): string | undefined {
67 const cached = cachedDebugName.get(self);
101 return undefined;
102 }
103 > debugName.ts
104 function findKey(obj: object, value: object): string | undefined {
105 for (const key in obj) {
110 return undefined;
111 }
112 > debugName.ts
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);
127 return result;
128 }
129 > debugName.ts
130 > export function getClassName(obj: object): string | undefined {
131 const ctor = obj.constructor;
132 if (ctor) {
138 return undefined;
139 }
140 > debugName.ts
141 > export function getFunctionName(fn: Function): string | undefined {
142 const fnSrc = fn.toString();
143 // Pattern: /** @description ... */
src/vs/base/common/observableInternal/logging/consoleObservableLogger.ts 52 introduced LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- consoleObservableLogger.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 { IObservable } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { IObservableLogger, IChangeInformation, addLogger } from './logging.js';
9 > import { FromEventObservable } from '../observables/observableFromEvent.js';
10 > import { getClassName } from '../debugName.js';
11 > import { Derived } from '../observables/derivedImpl.js';
12 > import { AutorunObserver } from '../reactions/autorunImpl.js';
13 >
14 > let consoleObservableLogger: ConsoleObservableLogger | undefined;
15 >
16 > export function logObservableToConsole(obs: IObservable<any>): void {
17 if (!consoleObservableLogger) {
18 consoleObservableLogger = new ConsoleObservableLogger();
21 consoleObservableLogger.addFilteredObj(obs);
22 }
24 > export class ConsoleObservableLogger implements IObservableLogger {
25 private indentation = 0;
26
118
119 private readonly changedObservablesSets = new WeakMap<object, Set<IObservable<any>>>();
121 > formatChanges(changes: Set<IObservable<any>>): ConsoleText | undefined {
122 if (changes.size === 0) {
123 return undefined;
130 );
131 }
133 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
134 if (!this._isIncluded(derived)) { return; }
135
136 this.changedObservablesSets.get(derived)?.add(observable);
137 }
139 > _handleDerivedRecomputed(derived: Derived<unknown>, info: IChangeInformation): void {
140 if (!this._isIncluded(derived)) { return; }
141
151 changedObservables.clear();
152 }
154 > handleDerivedCleared(derived: Derived<unknown>): void {
155 if (!this._isIncluded(derived)) { return; }
156
160 ]));
161 }
163 > handleFromEventObservableTriggered(observable: FromEventObservable<any, any>, info: IChangeInformation): void {
164 if (!this._isIncluded(observable)) { return; }
165
171 ]));
172 }
174 > handleAutorunCreated(autorun: AutorunObserver): void {
175 if (!this._isIncluded(autorun)) { return; }
176
177 this.changedObservablesSets.set(autorun, new Set());
178 }
180 > handleAutorunDisposed(autorun: AutorunObserver): void {
181 }
183 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void {
184 if (!this._isIncluded(autorun)) { return; }
185
186 this.changedObservablesSets.get(autorun)!.add(observable);
187 }
189 > handleAutorunStarted(autorun: AutorunObserver): void {
190 const changedObservables = this.changedObservablesSets.get(autorun);
191 if (!changedObservables) { return; }
202 this.indentation++;
203 }
205 > handleAutorunFinished(autorun: AutorunObserver): void {
206 this.indentation--;
207 }
209 > handleBeginTransaction(transaction: TransactionImpl): void {
210 let transactionName = transaction.getDebugName();
211 if (transactionName === undefined) {
221 this.indentation++;
222 }
224 > handleEndTransaction(): void {
225 this.indentation--;
226 }
228 > type ConsoleText = (ConsoleText | undefined)[] |
229 > { text: string; style: string; data?: unknown[] } |
230 > { data: unknown[] };
231 function consoleTextToArgs(text: ConsoleText): unknown[] {
232 const styles = new Array<any>();
294 };
295 }
297 > export function formatValue(value: unknown, availableLen: number): string {
298 switch (typeof value) {
299 case 'number':
325 }
326 }
328 function formatArray(value: unknown[], availableLen: number): string {
329 let result = '[ ';
343 return result;
344 }
346 function formatObject(value: object, availableLen: number): string {
347 if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
371 return result;
372 }
374 function repeat(str: string, count: number): string {
375 let result = '';
379 return result;
380 }
382 function padStr(str: string, length: number): string {
383 while (str.length < length) {
src/vs/base/common/observableInternal/observables/observableFromEvent.ts 51 introduced LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableFromEvent.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 { IObservable, ITransaction } from '../base.js';
7 > import { subtransaction } from '../transaction.js';
8 > import { EqualityComparer, Event, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 >
15 > export function observableFromEvent<T, TArgs = unknown>(
16 > owner: DebugOwner,
17 > event: Event<TArgs>,
18 > getValue: (args: TArgs | undefined) => T,
19 > debugLocation?: DebugLocation,
20 > ): IObservable<T>;
21 > export function observableFromEvent<T, TArgs = unknown>(
22 > event: Event<TArgs>,
23 > getValue: (args: TArgs | undefined) => T,
24 > ): IObservable<T>;
25 > export function observableFromEvent(...args:
26 [owner: DebugOwner, event: Event<any>, getValue: (args: any | undefined) => any, debugLocation?: DebugLocation] |
27 [event: Event<any>, getValue: (args: any | undefined) => any]
45 );
46 }
48 > export function observableFromEventOpts<T, TArgs = unknown>(
49 options: IDebugNameData & {
50 equalsFn?: EqualityComparer<T>;
64 );
65 }
67 > export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
68 > public static globalTransaction: ITransaction | undefined;
69 >
70 > private _value: T | undefined;
71 > private _hasValue = false;
72 > private _subscription: IDisposable | undefined;
73 >
74 > constructor(
75 private readonly _debugNameData: DebugNameData,
76 private readonly event: Event<TArgs>,
131 }
132 };
134 > protected override onLastObserverRemoved(): void {
135 this._subscription!.dispose();
136 this._subscription = undefined;
138 this._value = undefined;
139 }
141 > public get(): T {
142 if (this._subscription) {
143 if (!this._hasValue) {
151 }
152 }
154 > public debugSetValue(value: unknown): void {
155 // eslint-disable-next-line local/code-no-any-casts
156 this._value = value as any;
157 }
159 > public debugGetState() {
160 return { value: this._value, hasValue: this._hasValue };
161 }
163 >
164 > export namespace observableFromEvent {
165 > export const Observer = FromEventObservable;
166 >
167 > export function batchEventsGlobally(tx: ITransaction, fn: () => void): void {
168 let didSet = false;
169 if (FromEventObservable.globalTransaction === undefined) {
src/vs/base/common/observableInternal/observables/lazyObservableValue.ts 49 introduced LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lazyObservableValue.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 { EqualityComparer } from '../commonFacade/deps.js';
7 > import { IObserver, ISettableObservable, ITransaction } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import { DebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Holds off updating observers until the value is actually read.
16 > */
17 > export class LazyObservableValue<T, TChange = void>
18 > extends BaseObservable<T, TChange>
19 > implements ISettableObservable<T, TChange> {
20 > protected _value: T;
21 > private _isUpToDate = true;
22 > private readonly _deltas: TChange[] = [];
23 >
24 > get debugName() {
25 > return this._debugNameData.getDebugName(this) ?? 'LazyObservableValue';
26 > }
27 >
28 > constructor(
29 private readonly _debugNameData: DebugNameData,
30 initialValue: T,
35 this._value = initialValue;
36 }
38 > public override get(): T {
39 this._update();
40 return this._value;
41 }
43 > private _update(): void {
44 if (this._isUpToDate) {
45 return;
62 }
63 }
65 > private _updateCounter = 0;
66 >
67 > private _beginUpdate(): void {
68 this._updateCounter++;
69 if (this._updateCounter === 1) {
73 }
74 }
76 > private _endUpdate(): void {
77 this._updateCounter--;
78 if (this._updateCounter === 0) {
86 }
87 }
89 > public override addObserver(observer: IObserver): void {
90 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCounter > 0;
91 super.addObserver(observer);
95 }
96 }
98 > public override removeObserver(observer: IObserver): void {
99 const shouldCallEndUpdate = this._observers.has(observer) && this._updateCounter > 0;
100 super.removeObserver(observer);
105 }
106 }
108 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
109 if (change === undefined && this._equalityComparator(this._value, value)) {
110 return;
src/vs/base/common/observableInternal/debugLocation.ts 42 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugLocation.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 > export type DebugLocation = DebugLocationImpl | undefined;
7 >
8 > export namespace DebugLocation {
9 > let enabled = false;
10 >
11 > export function enable(): void {
12 enabled = true;
13 }
15 > export function ofCaller(): DebugLocation {
16 if (!enabled) {
17 return undefined;
26 return DebugLocationImpl.fromStack(stack, 2);
27 }
29 >
30 > class DebugLocationImpl implements ILocation {
31 > public static fromStack(stack: string, parentIdx: number): DebugLocationImpl | undefined {
32 > const lines = stack.split('\n');
33 > const location = parseLine(lines[parentIdx + 1]);
34 > if (location) {
35 > return new DebugLocationImpl(
36 > location.fileName,
37 > location.line,
38 > location.column,
39 > location.id
40 > );
41 > } else {
42 > return undefined;
43 > }
44 > }
45 >
46 > constructor(
47 public readonly fileName: string,
48 public readonly line: number,
51 ) {
52 }
54 >
55 >
56 > export interface ILocation {
57 > fileName: string;
58 > line: number;
59 > column: number;
60 > id: string;
61 > }
62 >
63 function parseLine(stackLine: string): ILocation | undefined {
64 const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
src/vs/base/common/observableInternal/transaction.ts 39 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- transaction.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 { handleBugIndicatingErrorRecovery, IObservable, IObserver, ITransaction } from './base.js';
7 > import { getFunctionName } from './debugName.js';
8 > import { getLogger } from './logging/logging.js';
9 >
10 > /**
11 > * Starts a transaction in which many observables can be changed at once.
12 > * {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
13 > * Reaction run on demand or when the transaction ends.
14 > */
15 >
16 > export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
17 const tx = new TransactionImpl(fn, getDebugName);
18 try {
22 }
23 }
24 > let _globalTransaction: ITransaction | undefined = undefined; transaction.ts
25 >
26 > export function globalTransaction(fn: (tx: ITransaction) => void) {
27 if (_globalTransaction) {
28 fn(_globalTransaction);
40 }
41 }
42 > /** @deprecated */ transaction.ts
43 >
44 export async function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void> {
45 const tx = new TransactionImpl(fn, getDebugName);
50 }
51 }
52 > /** transaction.ts
53 > * Allows to chain transactions.
54 > */
55 >
56 > export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
57 if (!tx) {
58 transaction(fn, getDebugName);
60 fn(tx);
61 }
62 > } export class TransactionImpl implements ITransaction { transaction.ts
63 > private _updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
64 >
65 > constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) {
66 getLogger()?.handleBeginTransaction(this);
67 }
69 > public getDebugName(): string | undefined {
70 if (this._getDebugName) {
71 return this._getDebugName();
73 return getFunctionName(this._fn);
74 }
76 > public updateObserver(observer: IObserver, observable: IObservable<any>): void {
77 if (!this._updatingObservers) {
78 // This happens when a transaction is used in a callback or async function.
90 observer.beginUpdate(observable);
91 }
93 > public finish(): void {
94 const updatingObservers = this._updatingObservers;
95 if (!updatingObservers) {
106 getLogger()?.handleEndTransaction(this);
107 }
109 > public debugGetUpdatingObservers() {
110 return this._updatingObservers;
111 }
112 > } transaction.ts
113