mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
Adds debug methods to observables (#242600)
This commit is contained in:
committed by
GitHub
parent
04a1f0815a
commit
73072ae20b
@@ -168,13 +168,13 @@ export const enum AutorunState {
|
||||
}
|
||||
|
||||
export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader, IDisposable {
|
||||
public _state = AutorunState.stale;
|
||||
private updateCount = 0;
|
||||
private disposed = false;
|
||||
public _dependencies = new Set<IObservable<any>>();
|
||||
private dependenciesToBeRemoved = new Set<IObservable<any>>();
|
||||
private changeSummary: TChangeSummary | undefined;
|
||||
public _isRunning = false;
|
||||
private _state = AutorunState.stale;
|
||||
private _updateCount = 0;
|
||||
private _disposed = false;
|
||||
private _dependencies = new Set<IObservable<any>>();
|
||||
private _dependenciesToBeRemoved = new Set<IObservable<any>>();
|
||||
private _changeSummary: TChangeSummary | undefined;
|
||||
private _isRunning = false;
|
||||
|
||||
public get debugName(): string {
|
||||
return this._debugNameData.getDebugName(this) ?? '(anonymous)';
|
||||
@@ -186,7 +186,7 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
private readonly createChangeSummary: (() => TChangeSummary) | undefined,
|
||||
private readonly _handleChange: ((context: IChangeContext, summary: TChangeSummary) => boolean) | undefined,
|
||||
) {
|
||||
this.changeSummary = this.createChangeSummary?.();
|
||||
this._changeSummary = this.createChangeSummary?.();
|
||||
getLogger()?.handleAutorunCreated(this);
|
||||
this._run();
|
||||
|
||||
@@ -194,7 +194,7 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposed = true;
|
||||
this._disposed = true;
|
||||
for (const o of this._dependencies) {
|
||||
o.removeObserver(this); // Warning: external call!
|
||||
}
|
||||
@@ -205,18 +205,18 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
}
|
||||
|
||||
private _run() {
|
||||
const emptySet = this.dependenciesToBeRemoved;
|
||||
this.dependenciesToBeRemoved = this._dependencies;
|
||||
const emptySet = this._dependenciesToBeRemoved;
|
||||
this._dependenciesToBeRemoved = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
|
||||
this._state = AutorunState.upToDate;
|
||||
|
||||
try {
|
||||
if (!this.disposed) {
|
||||
if (!this._disposed) {
|
||||
getLogger()?.handleAutorunStarted(this);
|
||||
const changeSummary = this.changeSummary!;
|
||||
const changeSummary = this._changeSummary!;
|
||||
try {
|
||||
this.changeSummary = this.createChangeSummary?.(); // Warning: external call!
|
||||
this._changeSummary = this.createChangeSummary?.(); // Warning: external call!
|
||||
this._isRunning = true;
|
||||
this._runFn(this, changeSummary); // Warning: external call!
|
||||
} catch (e) {
|
||||
@@ -226,15 +226,15 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!this.disposed) {
|
||||
if (!this._disposed) {
|
||||
getLogger()?.handleAutorunFinished(this);
|
||||
}
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this.dependenciesToBeRemoved) {
|
||||
for (const o of this._dependenciesToBeRemoved) {
|
||||
o.removeObserver(this); // Warning: external call!
|
||||
}
|
||||
this.dependenciesToBeRemoved.clear();
|
||||
this._dependenciesToBeRemoved.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,12 +247,12 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
if (this._state === AutorunState.upToDate) {
|
||||
this._state = AutorunState.dependenciesMightHaveChanged;
|
||||
}
|
||||
this.updateCount++;
|
||||
this._updateCount++;
|
||||
}
|
||||
|
||||
public endUpdate(_observable: IObservable<any>): void {
|
||||
try {
|
||||
if (this.updateCount === 1) {
|
||||
if (this._updateCount === 1) {
|
||||
do {
|
||||
if (this._state === AutorunState.dependenciesMightHaveChanged) {
|
||||
this._state = AutorunState.upToDate;
|
||||
@@ -271,10 +271,10 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
} while (this._state !== AutorunState.upToDate);
|
||||
}
|
||||
} finally {
|
||||
this.updateCount--;
|
||||
this._updateCount--;
|
||||
}
|
||||
|
||||
assertFn(() => this.updateCount >= 0);
|
||||
assertFn(() => this._updateCount >= 0);
|
||||
}
|
||||
|
||||
public handlePossibleChange(observable: IObservable<any>): void {
|
||||
@@ -292,7 +292,7 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
changedObservable: observable,
|
||||
change,
|
||||
didChange: (o): this is any => o === observable as any,
|
||||
}, this.changeSummary!) : true;
|
||||
}, this._changeSummary!) : true;
|
||||
if (shouldReact) {
|
||||
this._state = AutorunState.stale;
|
||||
}
|
||||
@@ -303,7 +303,7 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
}
|
||||
|
||||
private _isDependency(observable: IObservableWithChange<any, any>): boolean {
|
||||
return this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable);
|
||||
return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable);
|
||||
}
|
||||
|
||||
// IReader implementation
|
||||
@@ -312,16 +312,33 @@ export class AutorunObserver<TChangeSummary = any> implements IObserver, IReader
|
||||
if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
|
||||
|
||||
// In case the run action disposes the autorun
|
||||
if (this.disposed) {
|
||||
if (this._disposed) {
|
||||
return observable.get(); // warning: external call!
|
||||
}
|
||||
|
||||
observable.addObserver(this); // warning: external call!
|
||||
const value = observable.get(); // warning: external call!
|
||||
this._dependencies.add(observable);
|
||||
this.dependenciesToBeRemoved.delete(observable);
|
||||
this._dependenciesToBeRemoved.delete(observable);
|
||||
return value;
|
||||
}
|
||||
|
||||
public debugGetState() {
|
||||
return {
|
||||
isRunning: this._isRunning,
|
||||
updateCount: this._updateCount,
|
||||
dependencies: this._dependencies,
|
||||
state: this._state,
|
||||
};
|
||||
}
|
||||
|
||||
public debugRerun(): void {
|
||||
if (!this._isRunning) {
|
||||
this._run();
|
||||
} else {
|
||||
this._state = AutorunState.stale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace autorun {
|
||||
|
||||
@@ -290,7 +290,7 @@ export abstract class ConvenientObservable<T, TChange> implements IObservableWit
|
||||
}
|
||||
|
||||
export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
|
||||
public readonly _observers = new Set<IObserver>();
|
||||
protected readonly _observers = new Set<IObserver>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -329,6 +329,10 @@ export abstract class BaseObservable<T, TChange = void> extends ConvenientObserv
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public debugGetObservers() {
|
||||
return this._observers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +389,7 @@ export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransacti
|
||||
}
|
||||
|
||||
export class TransactionImpl implements ITransaction {
|
||||
public _updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
|
||||
private _updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
|
||||
|
||||
constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) {
|
||||
getLogger()?.handleBeginTransaction(this);
|
||||
@@ -414,6 +418,10 @@ export class TransactionImpl implements ITransaction {
|
||||
this._updatingObservers = null;
|
||||
getLogger()?.handleEndTransaction(this);
|
||||
}
|
||||
|
||||
public debugGetUpdatingObservers() {
|
||||
return this._updatingObservers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,6 +503,16 @@ export class ObservableValue<T, TChange = void>
|
||||
protected _setValue(newValue: T): void {
|
||||
this._value = newValue;
|
||||
}
|
||||
|
||||
public debugGetState() {
|
||||
return {
|
||||
value: this._value,
|
||||
};
|
||||
}
|
||||
|
||||
public debugSetValue(value: unknown) {
|
||||
this._value = value as T;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -194,14 +194,14 @@ export const enum DerivedState {
|
||||
}
|
||||
|
||||
export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> implements IReader, IObserver {
|
||||
public _state = DerivedState.initial;
|
||||
private value: T | undefined = undefined;
|
||||
public _updateCount = 0;
|
||||
public _dependencies = new Set<IObservable<any>>();
|
||||
private dependenciesToBeRemoved = new Set<IObservable<any>>();
|
||||
private changeSummary: TChangeSummary | undefined = undefined;
|
||||
private _state = DerivedState.initial;
|
||||
private _value: T | undefined = undefined;
|
||||
private _updateCount = 0;
|
||||
private _dependencies = new Set<IObservable<any>>();
|
||||
private _dependenciesToBeRemoved = new Set<IObservable<any>>();
|
||||
private _changeSummary: TChangeSummary | undefined = undefined;
|
||||
private _isUpdating = false;
|
||||
public _isComputing = false;
|
||||
private _isComputing = false;
|
||||
|
||||
public override get debugName(): string {
|
||||
return this._debugNameData.getDebugName(this) ?? '(anonymous)';
|
||||
@@ -216,7 +216,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
private readonly _equalityComparator: EqualityComparer<T>,
|
||||
) {
|
||||
super();
|
||||
this.changeSummary = this.createChangeSummary?.();
|
||||
this._changeSummary = this.createChangeSummary?.();
|
||||
}
|
||||
|
||||
protected override onLastObserverRemoved(): void {
|
||||
@@ -225,7 +225,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
* that our cache is invalid.
|
||||
*/
|
||||
this._state = DerivedState.initial;
|
||||
this.value = undefined;
|
||||
this._value = undefined;
|
||||
getLogger()?.handleDerivedCleared(this);
|
||||
for (const d of this._dependencies) {
|
||||
d.removeObserver(this);
|
||||
@@ -283,17 +283,17 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
}
|
||||
// In case recomputation changed one of our dependencies, we need to recompute again.
|
||||
} while (this._state !== DerivedState.upToDate);
|
||||
return this.value!;
|
||||
return this._value!;
|
||||
}
|
||||
}
|
||||
|
||||
private _recompute() {
|
||||
const emptySet = this.dependenciesToBeRemoved;
|
||||
this.dependenciesToBeRemoved = this._dependencies;
|
||||
const emptySet = this._dependenciesToBeRemoved;
|
||||
this._dependenciesToBeRemoved = this._dependencies;
|
||||
this._dependencies = emptySet;
|
||||
|
||||
const hadValue = this._state !== DerivedState.initial;
|
||||
const oldValue = this.value;
|
||||
const oldValue = this._value;
|
||||
this._state = DerivedState.upToDate;
|
||||
|
||||
let didChange = false;
|
||||
@@ -301,27 +301,27 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
this._isComputing = true;
|
||||
|
||||
try {
|
||||
const changeSummary = this.changeSummary!;
|
||||
this.changeSummary = this.createChangeSummary?.();
|
||||
const changeSummary = this._changeSummary!;
|
||||
this._changeSummary = this.createChangeSummary?.();
|
||||
try {
|
||||
this._isReaderValid = true;
|
||||
/** might call {@link handleChange} indirectly, which could invalidate us */
|
||||
this.value = this._computeFn(this, changeSummary);
|
||||
this._value = this._computeFn(this, changeSummary);
|
||||
} finally {
|
||||
this._isReaderValid = false;
|
||||
// We don't want our observed observables to think that they are (not even temporarily) not being observed.
|
||||
// Thus, we only unsubscribe from observables that are definitely not read anymore.
|
||||
for (const o of this.dependenciesToBeRemoved) {
|
||||
for (const o of this._dependenciesToBeRemoved) {
|
||||
o.removeObserver(this);
|
||||
}
|
||||
this.dependenciesToBeRemoved.clear();
|
||||
this._dependenciesToBeRemoved.clear();
|
||||
}
|
||||
|
||||
didChange = hadValue && !(this._equalityComparator(oldValue!, this.value));
|
||||
didChange = hadValue && !(this._equalityComparator(oldValue!, this._value));
|
||||
|
||||
getLogger()?.handleObservableUpdated(this, {
|
||||
oldValue,
|
||||
newValue: this.value,
|
||||
newValue: this._value,
|
||||
change: undefined,
|
||||
didChange,
|
||||
hadValue,
|
||||
@@ -396,7 +396,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
|
||||
public handlePossibleChange<T>(observable: IObservable<T>): void {
|
||||
// In all other states, observers already know that we might have changed.
|
||||
if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) {
|
||||
if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
|
||||
this._state = DerivedState.dependenciesMightHaveChanged;
|
||||
for (const r of this._observers) {
|
||||
r.handlePossibleChange(this);
|
||||
@@ -405,7 +405,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
}
|
||||
|
||||
public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
|
||||
if (this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) {
|
||||
if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
|
||||
getLogger()?.handleDerivedDependencyChanged(this, observable, change);
|
||||
|
||||
let shouldReact = false;
|
||||
@@ -414,7 +414,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
changedObservable: observable,
|
||||
change,
|
||||
didChange: (o): this is any => o === observable as any,
|
||||
}, this.changeSummary!) : true;
|
||||
}, this._changeSummary!) : true;
|
||||
} catch (e) {
|
||||
onBugIndicatingError(e);
|
||||
}
|
||||
@@ -443,7 +443,7 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
const value = observable.get();
|
||||
// Which is why we only add the observable to the dependencies now.
|
||||
this._dependencies.add(observable);
|
||||
this.dependenciesToBeRemoved.delete(observable);
|
||||
this._dependenciesToBeRemoved.delete(observable);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -469,6 +469,21 @@ export class Derived<T, TChangeSummary = any> extends BaseObservable<T, void> im
|
||||
}
|
||||
super.removeObserver(observer);
|
||||
}
|
||||
|
||||
public debugGetState() {
|
||||
return {
|
||||
state: this._state,
|
||||
updateCount: this._updateCount,
|
||||
isComputing: this._isComputing,
|
||||
dependencies: this._dependencies,
|
||||
value: this._value,
|
||||
};
|
||||
}
|
||||
|
||||
public debugSetValue(newValue: unknown) {
|
||||
this._value = newValue as any;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ export type ObsDebuggerApi = {
|
||||
getSummarizedInstances(): IObsPushState;
|
||||
getDerivedInfo(instanceId: ObsInstanceId): IDerivedObservableDetailedInfo;
|
||||
getAutorunInfo(instanceId: ObsInstanceId): IAutorunDetailedInfo;
|
||||
getObservableValueInfo(instanceId: ObsInstanceId): IObservableValueInfo;
|
||||
setValue(instanceId: ObsInstanceId, jsonValue: unknown): void;
|
||||
getValue(instanceId: ObsInstanceId): unknown;
|
||||
|
||||
getTransactionState(): ITransactionState | undefined;
|
||||
}
|
||||
@@ -94,6 +97,10 @@ export type ObsStateUpdate = Partial<IObsDeclarations> & DeepPartial<IObsPushSta
|
||||
|
||||
type DeepPartial<T> = { [TKey in keyof T]?: DeepPartial<T[TKey]> };
|
||||
|
||||
export interface IObservableValueInfo {
|
||||
observers: IObsInstanceRef[];
|
||||
}
|
||||
|
||||
export interface IDerivedObservableDetailedInfo {
|
||||
dependencies: IObsInstanceRef[];
|
||||
observers: IObsInstanceRef[];
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AutorunObserver, AutorunState } from '../../autorun.js';
|
||||
import { IObservable, IObserver, TransactionImpl } from '../../base.js';
|
||||
import { BaseObservable, IObservable, IObserver, ObservableValue, TransactionImpl } from '../../base.js';
|
||||
import { Derived, DerivedState } from '../../derived.js';
|
||||
import { IChangeInformation, IObservableLogger } from '../logging.js';
|
||||
import { formatValue } from '../consoleObservableLogger.js';
|
||||
@@ -12,6 +12,8 @@ import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransa
|
||||
import { registerDebugChannel } from './debuggerRpc.js';
|
||||
import { deepAssign, deepAssignDeleteNulls, getFirstStackFrameOutsideOf, ILocation, Throttler } from './utils.js';
|
||||
import { isDefined } from '../../../types.js';
|
||||
import { FromEventObservable } from '../../utils.js';
|
||||
import { BugIndicatingError, onUnexpectedError } from '../../../errors.js';
|
||||
|
||||
interface IInstanceInfo {
|
||||
declarationId: number;
|
||||
@@ -75,21 +77,61 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
getSummarizedInstances: () => {
|
||||
return null!;
|
||||
},
|
||||
getObservableValueInfo: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
|
||||
return {
|
||||
observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getDerivedInfo: instanceId => {
|
||||
const d = this._aliveInstances.get(instanceId) as Derived<any>;
|
||||
return {
|
||||
dependencies: [...d._dependencies].map(d => this._formatObservable(d)),
|
||||
observers: [...d._observers].map(d => this._formatObserver(d)).filter(isDefined),
|
||||
dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
|
||||
observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getAutorunInfo: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId) as AutorunObserver;
|
||||
return {
|
||||
dependencies: [...obs._dependencies].map(d => this._formatObservable(d)),
|
||||
dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
|
||||
};
|
||||
},
|
||||
getTransactionState: () => {
|
||||
return this.getTransactionState();
|
||||
},
|
||||
setValue: (instanceId, jsonValue) => {
|
||||
const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
|
||||
|
||||
if (obs instanceof Derived) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
} else if (obs instanceof ObservableValue) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
} else if (obs instanceof FromEventObservable) {
|
||||
obs.debugSetValue(jsonValue);
|
||||
} else {
|
||||
throw new BugIndicatingError('Observable is not supported');
|
||||
}
|
||||
|
||||
const observers = [...obs.debugGetObservers()];
|
||||
for (const d of observers) {
|
||||
d.beginUpdate(obs);
|
||||
}
|
||||
for (const d of observers) {
|
||||
d.handleChange(obs, undefined);
|
||||
}
|
||||
for (const d of observers) {
|
||||
d.endUpdate(obs);
|
||||
}
|
||||
},
|
||||
getValue: instanceId => {
|
||||
const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
|
||||
if (obs instanceof Derived) {
|
||||
return formatValue(obs.debugGetState().value, 200);
|
||||
} else if (obs instanceof ObservableValue) {
|
||||
return formatValue(obs.debugGetState().value, 200);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -101,7 +143,7 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
if (txs.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const observerQueue = txs.flatMap(t => t._updatingObservers ?? []).map(o => o.observer);
|
||||
const observerQueue = txs.flatMap(t => t.debugGetUpdatingObservers() ?? []).map(o => o.observer);
|
||||
const processedObservers = new Set<IObserver>();
|
||||
while (observerQueue.length > 0) {
|
||||
const observer = observerQueue.shift()!;
|
||||
@@ -124,36 +166,42 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected };
|
||||
}
|
||||
|
||||
private _getObservableInfo(observable: IObservable<any>): IObservableInfo {
|
||||
private _getObservableInfo(observable: IObservable<any>): IObservableInfo | undefined {
|
||||
const info = this._instanceInfos.get(observable);
|
||||
if (!info) {
|
||||
throw new Error('No info found');
|
||||
onUnexpectedError(new BugIndicatingError('No info found'));
|
||||
return undefined;
|
||||
}
|
||||
return info as IObservableInfo;
|
||||
}
|
||||
|
||||
private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo {
|
||||
private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo | undefined {
|
||||
const info = this._instanceInfos.get(autorun);
|
||||
if (!info) {
|
||||
throw new Error('No info found');
|
||||
onUnexpectedError(new BugIndicatingError('No info found'));
|
||||
return undefined;
|
||||
}
|
||||
return info as IAutorunInfo;
|
||||
}
|
||||
|
||||
private _getInfo(observer: IObserver, queue: (observer: IObserver) => void): ObserverInstanceState | undefined {
|
||||
if (observer instanceof Derived) {
|
||||
const observersToUpdate = [...observer._observers];
|
||||
const observersToUpdate = [...observer.debugGetObservers()];
|
||||
for (const o of observersToUpdate) {
|
||||
queue(o);
|
||||
}
|
||||
|
||||
const info = this._getObservableInfo(observer)!;
|
||||
const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: observer._updateCount };
|
||||
const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)!.instanceId);
|
||||
if (observer._isComputing) {
|
||||
const info = this._getObservableInfo(observer);
|
||||
if (!info) { return; }
|
||||
|
||||
const observerState = observer.debugGetState();
|
||||
|
||||
const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: observerState.updateCount };
|
||||
const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)?.instanceId).filter(isDefined);
|
||||
if (observerState.isComputing) {
|
||||
return { ...base, type: 'observable/derived', state: 'updating', changedDependencies, initialComputation: false };
|
||||
}
|
||||
switch (observer._state) {
|
||||
switch (observerState.state) {
|
||||
case DerivedState.initial:
|
||||
return { ...base, type: 'observable/derived', state: 'noValue' };
|
||||
case DerivedState.upToDate:
|
||||
@@ -164,13 +212,15 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
return { ...base, type: 'observable/derived', state: 'possiblyStale' };
|
||||
}
|
||||
} else if (observer instanceof AutorunObserver) {
|
||||
const info = this._getAutorunInfo(observer)!;
|
||||
const info = this._getAutorunInfo(observer);
|
||||
if (!info) { return undefined; }
|
||||
|
||||
const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: info.updateCount };
|
||||
const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)!.instanceId);
|
||||
if (observer._isRunning) {
|
||||
if (observer.debugGetState().isRunning) {
|
||||
return { ...base, type: 'autorun', state: 'updating', changedDependencies };
|
||||
}
|
||||
switch (observer._state) {
|
||||
switch (observer.debugGetState().state) {
|
||||
case AutorunState.upToDate:
|
||||
return { ...base, type: 'autorun', state: 'upToDate' };
|
||||
case AutorunState.stale:
|
||||
@@ -183,8 +233,10 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _formatObservable(obs: IObservable<any>): { name: string; instanceId: ObsInstanceId } {
|
||||
return { name: obs.debugName, instanceId: this._getObservableInfo(obs)?.instanceId! };
|
||||
private _formatObservable(obs: IObservable<any>): { name: string; instanceId: ObsInstanceId } | undefined {
|
||||
const info = this._getObservableInfo(obs);
|
||||
if (!info) { return undefined; }
|
||||
return { name: obs.debugName, instanceId: info.instanceId };
|
||||
}
|
||||
|
||||
private _formatObserver(obs: IObserver): { name: string; instanceId: ObsInstanceId } | undefined {
|
||||
@@ -236,10 +288,10 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
const stack = new Error().stack!;
|
||||
Error.stackTraceLimit = l;
|
||||
|
||||
let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|[/\\]util(s)?\./);
|
||||
let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe|[/\\]util(s)?\./);
|
||||
|
||||
if (!shallow && !result) {
|
||||
result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]/)!;
|
||||
result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe/)!;
|
||||
}
|
||||
if (result) {
|
||||
loc = result;
|
||||
@@ -284,7 +336,8 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
|
||||
handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
if (info.listenerCount === 0 && newCount > 0) {
|
||||
const type: IObsDeclaration['type'] =
|
||||
observable instanceof Derived ? 'observable/derived' : 'observable/value';
|
||||
@@ -308,7 +361,6 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
}
|
||||
info.listenerCount = newCount;
|
||||
}
|
||||
}
|
||||
|
||||
handleObservableUpdated(observable: IObservable<any>, changeInfo: IChangeInformation): void {
|
||||
if (observable instanceof Derived) {
|
||||
@@ -355,33 +407,33 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
}
|
||||
handleAutorunDisposed(autorun: AutorunObserver): void {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: null }
|
||||
});
|
||||
this._instanceInfos.delete(autorun);
|
||||
this._aliveInstances.delete(info.instanceId);
|
||||
}
|
||||
}
|
||||
handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
info.changedObservables.add(observable);
|
||||
}
|
||||
}
|
||||
handleAutorunStarted(autorun: AutorunObserver): void {
|
||||
|
||||
}
|
||||
handleAutorunFinished(autorun: AutorunObserver): void {
|
||||
const info = this._getAutorunInfo(autorun);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
info.changedObservables.clear();
|
||||
info.updateCount++;
|
||||
this._handleChange({
|
||||
instances: { [info.instanceId]: { runCount: info.updateCount } }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
|
||||
const info = this._getObservableInfo(derived);
|
||||
@@ -391,7 +443,8 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
}
|
||||
_handleDerivedRecomputed(observable: Derived<any>, changeInfo: IChangeInformation): void {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
const formattedValue = formatValue(changeInfo.newValue, 30);
|
||||
info.updateCount++;
|
||||
info.changedObservables.clear();
|
||||
@@ -403,10 +456,10 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
handleDerivedCleared(observable: Derived<any>): void {
|
||||
const info = this._getObservableInfo(observable);
|
||||
if (info) {
|
||||
if (!info) { return; }
|
||||
|
||||
info.lastValue = undefined;
|
||||
info.changedObservables.clear();
|
||||
if (info.listenerCount > 0) {
|
||||
@@ -419,7 +472,6 @@ export class DevToolsLogger implements IObservableLogger {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
handleBeginTransaction(transaction: TransactionImpl): void {
|
||||
this._activeTransactions.add(transaction);
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ export function observableFromEventOpts<T, TArgs = unknown>(
|
||||
export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
|
||||
public static globalTransaction: ITransaction | undefined;
|
||||
|
||||
private value: T | undefined;
|
||||
private hasValue = false;
|
||||
private subscription: IDisposable | undefined;
|
||||
private _value: T | undefined;
|
||||
private _hasValue = false;
|
||||
private _subscription: IDisposable | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _debugNameData: DebugNameData,
|
||||
@@ -126,25 +126,25 @@ export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
|
||||
}
|
||||
|
||||
protected override onFirstObserverAdded(): void {
|
||||
this.subscription = this.event(this.handleEvent);
|
||||
this._subscription = this.event(this.handleEvent);
|
||||
}
|
||||
|
||||
private readonly handleEvent = (args: TArgs | undefined) => {
|
||||
const newValue = this._getValue(args);
|
||||
const oldValue = this.value;
|
||||
const oldValue = this._value;
|
||||
|
||||
const didChange = !this.hasValue || !(this._equalityComparator(oldValue!, newValue));
|
||||
const didChange = !this._hasValue || !(this._equalityComparator(oldValue!, newValue));
|
||||
let didRunTransaction = false;
|
||||
|
||||
if (didChange) {
|
||||
this.value = newValue;
|
||||
this._value = newValue;
|
||||
|
||||
if (this.hasValue) {
|
||||
if (this._hasValue) {
|
||||
didRunTransaction = true;
|
||||
subtransaction(
|
||||
this._getTransaction(),
|
||||
(tx) => {
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue });
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue });
|
||||
|
||||
for (const o of this._observers) {
|
||||
tx.updateObserver(o, this);
|
||||
@@ -157,33 +157,37 @@ export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
|
||||
}
|
||||
);
|
||||
}
|
||||
this.hasValue = true;
|
||||
this._hasValue = true;
|
||||
}
|
||||
|
||||
if (!didRunTransaction) {
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue });
|
||||
getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue });
|
||||
}
|
||||
};
|
||||
|
||||
protected override onLastObserverRemoved(): void {
|
||||
this.subscription!.dispose();
|
||||
this.subscription = undefined;
|
||||
this.hasValue = false;
|
||||
this.value = undefined;
|
||||
this._subscription!.dispose();
|
||||
this._subscription = undefined;
|
||||
this._hasValue = false;
|
||||
this._value = undefined;
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
if (this.subscription) {
|
||||
if (!this.hasValue) {
|
||||
if (this._subscription) {
|
||||
if (!this._hasValue) {
|
||||
this.handleEvent(undefined);
|
||||
}
|
||||
return this.value!;
|
||||
return this._value!;
|
||||
} else {
|
||||
// no cache, as there are no subscribers to keep it updated
|
||||
const value = this._getValue(undefined);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public debugSetValue(value: unknown) {
|
||||
this._value = value as any;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace observableFromEvent {
|
||||
|
||||
Reference in New Issue
Block a user