Files
vscode-debug-visualizer/extension/src/VisualizationWatchModel/VisualizationWatchModelImpl.ts
mynameisdeleted 18acf1d7f4
Some checks failed
/ build (macos-latest) (push) Has been cancelled
/ build (ubuntu-latest) (push) Has been cancelled
/ build (windows-latest) (push) Has been cancelled
Add evaluateForEffect method to support expression evaluation for side effects
2026-07-10 03:57:54 -04:00

145 lines
4.1 KiB
TypeScript

import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
import { hotClass } from "@hediet/node-reload";
import { Disposable } from "@hediet/std/disposable";
import { EventEmitter } from "@hediet/std/events";
import { wait } from "@hediet/std/timer";
import { action, observable } from "mobx";
import { CancellationToken, CancellationTokenSource } from "vscode";
import { VisualizationBackend } from "../VisualizationBackend/VisualizationBackend";
import { CompletionItem, DataExtractionState } from "../webviewContract";
import { VisualizationWatch, VisualizationWatchModel, VisualizationWatchOptions } from "./VisualizationWatchModel";
@hotClass(module)
export class VisualizationWatchModelImpl implements VisualizationWatchModel {
public readonly dispose = Disposable.fn();
private readonly watchers = new Set<ObservableVisualizationWatch>();
constructor(private readonly visualizationBackend: VisualizationBackend) {
this.dispose.track(
visualizationBackend.onChange.sub(() => {
if (visualizationBackend.expressionLanguageId !== undefined) {
this.lastLanguageId = visualizationBackend.expressionLanguageId;
}
})
);
this.lastLanguageId = visualizationBackend.expressionLanguageId;
}
public createWatch(expression: string, options: VisualizationWatchOptions): VisualizationWatch {
const w = new ObservableVisualizationWatch(expression, options, this.visualizationBackend);
w.onDispose.sub(() => {
this.watchers.delete(w);
});
this.watchers.add(w);
return w;
}
@observable
private lastLanguageId: string | undefined = undefined;
get languageId(): string | undefined {
return this.lastLanguageId;
}
public getCompletions(text: string, column: number): Promise<CompletionItem[]> {
return this.visualizationBackend.getCompletions(text, column);
}
public evaluateForEffect(
expression: string
): Promise<{ result: string } | { error: string }> {
return this.visualizationBackend.evaluateForEffect(expression);
}
}
class ObservableVisualizationWatch implements VisualizationWatch {
public readonly dispose = Disposable.fn();
private readonly onDisposeEmitter = new EventEmitter();
public readonly onDispose = this.onDisposeEmitter.asEvent();
constructor(
public readonly expression: string,
options: VisualizationWatchOptions,
private readonly visualizationBackend: VisualizationBackend
) {
this._preferredDataExtractor = options.preferredDataExtractor;
this.dispose.track(
visualizationBackend.onChange.sub(() => {
this.refresh();
})
);
this.dispose.track({
dispose: () => {
this.onDisposeEmitter.emit();
},
});
this.refresh();
}
private _preferredDataExtractor: DataExtractorId | undefined = undefined;
public get preferredDataExtractor(): DataExtractorId | undefined {
return this._preferredDataExtractor;
}
@action
public setPreferredDataExtractor(id: DataExtractorId | undefined): void {
this._preferredDataExtractor = id;
this.refresh();
}
private runningRefreshOperation: Disposable | undefined;
public refresh(): void {
if (this.runningRefreshOperation) {
this.runningRefreshOperation.dispose();
}
const tokenSource = new CancellationTokenSource();
this.runningRefreshOperation = {
dispose: () => {
tokenSource.cancel();
},
};
this._refresh(tokenSource.token);
}
public sessionState: unknown = undefined;
private async _refresh(token: CancellationToken): Promise<void> {
this._state = { kind: "loading" };
const that = this;
const result = await this.visualizationBackend.getVisualizationData({
expression: this.expression,
preferredExtractorId: this.preferredDataExtractor,
sessionStore: {
get data(): unknown {
return that.sessionState;
},
set data(value: unknown) {
that.sessionState = value;
},
},
});
if (result.kind === "error") {
// give cancellation requested more time in case of errors
await wait(330);
}
if (token.isCancellationRequested) {
return;
}
this._state = result;
}
@observable
public _state: DataExtractionState = { kind: "loading" };
public get state(): DataExtractionState {
return this._state;
}
}