дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
feat(debugger): Show the value of the selected code while paused (#173)
Settings › Debugger › Data Views gains Show value tooltip on code selection (on by default). While the debugger is paused, selecting an expression on one line shows its value once the selection settles, with objects expandable like the hover tooltip.
Цей коміт міститься в:
1 джерело
1c46a1cd04
коміт
7cfbf6c47b
8 files changed
+92
-3
No files matched your search
@@ -1,4 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { EditorSelection, EditorState } from "@codemirror/state";
|
||||
import { selectionExpression } from "../editor/valueTooltip";
|
||||
import { classifyValue, identifiersIn, inlineValue, sortVariables } from "../values";
|
||||
import { inlineValuesFor } from "../editor/execution";
|
||||
import { frameLabel, groupFrames, isLibraryFrame } from "../frames";
|
||||
@@ -109,3 +111,22 @@ describe("sortVariables", () => {
|
||||
expect(names(sortVariables(items))).toEqual(["[0]", "[1]", "[10]", "[2]"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("selectionExpression", () => {
|
||||
const at = (doc: string, from: number, to: number) =>
|
||||
selectionExpression(EditorState.create({ doc, selection: EditorSelection.single(from, to) }));
|
||||
it("takes a one-line selection", () => {
|
||||
expect(at("total += item.price", 9, 19)).toEqual({ from: 9, text: "item.price" });
|
||||
});
|
||||
it("ignores empty, blank, multi-line and multiple selections", () => {
|
||||
expect(at("a + b", 2, 2)).toBeNull();
|
||||
expect(at("a b", 1, 4)).toBeNull();
|
||||
expect(at("a\nb", 0, 3)).toBeNull();
|
||||
const multi = EditorState.create({
|
||||
doc: "a + b",
|
||||
selection: EditorSelection.create([EditorSelection.range(0, 1), EditorSelection.range(4, 5)]),
|
||||
extensions: EditorState.allowMultipleSelections.of(true),
|
||||
});
|
||||
expect(selectionExpression(multi)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -3,9 +3,10 @@
|
||||
* while suspended, hovering an expression (or the selection under the mouse)
|
||||
* evaluates it in the selected frame.
|
||||
*/
|
||||
import type { Extension } from "@codemirror/state";
|
||||
import type { EditorState, Extension } from "@codemirror/state";
|
||||
import type { DebugSessionId, Variable } from "@/bindings";
|
||||
import { hoverTooltip, type EditorView } from "@codemirror/view";
|
||||
import { hoverTooltip, ViewPlugin, type EditorView, type ViewUpdate } from "@codemirror/view";
|
||||
import { showHint } from "@/editor/lsp/hint";
|
||||
import { workflowSettings } from "@/platform/workflowSettings";
|
||||
import { navigationModifierHeld } from "@/platform/navigationModifier";
|
||||
import { activeSession, evaluate } from "../state";
|
||||
@@ -109,3 +110,59 @@ export function valueTooltip(): Extension {
|
||||
{ hoverTime: BASE_HOVER },
|
||||
);
|
||||
}
|
||||
|
||||
const SELECTION_DELAY = 400;
|
||||
const MAX_SELECTION = 200;
|
||||
|
||||
/** The selected text when it can be evaluated: one non-blank line, not too long. */
|
||||
export function selectionExpression(state: EditorState): { from: number; text: string } | null {
|
||||
const sel = state.selection;
|
||||
if (sel.ranges.length !== 1 || sel.main.empty) return null;
|
||||
const text = state.sliceDoc(sel.main.from, sel.main.to).trim();
|
||||
if (!text || text.length > MAX_SELECTION || text.includes("\n")) return null;
|
||||
return { from: sel.main.from, text };
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings › Debugger › Data Views › "Show value tooltip on code selection": while
|
||||
* suspended, selecting an expression shows its value once the selection settles.
|
||||
*/
|
||||
export function selectionValueTooltip(): Extension {
|
||||
return ViewPlugin.fromClass(
|
||||
class {
|
||||
private timer: ReturnType<typeof setTimeout> | undefined;
|
||||
private seq = 0;
|
||||
constructor(readonly view: EditorView) {}
|
||||
|
||||
update(u: ViewUpdate) {
|
||||
if (!u.selectionSet) return;
|
||||
clearTimeout(this.timer);
|
||||
this.seq++;
|
||||
if (!u.transactions.some((tr) => tr.isUserEvent("select"))) return;
|
||||
const seq = this.seq;
|
||||
this.timer = setTimeout(() => void this.show(seq), SELECTION_DELAY);
|
||||
}
|
||||
|
||||
private async show(seq: number) {
|
||||
if (!workflowSettings().debugger.valueTooltipOnSelection) return;
|
||||
const session = activeSession();
|
||||
if (!session || session.status !== "paused") return;
|
||||
const expr = selectionExpression(this.view.state);
|
||||
if (!expr) return;
|
||||
let value;
|
||||
try {
|
||||
value = await evaluate(expr.text, "hover", session);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
// The selection moved on while evaluating.
|
||||
if (seq !== this.seq) return;
|
||||
showHint(this.view, () => valueHint(expr.text, value, session.id), { pos: expr.from, timeout: 60_000 });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
import { lazyComponent } from "@/ui";
|
||||
import { onIdeEvent } from "@/platform/ipc";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { valueTooltip } from "./editor/valueTooltip";
|
||||
import { selectionValueTooltip, valueTooltip } from "./editor/valueTooltip";
|
||||
import { registerEditorContribution } from "@/editor/api";
|
||||
import {
|
||||
registerMenuGroup,
|
||||
@@ -65,6 +65,7 @@ export function activate(): () => void {
|
||||
executionPoint(ctx),
|
||||
runToCursorInlay(ctx),
|
||||
valueTooltip(),
|
||||
selectionValueTooltip(),
|
||||
smartStepTargets(ctx),
|
||||
],
|
||||
}),
|
||||
|
||||
Reference in new issue
Заблокувати користувача