Code improvements.
This commit is contained in:
@@ -1,55 +1,50 @@
|
||||
import { workspace } from "vscode";
|
||||
import { workspace, window, ColorThemeKind } from "vscode";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { observable } from "mobx";
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
|
||||
const useChromeKioskModeKey = "debugVisualizer.useChromeKioskMode";
|
||||
const debugAdapterConfigsKey = "debugVisualizer.debugAdapterConfigurations";
|
||||
import { VsCodeSetting, serializerWithDefault } from "./utils/VsCodeSettings";
|
||||
|
||||
export class Config {
|
||||
public dispose = Disposable.fn();
|
||||
|
||||
@observable
|
||||
private _useChromeKioskMode!: boolean;
|
||||
|
||||
@observable
|
||||
private _debugAdapterConfigs!: DebugAdapterConfigs;
|
||||
private readonly _useChromeKioskMode = new VsCodeSetting(
|
||||
"debugVisualizer.useChromeKioskMode",
|
||||
{ serializer: serializerWithDefault<boolean>(true) }
|
||||
);
|
||||
|
||||
public get useChromeKioskMode(): boolean {
|
||||
return this._useChromeKioskMode;
|
||||
return this._useChromeKioskMode.get();
|
||||
}
|
||||
|
||||
public get debugAdapterConfigs(): DebugAdapterConfigs {
|
||||
return this._debugAdapterConfigs;
|
||||
private readonly _debugAdapterConfigs = new VsCodeSetting(
|
||||
"debugVisualizer.debugAdapterConfigurations",
|
||||
{ serializer: serializerWithDefault<DebugAdapterConfigs>({}) }
|
||||
);
|
||||
|
||||
@observable
|
||||
private _vsCodeTheme = window.activeColorTheme;
|
||||
|
||||
public get theme(): "light" | "dark" {
|
||||
if (this._vsCodeTheme.kind === ColorThemeKind.Light) {
|
||||
return "light";
|
||||
} else if (this._vsCodeTheme.kind === ColorThemeKind.Dark) {
|
||||
return "dark";
|
||||
}
|
||||
return "light";
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.updateConfig();
|
||||
this.dispose.track(
|
||||
workspace.onDidChangeConfiguration(() => {
|
||||
this.updateConfig();
|
||||
window.onDidChangeActiveColorTheme(() => {
|
||||
this._vsCodeTheme = window.activeColorTheme;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private updateConfig(): void {
|
||||
const c = workspace.getConfiguration();
|
||||
|
||||
this._useChromeKioskMode = mapUndefined(
|
||||
c.get<boolean>(useChromeKioskModeKey),
|
||||
true
|
||||
);
|
||||
|
||||
this._debugAdapterConfigs = mapUndefined(
|
||||
c.get<DebugAdapterConfigs>(debugAdapterConfigsKey),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
public getDebugAdapterConfig(
|
||||
debugAdapterType: string
|
||||
): DebugAdapterConfig | undefined {
|
||||
const c = this.debugAdapterConfigs[debugAdapterType];
|
||||
const c = this._debugAdapterConfigs.get()[debugAdapterType];
|
||||
if (!c) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -65,13 +60,6 @@ export class Config {
|
||||
}
|
||||
}
|
||||
|
||||
function mapUndefined<T>(val: T | undefined, defaultVal: T) {
|
||||
if (val === undefined) {
|
||||
return defaultVal;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
type DebugAdapterConfigs = {
|
||||
[debugAdapter: string]: {
|
||||
context?: "watch" | "repl";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
|
||||
export class ComposedEvaluationEngine implements EvaluationEngine {
|
||||
constructor(public readonly engines: EvaluationEngine[]) {}
|
||||
|
||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
for (const f of this.engines) {
|
||||
const evaluator = f.createEvaluator(session);
|
||||
if (evaluator) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import { EvaluationEngine, Evaluator } from "./EvaluationEngine";
|
||||
import { Config, DebugAdapterConfig } from "../../Config";
|
||||
import { GenericEvaluator } from "./GenericEvaluationEngine";
|
||||
@@ -11,7 +11,7 @@ registerUpdateReconciler(module);
|
||||
export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
||||
constructor(private readonly config: Config) {}
|
||||
|
||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
const config = this.config.getDebugAdapterConfig(session.session.type);
|
||||
if (!config) {
|
||||
return undefined;
|
||||
@@ -22,7 +22,7 @@ export class ConfiguredEvaluationEngine implements EvaluationEngine {
|
||||
|
||||
class ConfiguredEvaluator extends GenericEvaluator {
|
||||
constructor(
|
||||
session: VsCodeDebugSession,
|
||||
session: EnhancedDebugSession,
|
||||
private readonly config: DebugAdapterConfig
|
||||
) {
|
||||
super(session);
|
||||
|
||||
@@ -2,11 +2,11 @@ import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
||||
import { FormattedMessage } from "../../contract";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
|
||||
export interface EvaluationEngine {
|
||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined;
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined;
|
||||
}
|
||||
|
||||
export interface Evaluator {
|
||||
|
||||
@@ -2,21 +2,21 @@ import {
|
||||
DataExtractionResult,
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import {
|
||||
EvaluationEngine,
|
||||
Evaluator,
|
||||
EvaluationArgs,
|
||||
} from "./EvaluationEngine";
|
||||
import { parseEvaluationResultFromGenericDebugAdapter } from "./parseEvaluationResultFromGenericDebugAdapter";
|
||||
import { FormattedMessage } from "../../contract";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class GenericEvaluationEngine implements EvaluationEngine {
|
||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
return new GenericEvaluator(session);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export class GenericEvaluationEngine implements EvaluationEngine {
|
||||
export class GenericEvaluator implements Evaluator {
|
||||
public readonly languageId = "text";
|
||||
|
||||
constructor(private readonly session: VsCodeDebugSession) {}
|
||||
constructor(private readonly session: EnhancedDebugSession) {}
|
||||
|
||||
public async evaluate({
|
||||
expression,
|
||||
|
||||
@@ -5,20 +5,20 @@ import {
|
||||
getExpressionToInitializeDataExtractorApi,
|
||||
DataExtractionResult,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { VsCodeDebugSession } from "../../VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "../../debugger/EnhancedDebugSession";
|
||||
import {
|
||||
EvaluationEngine,
|
||||
Evaluator,
|
||||
EvaluationArgs,
|
||||
} from "./EvaluationEngine";
|
||||
import { FormattedMessage } from "../../contract";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
import { registerUpdateReconciler, hotClass } from "@hediet/node-reload";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class JsEvaluationEngine implements EvaluationEngine {
|
||||
createEvaluator(session: VsCodeDebugSession): Evaluator | undefined {
|
||||
createEvaluator(session: EnhancedDebugSession): Evaluator | undefined {
|
||||
const supportedDebugAdapters = [
|
||||
"node",
|
||||
"node2",
|
||||
@@ -37,7 +37,7 @@ export class JsEvaluationEngine implements EvaluationEngine {
|
||||
class JsEvaluator implements Evaluator {
|
||||
public readonly languageId = "javascript";
|
||||
|
||||
constructor(private readonly session: VsCodeDebugSession) {}
|
||||
constructor(private readonly session: EnhancedDebugSession) {}
|
||||
|
||||
private getContext(): "copy" | "repl" {
|
||||
if (this.session.session.type.startsWith("pwa-")) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
DataExtractionResult,
|
||||
isExtractedData,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
import { FormattedMessage } from "../../contract";
|
||||
import { FormattedMessage } from "../../webviewContract";
|
||||
|
||||
export interface ParseEvaluationResultContext {
|
||||
debugAdapterType: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../contract";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
|
||||
export interface EvaluationWatchService {
|
||||
createEvaluationWatcher(
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
import { observable, autorun, action } from "mobx";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../contract";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
import { hotClass } from "@hediet/node-reload";
|
||||
import { VsCodeDebuggerView } from "../VsCodeDebugger";
|
||||
import { VsCodeDebuggerView } from "../debugger/VsCodeDebuggerView";
|
||||
import { EvaluationEngine } from "./EvaluationEngine/EvaluationEngine";
|
||||
|
||||
@hotClass(module)
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
import { CompletionItem } from "./contract";
|
||||
import { observable, runInAction, action } from "mobx";
|
||||
|
||||
export class VsCodeDebugger {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly sessions = new Map<DebugSession, VsCodeDebugSession>();
|
||||
|
||||
private readonly _onDidStartDebugSession = new EventEmitter<{
|
||||
session: VsCodeDebugSession;
|
||||
}>();
|
||||
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
||||
|
||||
public getDebugSession(session: DebugSession): VsCodeDebugSession {
|
||||
let result = this.sessions.get(session);
|
||||
if (!result) {
|
||||
result = new VsCodeDebugSession(session);
|
||||
this.sessions.set(session, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.dispose.track([
|
||||
debug.onDidStartDebugSession(session => {
|
||||
const e = this.sessions.get(session)!;
|
||||
this._onDidStartDebugSession.emit({ session: e });
|
||||
}),
|
||||
debug.onDidTerminateDebugSession(session => {
|
||||
this.sessions.delete(session);
|
||||
}),
|
||||
debug.registerDebugAdapterTrackerFactory("*", {
|
||||
createDebugAdapterTracker: session => {
|
||||
const extendedSession = this.getDebugSession(session);
|
||||
return {
|
||||
onDidSendMessage: async msg => {
|
||||
type Message =
|
||||
| StoppedEvent
|
||||
| ThreadsResponse
|
||||
| ContinueLikeResponse;
|
||||
|
||||
interface ContinueLikeResponse {
|
||||
type: "response";
|
||||
command:
|
||||
| "continue"
|
||||
| "stepIn"
|
||||
| "stepOut"
|
||||
| "next";
|
||||
}
|
||||
|
||||
interface StoppedEvent {
|
||||
type: "event";
|
||||
event: "stopped";
|
||||
body: {
|
||||
threadId: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface ThreadsResponse {
|
||||
type: "response";
|
||||
command: "threads";
|
||||
success: boolean;
|
||||
body: {
|
||||
threads: ThreadInfo[];
|
||||
};
|
||||
}
|
||||
|
||||
interface ThreadInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const m = msg as Message;
|
||||
if (m.type === "event") {
|
||||
if (m.event === "stopped") {
|
||||
const threadId = m.body.threadId;
|
||||
const r = await extendedSession[
|
||||
"getStackTrace"
|
||||
]({
|
||||
threadId,
|
||||
startFrame: 0,
|
||||
levels: 1,
|
||||
});
|
||||
extendedSession["activeStackFrame"] =
|
||||
r.stackFrames.length > 0
|
||||
? r.stackFrames[0].id
|
||||
: undefined;
|
||||
}
|
||||
} else if (m.type === "response") {
|
||||
if (
|
||||
m.command === "continue" ||
|
||||
m.command === "next" ||
|
||||
m.command === "stepIn" ||
|
||||
m.command === "stepOut"
|
||||
) {
|
||||
extendedSession[
|
||||
"activeStackFrame"
|
||||
] = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
interface StackFrame {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class VsCodeDebugSession {
|
||||
@observable protected activeStackFrame: number | undefined;
|
||||
|
||||
constructor(public readonly session: DebugSession) {}
|
||||
|
||||
protected async getStackTrace(args: {
|
||||
threadId: number;
|
||||
startFrame?: number;
|
||||
levels?: number;
|
||||
}): Promise<{ totalFrames?: number; stackFrames: StackFrame[] }> {
|
||||
try {
|
||||
const reply = (await this.session.customRequest("stackTrace", {
|
||||
threadId: args.threadId,
|
||||
levels: args.levels,
|
||||
startFrame: args.startFrame || 0,
|
||||
})) as { totalFrames?: number; stackFrames: StackFrame[] };
|
||||
return reply;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public async getCompletions(args: {
|
||||
text: string;
|
||||
column: number;
|
||||
frameId: number | undefined;
|
||||
}): Promise<CompletionItem[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("completions", {
|
||||
text: args.text,
|
||||
frameId: args.frameId,
|
||||
column: args.column,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
return reply.targets;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given expression.
|
||||
* If context is "watch", long results are usually shortened.
|
||||
*/
|
||||
public async evaluate(args: {
|
||||
expression: string;
|
||||
frameId: number | undefined;
|
||||
context: "watch" | "repl" | "copy";
|
||||
}): Promise<{ result: string }> {
|
||||
const reply = await this.session.customRequest("evaluate", {
|
||||
expression: args.expression,
|
||||
frameId: args.frameId,
|
||||
|
||||
context: args.context,
|
||||
});
|
||||
return { result: reply.result };
|
||||
}
|
||||
}
|
||||
|
||||
export class VsCodeDebuggerView {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
@observable private _activeDebugSession: VsCodeDebugSession | undefined;
|
||||
|
||||
public get activeDebugSession(): VsCodeDebugSession | undefined {
|
||||
return this._activeDebugSession;
|
||||
}
|
||||
|
||||
public get activeFrameId(): number | undefined {
|
||||
if (!this._activeDebugSession) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._activeDebugSession["activeStackFrame"];
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private vsCodeDebugger: VsCodeDebugger) {
|
||||
this.dispose.track(
|
||||
debug.onDidChangeActiveDebugSession(activeSession => {
|
||||
this.updateActiveDebugSession(activeSession);
|
||||
})
|
||||
);
|
||||
this.updateActiveDebugSession(debug.activeDebugSession);
|
||||
}
|
||||
|
||||
@action
|
||||
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
|
||||
if (!activeSession) {
|
||||
this._activeDebugSession = undefined;
|
||||
} else {
|
||||
const s = this.vsCodeDebugger.getDebugSession(activeSession);
|
||||
this._activeDebugSession = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
extension/src/debugger/EnhancedDebugSession.ts
Normal file
70
extension/src/debugger/EnhancedDebugSession.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { DebugSession } from "vscode";
|
||||
import { CompletionItem } from "../webviewContract";
|
||||
import { observable } from "mobx";
|
||||
|
||||
export class EnhancedDebugSession {
|
||||
@observable protected activeStackFrame: number | undefined;
|
||||
|
||||
constructor(public readonly session: DebugSession) {}
|
||||
|
||||
protected async getStackTrace(args: {
|
||||
threadId: number;
|
||||
startFrame?: number;
|
||||
levels?: number;
|
||||
}): Promise<{ totalFrames?: number; stackFrames: StackFrame[] }> {
|
||||
try {
|
||||
const reply = (await this.session.customRequest("stackTrace", {
|
||||
threadId: args.threadId,
|
||||
levels: args.levels,
|
||||
startFrame: args.startFrame || 0,
|
||||
})) as { totalFrames?: number; stackFrames: StackFrame[] };
|
||||
return reply;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public async getCompletions(args: {
|
||||
text: string;
|
||||
column: number;
|
||||
frameId: number | undefined;
|
||||
}): Promise<CompletionItem[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("completions", {
|
||||
text: args.text,
|
||||
frameId: args.frameId,
|
||||
column: args.column,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
return reply.targets;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given expression.
|
||||
* If context is "watch", long results are usually shortened.
|
||||
*/
|
||||
|
||||
public async evaluate(args: {
|
||||
expression: string;
|
||||
frameId: number | undefined;
|
||||
context: "watch" | "repl" | "copy";
|
||||
}): Promise<{ result: string }> {
|
||||
const reply = await this.session.customRequest("evaluate", {
|
||||
expression: args.expression,
|
||||
frameId: args.frameId,
|
||||
context: args.context,
|
||||
});
|
||||
return { result: reply.result };
|
||||
}
|
||||
}
|
||||
interface StackFrame {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
116
extension/src/debugger/VsCodeDebugger.ts
Normal file
116
extension/src/debugger/VsCodeDebugger.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
import { runInAction } from "mobx";
|
||||
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||
|
||||
/**
|
||||
* Decorates DebugSession instances and emits an event for new debug sessions.
|
||||
*/
|
||||
export class VsCodeDebugger {
|
||||
public readonly dispose = Disposable.fn();
|
||||
private readonly sessions = new Map<DebugSession, EnhancedDebugSession>();
|
||||
|
||||
private readonly _onDidStartDebugSession = new EventEmitter<{
|
||||
session: EnhancedDebugSession;
|
||||
}>();
|
||||
public readonly onDidStartDebugSession = this._onDidStartDebugSession.asEvent();
|
||||
|
||||
public getEnhancedDebugSession(
|
||||
session: DebugSession
|
||||
): EnhancedDebugSession {
|
||||
let result = this.sessions.get(session);
|
||||
if (!result) {
|
||||
result = new EnhancedDebugSession(session);
|
||||
this.sessions.set(session, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.dispose.track([
|
||||
debug.onDidStartDebugSession(session => {
|
||||
const e = this.sessions.get(session)!;
|
||||
this._onDidStartDebugSession.emit({ session: e });
|
||||
}),
|
||||
debug.onDidTerminateDebugSession(session => {
|
||||
this.sessions.delete(session);
|
||||
}),
|
||||
debug.registerDebugAdapterTrackerFactory("*", {
|
||||
createDebugAdapterTracker: session => {
|
||||
const extendedSession = this.getEnhancedDebugSession(
|
||||
session
|
||||
);
|
||||
return {
|
||||
onDidSendMessage: async msg => {
|
||||
type Message =
|
||||
| StoppedEvent
|
||||
| ThreadsResponse
|
||||
| ContinueLikeResponse;
|
||||
|
||||
interface ContinueLikeResponse {
|
||||
type: "response";
|
||||
command:
|
||||
| "continue"
|
||||
| "stepIn"
|
||||
| "stepOut"
|
||||
| "next";
|
||||
}
|
||||
|
||||
interface StoppedEvent {
|
||||
type: "event";
|
||||
event: "stopped";
|
||||
body: {
|
||||
threadId: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface ThreadsResponse {
|
||||
type: "response";
|
||||
command: "threads";
|
||||
success: boolean;
|
||||
body: {
|
||||
threads: ThreadInfo[];
|
||||
};
|
||||
}
|
||||
|
||||
interface ThreadInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const m = msg as Message;
|
||||
if (m.type === "event") {
|
||||
if (m.event === "stopped") {
|
||||
const threadId = m.body.threadId;
|
||||
const r = await extendedSession[
|
||||
"getStackTrace"
|
||||
]({
|
||||
threadId,
|
||||
startFrame: 0,
|
||||
levels: 1,
|
||||
});
|
||||
extendedSession["activeStackFrame"] =
|
||||
r.stackFrames.length > 0
|
||||
? r.stackFrames[0].id
|
||||
: undefined;
|
||||
}
|
||||
} else if (m.type === "response") {
|
||||
if (
|
||||
m.command === "continue" ||
|
||||
m.command === "next" ||
|
||||
m.command === "stepIn" ||
|
||||
m.command === "stepOut"
|
||||
) {
|
||||
extendedSession[
|
||||
"activeStackFrame"
|
||||
] = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
48
extension/src/debugger/VsCodeDebuggerView.ts
Normal file
48
extension/src/debugger/VsCodeDebuggerView.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debug, DebugSession } from "vscode";
|
||||
import { observable, action } from "mobx";
|
||||
import { VsCodeDebugger } from "./VsCodeDebugger";
|
||||
import { EnhancedDebugSession } from "./EnhancedDebugSession";
|
||||
|
||||
/**
|
||||
* Tracks the currently active debug session and its currently active stack frame.
|
||||
* This is currently all faked. A proper API would be nice.
|
||||
*/
|
||||
export class VsCodeDebuggerView {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
@observable private _activeDebugSession: EnhancedDebugSession | undefined;
|
||||
|
||||
public get activeDebugSession(): EnhancedDebugSession | undefined {
|
||||
return this._activeDebugSession;
|
||||
}
|
||||
|
||||
public get activeFrameId(): number | undefined {
|
||||
if (!this._activeDebugSession) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._activeDebugSession["activeStackFrame"];
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private vsCodeDebugger: VsCodeDebugger) {
|
||||
this.dispose.track(
|
||||
debug.onDidChangeActiveDebugSession(activeSession => {
|
||||
this.updateActiveDebugSession(activeSession);
|
||||
})
|
||||
);
|
||||
this.updateActiveDebugSession(debug.activeDebugSession);
|
||||
}
|
||||
|
||||
@action
|
||||
private updateActiveDebugSession(activeSession: DebugSession | undefined) {
|
||||
if (!activeSession) {
|
||||
this._activeDebugSession = undefined;
|
||||
} else {
|
||||
const s = this.vsCodeDebugger.getEnhancedDebugSession(
|
||||
activeSession
|
||||
);
|
||||
this._activeDebugSession = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,11 @@ if (process.env.HOT_RELOAD) {
|
||||
}
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
import { WebViews } from "./WebViews";
|
||||
import { Server } from "./Server";
|
||||
import { InternalWebviewManager } from "./webview/InternalWebviewManager";
|
||||
import { WebviewServer } from "./webview/WebviewServer";
|
||||
import { Config } from "./Config";
|
||||
import { VsCodeDebugger, VsCodeDebuggerView } from "./VsCodeDebugger";
|
||||
import { VsCodeDebugger } from "./debugger/VsCodeDebugger";
|
||||
import { VsCodeDebuggerView } from "./debugger/VsCodeDebuggerView";
|
||||
import { EvaluationWatchServiceImpl } from "./EvaluationWatchService";
|
||||
import {
|
||||
ComposedEvaluationEngine,
|
||||
@@ -24,11 +25,18 @@ import {
|
||||
ConfiguredEvaluationEngine,
|
||||
} from "./EvaluationWatchService/EvaluationEngine";
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
hotRequireExportedFn(module, Extension, Extension => new Extension())
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
export class Extension {
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
private readonly config = new Config();
|
||||
|
||||
private readonly debugger = this.dispose.track(new VsCodeDebugger());
|
||||
private readonly debuggerView = this.dispose.track(
|
||||
new VsCodeDebuggerView(this.debugger)
|
||||
@@ -43,8 +51,10 @@ export class Extension {
|
||||
])
|
||||
);
|
||||
|
||||
private readonly server = new Server(this.dataSource, this.config);
|
||||
private readonly views = this.dispose.track(new WebViews(this.server));
|
||||
private readonly server = new WebviewServer(this.dataSource, this.config);
|
||||
private readonly views = this.dispose.track(
|
||||
new InternalWebviewManager(this.server, this.config)
|
||||
);
|
||||
|
||||
constructor() {
|
||||
if (getReloadCount(module) > 0) {
|
||||
@@ -79,10 +89,10 @@ export class Extension {
|
||||
}
|
||||
|
||||
const connections = [...this.server.connections.values()];
|
||||
if (connections.length > 0) {
|
||||
const latestConnection =
|
||||
connections[connections.length - 1];
|
||||
const latestConnection =
|
||||
connections[connections.length - 1];
|
||||
|
||||
if (latestConnection) {
|
||||
latestConnection.setExpression(selectedText);
|
||||
} else {
|
||||
this.views.createNew(selectedText);
|
||||
@@ -92,11 +102,3 @@ export class Extension {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
hotRequireExportedFn(module, Extension, Extension => new Extension())
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
120
extension/src/utils/VsCodeSettings.ts
Normal file
120
extension/src/utils/VsCodeSettings.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Uri, workspace, ConfigurationTarget, Disposable } from "vscode";
|
||||
import { fromResource } from "mobx-utils";
|
||||
import { computed, runInAction } from "mobx";
|
||||
import { EventEmitter } from "@hediet/std/events";
|
||||
|
||||
export interface Serializer<T> {
|
||||
deserialize: (val: any) => T;
|
||||
serializer: (val: T) => any;
|
||||
}
|
||||
|
||||
export function serializerWithDefault<T>(defaultValue: T): Serializer<T> {
|
||||
return {
|
||||
deserialize: val => (val === undefined ? defaultValue : val),
|
||||
serializer: val => val,
|
||||
};
|
||||
}
|
||||
|
||||
export class VsCodeSetting<T> {
|
||||
public get T(): T {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public readonly serializer: Serializer<T>;
|
||||
public readonly scope: Uri | undefined;
|
||||
private readonly settingResource: VsCodeSettingResource;
|
||||
|
||||
public constructor(
|
||||
public readonly id: string,
|
||||
options: {
|
||||
serializer?: Serializer<T>;
|
||||
scope?: Uri;
|
||||
} = {}
|
||||
) {
|
||||
this.scope = options.scope;
|
||||
this.serializer = options.serializer || {
|
||||
deserialize: val => val,
|
||||
serializer: val => val,
|
||||
};
|
||||
this.settingResource = new VsCodeSettingResource(this.id, this.scope);
|
||||
}
|
||||
|
||||
public get(): T {
|
||||
const result = this.settingResource.value;
|
||||
return this.serializer.deserialize(result);
|
||||
}
|
||||
|
||||
public async set(value: T): Promise<void> {
|
||||
const value2 = this.serializer.serializer(value);
|
||||
const c = workspace.getConfiguration(undefined, this.scope);
|
||||
const result = c.inspect(this.id);
|
||||
let target: ConfigurationTarget;
|
||||
|
||||
if (
|
||||
result &&
|
||||
[result.workspaceFolderValue].some(i => i !== undefined)
|
||||
) {
|
||||
target = ConfigurationTarget.WorkspaceFolder;
|
||||
}
|
||||
if (result && [result.workspaceValue].some(i => i !== undefined)) {
|
||||
target = ConfigurationTarget.Workspace;
|
||||
} else {
|
||||
target = ConfigurationTarget.Global;
|
||||
}
|
||||
|
||||
await c.update(this.id, value2, target);
|
||||
}
|
||||
}
|
||||
|
||||
class VsCodeSettingResource {
|
||||
public static onConfigChange = new EventEmitter();
|
||||
|
||||
private subscription: Disposable | undefined;
|
||||
private readonly resource = fromResource<any>(
|
||||
update => {
|
||||
update(this.readValue());
|
||||
this.subscription = VsCodeSettingResource.onConfigChange.sub(() => {
|
||||
update(this.readValue());
|
||||
});
|
||||
},
|
||||
() => this.subscription!.dispose(),
|
||||
this.readValue()
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly id: string,
|
||||
private readonly scope: Uri | undefined
|
||||
) {}
|
||||
|
||||
private readValue(): any {
|
||||
const val = workspace
|
||||
.getConfiguration(undefined, this.scope)
|
||||
.get(this.id);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* This improves change detection.
|
||||
*/
|
||||
private readonly stringifiedSettingValue = computed(
|
||||
() => JSON.stringify(this.resource.current()),
|
||||
{
|
||||
name: `VsCodeSettingResource[${this.id}].value`,
|
||||
context: this,
|
||||
}
|
||||
);
|
||||
|
||||
public get value() {
|
||||
const v = this.stringifiedSettingValue.get();
|
||||
if (v === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(v);
|
||||
}
|
||||
}
|
||||
|
||||
workspace.onDidChangeConfiguration(() => {
|
||||
runInAction("Update Configuration", () => {
|
||||
VsCodeSettingResource.onConfigChange.emit();
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,23 @@
|
||||
import { window, ViewColumn, WebviewPanel } from "vscode";
|
||||
import { Server } from "./Server";
|
||||
import { WebviewServer } from "./WebviewServer";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { WindowWithWebviewData } from "../webviewContract";
|
||||
import { Config } from "../Config";
|
||||
|
||||
export const debugVisualizer = "debugVisualizer";
|
||||
|
||||
export class WebViews {
|
||||
private readonly debugVisualizations = new Map<WebviewPanel, WebView>();
|
||||
export class InternalWebviewManager {
|
||||
private readonly openedWebviews = new Map<
|
||||
WebviewPanel,
|
||||
DebugVisualizerWebview
|
||||
>();
|
||||
|
||||
public readonly dispose = Disposable.fn();
|
||||
|
||||
constructor(private readonly server: Server) {
|
||||
constructor(
|
||||
private readonly server: WebviewServer,
|
||||
private readonly config: Config
|
||||
) {
|
||||
this.dispose.track(
|
||||
window.registerWebviewPanelSerializer(debugVisualizer, {
|
||||
deserializeWebviewPanel: async (panel, state) => {
|
||||
@@ -20,7 +28,7 @@ export class WebViews {
|
||||
|
||||
this.dispose.track({
|
||||
dispose: () => {
|
||||
for (const panel of this.debugVisualizations.keys()) {
|
||||
for (const panel of this.openedWebviews.keys()) {
|
||||
panel.dispose();
|
||||
}
|
||||
},
|
||||
@@ -28,7 +36,7 @@ export class WebViews {
|
||||
}
|
||||
|
||||
public createNew(expression: string | undefined = undefined) {
|
||||
const panel = window.createWebviewPanel(
|
||||
const webviewPanel = window.createWebviewPanel(
|
||||
debugVisualizer,
|
||||
"Debug Visualizer",
|
||||
ViewColumn.Two,
|
||||
@@ -44,33 +52,38 @@ export class WebViews {
|
||||
}
|
||||
);
|
||||
|
||||
this.setupView(panel, expression);
|
||||
this.initializeView(webviewPanel, expression);
|
||||
}
|
||||
|
||||
public restore(webviewPanel: WebviewPanel) {
|
||||
this.setupView(webviewPanel);
|
||||
private restore(webviewPanel: WebviewPanel) {
|
||||
this.initializeView(webviewPanel);
|
||||
}
|
||||
|
||||
private setupView(
|
||||
private initializeView(
|
||||
webviewPanel: WebviewPanel,
|
||||
expression: string | undefined = undefined
|
||||
) {
|
||||
webviewPanel.webview.html = getWebviewHtml(this.server, expression);
|
||||
const view = new WebView(webviewPanel);
|
||||
this.debugVisualizations.set(webviewPanel, view);
|
||||
webviewPanel.webview.html = getDebugVisualizerWebviewHtml(
|
||||
this.server,
|
||||
expression,
|
||||
this.config
|
||||
);
|
||||
const view = new DebugVisualizerWebview(webviewPanel);
|
||||
this.openedWebviews.set(webviewPanel, view);
|
||||
webviewPanel.onDidDispose(() => {
|
||||
this.debugVisualizations.delete(webviewPanel);
|
||||
this.openedWebviews.delete(webviewPanel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WebView {
|
||||
export class DebugVisualizerWebview {
|
||||
constructor(private readonly webviewPanel: WebviewPanel) {}
|
||||
}
|
||||
|
||||
export function getWebviewHtml(
|
||||
server: Server,
|
||||
expression: string | undefined = undefined
|
||||
function getDebugVisualizerWebviewHtml(
|
||||
server: WebviewServer,
|
||||
initialExpression: string | undefined = undefined,
|
||||
config: Config
|
||||
) {
|
||||
const isDev = !!process.env.USE_DEV_UI;
|
||||
return `
|
||||
@@ -86,12 +99,15 @@ export function getWebviewHtml(
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.webViewData = ${JSON.stringify({
|
||||
serverSecret: server.secret,
|
||||
serverPort: server.port,
|
||||
publicPath: server.publicPath,
|
||||
expression,
|
||||
})};
|
||||
Object.assign(window, ${JSON.stringify({
|
||||
webviewData: {
|
||||
serverSecret: server.secret,
|
||||
serverPort: server.port,
|
||||
publicPath: server.publicPath,
|
||||
expression: initialExpression,
|
||||
theme: config.theme,
|
||||
},
|
||||
} as WindowWithWebviewData)});
|
||||
const api = window.VsCodeApi = acquireVsCodeApi();
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window.frames[0]) {
|
||||
@@ -109,10 +125,13 @@ export function getWebviewHtml(
|
||||
|
||||
${
|
||||
isDev
|
||||
? `<iframe sandbox="allow-top-navigation allow-scripts allow-same-origin allow-popups allow-pointer-lock allow-forms" src="${server.getIndexUrl(
|
||||
{ mode: "webViewIFrame", expression }
|
||||
? `<iframe sandbox="allow-top-navigation allow-scripts allow-same-origin allow-popups allow-pointer-lock allow-forms" src="${server.getWebviewPageUrl(
|
||||
{
|
||||
mode: "webviewIFrame",
|
||||
expression: initialExpression,
|
||||
}
|
||||
)}"></iframe>`
|
||||
: `<script type="text/javascript" src="${server.mainBundleUrl}"></script>`
|
||||
: `<script type="text/javascript" src="${server.webviewBundleUrl}"></script>`
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { debugVisualizerUIContract } from "./contract";
|
||||
import { webviewContract } from "../webviewContract";
|
||||
import { ConsoleRpcLogger, RequestHandlingError } from "@hediet/typed-json-rpc";
|
||||
import {
|
||||
EvaluationWatcher,
|
||||
EvaluationWatchService,
|
||||
} from "./EvaluationWatchService";
|
||||
} from "../EvaluationWatchService";
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import { observable, autorun } from "mobx";
|
||||
import { Server } from "./Server";
|
||||
import { WebviewServer } from "./WebviewServer";
|
||||
import * as open from "open";
|
||||
import chromeLauncher = require("chrome-launcher");
|
||||
import { Config } from "./Config";
|
||||
import { Config } from "../Config";
|
||||
|
||||
export class ClientConnection {
|
||||
export class WebviewConnection {
|
||||
public readonly dispose = Disposable.fn();
|
||||
@observable
|
||||
private watcher: EvaluationWatcher | undefined = undefined;
|
||||
|
||||
private readonly client: typeof debugVisualizerUIContract["TClientInterface"];
|
||||
private readonly client: typeof webviewContract["TClientInterface"];
|
||||
|
||||
constructor(
|
||||
evaluationWatchService: EvaluationWatchService,
|
||||
stream: WebSocketStream,
|
||||
server: Server,
|
||||
server: WebviewServer,
|
||||
config: Config,
|
||||
serverSecret: string
|
||||
) {
|
||||
@@ -34,10 +34,7 @@ export class ClientConnection {
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
client,
|
||||
channel,
|
||||
} = debugVisualizerUIContract.registerServerToStream(
|
||||
const { client, channel } = webviewContract.registerServerToStream(
|
||||
stream,
|
||||
new ConsoleRpcLogger(),
|
||||
{
|
||||
@@ -76,7 +73,7 @@ export class ClientConnection {
|
||||
openInBrowser: async ({}) => {
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
const url = server.getIndexUrl({
|
||||
const url = server.getWebviewPageUrl({
|
||||
mode: "standalone",
|
||||
expression: this.watcher
|
||||
? this.watcher.expression
|
||||
@@ -141,6 +138,10 @@ export class ClientConnection {
|
||||
public setExpression(expression: string) {
|
||||
this.client.setExpression({ expression });
|
||||
}
|
||||
|
||||
public setTheme(theme: "light" | "dark"): void {
|
||||
this.client.setTheme({ theme });
|
||||
}
|
||||
}
|
||||
|
||||
async function launchChrome(url: string): Promise<boolean> {
|
||||
@@ -1,22 +1,27 @@
|
||||
import { WebSocketStream } from "@hediet/typed-json-rpc-websocket";
|
||||
import { AddressInfo } from "net";
|
||||
import WebSocket = require("ws");
|
||||
import { ClientConnection } from "./ClientConnection";
|
||||
import { WebviewConnection } from "./WebviewConnection";
|
||||
import * as express from "express";
|
||||
import * as http from "http";
|
||||
import * as serveStatic from "serve-static";
|
||||
import { Config } from "./Config";
|
||||
import { Config } from "../Config";
|
||||
import cryptoRandomString = require("crypto-random-string");
|
||||
import { distPath } from "debug-visualizer-webview";
|
||||
import { EvaluationWatchService } from "./EvaluationWatchService/EvaluationWatchService";
|
||||
import { EvaluationWatchService } from "../EvaluationWatchService/EvaluationWatchService";
|
||||
import { URLSearchParams } from "url";
|
||||
import { autorun, reaction } from "mobx";
|
||||
|
||||
export class Server {
|
||||
export class WebviewServer {
|
||||
private readonly server: http.Server;
|
||||
public readonly secret = cryptoRandomString({ length: 20 });
|
||||
public readonly secret = cryptoRandomString({ length: 30 });
|
||||
|
||||
public readonly connections = new Set<ClientConnection>();
|
||||
public readonly connections = new Set<WebviewConnection>();
|
||||
|
||||
constructor(dataSource: EvaluationWatchService, config: Config) {
|
||||
constructor(
|
||||
dataSource: EvaluationWatchService,
|
||||
private readonly config: Config
|
||||
) {
|
||||
const app = express();
|
||||
|
||||
app.use(serveStatic(distPath));
|
||||
@@ -32,7 +37,7 @@ export class Server {
|
||||
const wss = new WebSocket.Server({ server: this.server });
|
||||
wss.on("connection", async ws => {
|
||||
const stream = new WebSocketStream(ws);
|
||||
const c = new ClientConnection(
|
||||
const c = new WebviewConnection(
|
||||
dataSource,
|
||||
stream,
|
||||
this,
|
||||
@@ -43,27 +48,42 @@ export class Server {
|
||||
await stream.onClosed;
|
||||
this.connections.delete(c);
|
||||
});
|
||||
|
||||
reaction(
|
||||
() => config.theme,
|
||||
theme => {
|
||||
for (const c of this.connections) {
|
||||
c.setTheme(theme);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public getIndexUrl(args: {
|
||||
public getWebviewPageUrl(args: {
|
||||
expression?: string;
|
||||
mode: "standalone" | "webViewIFrame";
|
||||
mode: "standalone" | "webviewIFrame";
|
||||
}): string {
|
||||
const port = process.env.USE_DEV_UI ? 8080 : this.port;
|
||||
const expr =
|
||||
args.expression !== undefined
|
||||
? `&expression=${encodeURIComponent(args.expression)}`
|
||||
: "";
|
||||
const inWebView =
|
||||
args.mode === "standalone" ? "" : "&mode=webViewIFrame";
|
||||
return `http://localhost:${port}/index.html?serverPort=${this.port}&serverSecret=${this.secret}${inWebView}${expr}`;
|
||||
const port = process.env.USE_DEV_UI ? 8081 : this.port;
|
||||
const params: Record<string, string> = {
|
||||
serverPort: this.port.toString(),
|
||||
serverSecret: this.secret,
|
||||
mode: args.mode,
|
||||
theme: this.config.theme,
|
||||
};
|
||||
if (args.expression !== undefined) {
|
||||
params.expression = args.expression;
|
||||
}
|
||||
|
||||
return `http://localhost:${port}/index.html?${new URLSearchParams(
|
||||
params
|
||||
).toString()}`;
|
||||
}
|
||||
|
||||
public get publicPath(): string {
|
||||
return `http://localhost:${this.port}/`;
|
||||
}
|
||||
|
||||
public get mainBundleUrl(): string {
|
||||
public get webviewBundleUrl(): string {
|
||||
return `${this.publicPath}main.js`;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,83 @@ import {
|
||||
DataExtractorId,
|
||||
} from "@hediet/debug-visualizer-data-extraction";
|
||||
|
||||
interface WebviewConfig {
|
||||
serverSecret: string;
|
||||
serverPort: number;
|
||||
publicPath: string;
|
||||
expression?: string;
|
||||
theme: "light" | "dark";
|
||||
}
|
||||
|
||||
export interface WebviewUrlParams {
|
||||
serverPort: string;
|
||||
serverSecret: string;
|
||||
mode: "standalone" | "webviewIFrame";
|
||||
expression: string;
|
||||
theme: "light" | "dark";
|
||||
}
|
||||
|
||||
export interface WindowWithWebviewData {
|
||||
webviewData?: WebviewConfig;
|
||||
}
|
||||
|
||||
export const webviewContract = contract({
|
||||
client: {
|
||||
updateState: notificationContract({
|
||||
params: types.type({
|
||||
newState: unchecked<DataExtractionState>(),
|
||||
}),
|
||||
}),
|
||||
updateLanguageId: notificationContract({
|
||||
params: types.type({
|
||||
languageId: types.union([types.null, types.string]),
|
||||
}),
|
||||
}),
|
||||
setExpression: requestContract({
|
||||
params: types.type({
|
||||
expression: types.string,
|
||||
}),
|
||||
}),
|
||||
setTheme: notificationContract({
|
||||
params: types.type({
|
||||
theme: types.union([
|
||||
types.literal("light"),
|
||||
types.literal("dark"),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
server: {
|
||||
authenticate: requestContract({
|
||||
params: types.type({
|
||||
secret: types.string,
|
||||
}),
|
||||
}),
|
||||
setPreferredDataExtractor: notificationContract({
|
||||
params: types.type({
|
||||
dataExtractorId: unchecked<DataExtractorId>(),
|
||||
}),
|
||||
}),
|
||||
setExpression: notificationContract({
|
||||
params: types.type({
|
||||
newExpression: types.string,
|
||||
}),
|
||||
}),
|
||||
refresh: notificationContract({}),
|
||||
openInBrowser: notificationContract({}),
|
||||
|
||||
getCompletions: requestContract({
|
||||
params: types.type({
|
||||
text: types.string,
|
||||
column: types.number,
|
||||
}),
|
||||
result: types.type({
|
||||
completions: types.array(unchecked<CompletionItem>()),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
function unchecked<T>(): types.Type<T, any, unknown> {
|
||||
return new types.Type<T, T, unknown>(
|
||||
"unchecked",
|
||||
@@ -92,52 +169,3 @@ export interface CompletionItem {
|
||||
*/
|
||||
length?: number;
|
||||
}
|
||||
|
||||
export const debugVisualizerUIContract = contract({
|
||||
client: {
|
||||
updateState: notificationContract({
|
||||
params: types.type({
|
||||
newState: unchecked<DataExtractionState>(),
|
||||
}),
|
||||
}),
|
||||
updateLanguageId: notificationContract({
|
||||
params: types.type({
|
||||
languageId: types.union([types.null, types.string]),
|
||||
}),
|
||||
}),
|
||||
setExpression: requestContract({
|
||||
params: types.type({
|
||||
expression: types.string,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
server: {
|
||||
authenticate: requestContract({
|
||||
params: types.type({
|
||||
secret: types.string,
|
||||
}),
|
||||
}),
|
||||
setPreferredDataExtractor: notificationContract({
|
||||
params: types.type({
|
||||
dataExtractorId: unchecked<DataExtractorId>(),
|
||||
}),
|
||||
}),
|
||||
setExpression: notificationContract({
|
||||
params: types.type({
|
||||
newExpression: types.string,
|
||||
}),
|
||||
}),
|
||||
refresh: notificationContract({}),
|
||||
openInBrowser: notificationContract({}),
|
||||
|
||||
getCompletions: requestContract({
|
||||
params: types.type({
|
||||
text: types.string,
|
||||
column: types.number,
|
||||
}),
|
||||
result: types.type({
|
||||
completions: types.array(unchecked<CompletionItem>()),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user