дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-13 21:25:47 +02:00
Порівняти коміти
2
Коміти
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
953b51b8c6 | ||
|
|
70b953c77a |
No files matched your search
@@ -307,7 +307,7 @@ describe("variables the adapter computes after the stop", () => {
|
||||
expect(await screen.findByText("Collecting data…")).toBeTruthy();
|
||||
adapterData.library = "com.example.library.Library@1";
|
||||
act(() => handleDebugEvent(1, { kind: "invalidated", areas: ["variables"] }));
|
||||
const value = await screen.findByText("com.example.library.Library@1");
|
||||
const value = await screen.findByText("{Library@1}");
|
||||
expect(screen.queryByText("Collecting data…")).toBeNull();
|
||||
expect(value.closest("[role=treeitem]")?.querySelector("[class*=changed]")).toBeNull();
|
||||
});
|
||||
@@ -348,7 +348,7 @@ describe("variables the adapter computes after the stop", () => {
|
||||
await screen.findByText("Collecting data…");
|
||||
adapterData.library = "Library@2";
|
||||
await act(() => setVariableValue({ container: 1, name: "count", evaluateName: "count" }, "4", session));
|
||||
expect(await screen.findByText("Library@2")).toBeTruthy();
|
||||
expect(await screen.findByText("{Library@2}")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does nothing for an invalidation while the program runs", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 { classifyValue, identifiersIn, inlineValue, shownValue, sortVariables } from "../values";
|
||||
import { inlineValuesFor } from "../editor/execution";
|
||||
import { frameLabel, groupFrames, isLibraryFrame } from "../frames";
|
||||
import type { StackFrame } from "@/platform/ipc";
|
||||
@@ -150,3 +150,25 @@ describe("selectionExpression", () => {
|
||||
expect(selectionExpression(multi)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("shown values", () => {
|
||||
it.each([
|
||||
["String[0]@9", "String[]", { label: "{String[0]@9}", text: "[]" }],
|
||||
["int[3]@12", "int[]", { label: "{int[3]@12}", text: "" }],
|
||||
["HashMap@10 size=1", "HashMap", { label: "{HashMap@10}", text: "size = 1" }],
|
||||
["ArrayList@15 size=0", "java.util.ArrayList", { label: "{ArrayList@15}", text: "size = 0" }],
|
||||
["Db@21", "Db", { label: "{Db@21}", text: "" }],
|
||||
["com.example.Library@1", "Library", { label: "{Library@1}", text: "" }],
|
||||
['HashMap$Node@31 "a":"b"', "HashMap$Node", { label: null, text: '"a" -> "b"', entry: true }],
|
||||
['HashMap$Node@32 "a:b":"c"', "HashMap$Node", { label: null, text: '"a:b" -> "c"', entry: true }],
|
||||
['TreeMap$Entry@33 1:"x"', "TreeMap$Entry", { label: null, text: '1 -> "x"', entry: true }],
|
||||
['"hello"', "String", { label: null, text: '"hello"' }],
|
||||
["42", "int", { label: "{int}", text: "42" }],
|
||||
// Other debuggers' values keep their text and type.
|
||||
["Foo@1", "Bar", { label: "{Bar}", text: "Foo@1" }],
|
||||
["{'a': 1}", "dict", { label: "{dict}", text: "{'a': 1}" }],
|
||||
["0x1000", null, { label: null, text: "0x1000" }],
|
||||
])("%s {%s}", (value, type, shown) => {
|
||||
expect(shownValue(value, type)).toEqual(shown);
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,7 @@ import { showHint } from "@/editor/lsp/hint";
|
||||
import { activeSession, useExecutionPoint } from "../state";
|
||||
import { onWorkflowSettingsChange, workflowSettings } from "@/platform/workflowSettings";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { identifiersIn, inlineValue } from "../values";
|
||||
import { identifiersIn, inlineValue, shownValue } from "../values";
|
||||
import { valueHint } from "./valueTooltip";
|
||||
import styles from "./editor.module.css";
|
||||
|
||||
@@ -113,11 +113,13 @@ export function inlineValuesFor(
|
||||
evaluateName: id,
|
||||
})
|
||||
: raw;
|
||||
const shown = shownValue(variable.value, variable.typeName);
|
||||
const value = shown.text || shown.label || variable.value;
|
||||
parts.push({
|
||||
name: id,
|
||||
text: `${id}: ${inlineValue(variable.value, 40)}`,
|
||||
text: `${id}: ${inlineValue(value, 40)}`,
|
||||
variable,
|
||||
truncated: variable.value.length > 40,
|
||||
truncated: value.length > 40,
|
||||
});
|
||||
if (parts.length >= MAX_PER_LINE) break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { showHint } from "@/editor/lsp/hint";
|
||||
import { workflowSettings } from "@/platform/workflowSettings";
|
||||
import { navigationModifierHeld } from "@/platform/navigationModifier";
|
||||
import { activeSession, evaluate } from "../state";
|
||||
import { inlineValue } from "../values";
|
||||
import { inlineValue, shownValue } from "../values";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { iconMarkup, selectableText } from "@/ui";
|
||||
import { mountValueTree } from "../ui/ValueHintTree";
|
||||
@@ -70,13 +70,19 @@ export function valueHint(
|
||||
const name = document.createElement("span");
|
||||
name.className = styles.valueName!;
|
||||
name.textContent = `${expression} = `;
|
||||
dom.append(name, max === null ? value.value : inlineValue(value.value, max));
|
||||
if (value.typeName) {
|
||||
const type = document.createElement("span");
|
||||
type.className = styles.valueType!;
|
||||
type.textContent = ` {${value.typeName}}`;
|
||||
dom.append(type);
|
||||
}
|
||||
const shown = shownValue(value.value, value.typeName);
|
||||
// A JVM object's `{Type@id}` leads its value; other types follow it.
|
||||
const leading = shown.label?.includes("@") ?? false;
|
||||
const type = (text: string) => {
|
||||
const span = document.createElement("span");
|
||||
span.className = styles.valueType!;
|
||||
span.textContent = text;
|
||||
return span;
|
||||
};
|
||||
dom.append(name);
|
||||
if (leading) dom.append(type(`${shown.label} `));
|
||||
dom.append(max === null ? shown.text : inlineValue(shown.text, max));
|
||||
if (shown.label && !leading) dom.append(type(` ${shown.label}`));
|
||||
return { dom, destroy: () => tree?.unmount() };
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { addFieldWatchpoint } from "../breakpoints/special";
|
||||
import { methodAt } from "../breakpoints/symbols";
|
||||
import { activeSession, addWatch, removeWatch, revealFrame, sessionSupports, setVariableValue } from "../state";
|
||||
import { blurDebugView, focusDebugView } from "./focusedView";
|
||||
import { classifyValue, oneLine, sortVariables } from "../values";
|
||||
import { classifyValue, oneLine, shownValue, sortVariables } from "../values";
|
||||
import { valueText } from "../valueText";
|
||||
import { workflowSettings } from "@/platform/workflowSettings";
|
||||
import styles from "./VariablesView.module.css";
|
||||
@@ -486,20 +486,25 @@ function VarRow({
|
||||
</span>
|
||||
);
|
||||
const kind = node.error ? null : classifyValue(node.value, node.typeName);
|
||||
const shown = shownValue(node.value, node.typeName);
|
||||
return (
|
||||
<span className={styles.row}>
|
||||
<Icon name={nodeIcon(node)} size={16} className={styles.nodeIcon} />
|
||||
<span className={clsx(styles.name, node.kind === "var" && styles.varName, changed && styles.changed)}>
|
||||
{node.name}
|
||||
</span>
|
||||
<span className={styles.eq}>=</span>
|
||||
{!shown.entry && (
|
||||
<>
|
||||
<span className={clsx(styles.name, node.kind === "var" && styles.varName, changed && styles.changed)}>
|
||||
{node.name}
|
||||
</span>
|
||||
<span className={styles.eq}>=</span>
|
||||
</>
|
||||
)}
|
||||
{node.error ? (
|
||||
<span className={styles.error}>{oneLine(node.error)}</span>
|
||||
) : (
|
||||
<>
|
||||
{node.typeName && <span className={styles.type}>{`{${node.typeName}}`}</span>}
|
||||
{shown.label && <span className={styles.type}>{shown.label}</span>}
|
||||
<span ref={valueRef} className={clsx(styles.value, kind && styles[kind])} title={node.value}>
|
||||
{oneLine(node.value)}
|
||||
{oneLine(shown.text)}
|
||||
</span>
|
||||
{overflow && (
|
||||
<button
|
||||
|
||||
@@ -69,3 +69,35 @@ export function sortVariables<T extends { name: string }>(vars: readonly T[]): T
|
||||
if (vars.every((v) => INDEX.test(v.name))) return [...vars];
|
||||
return [...vars].sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: "base" }));
|
||||
}
|
||||
|
||||
/** How a variable's type and value read in the Variables view: the braced label before the value, then the value. */
|
||||
export interface ShownValue {
|
||||
label: string | null;
|
||||
text: string;
|
||||
/** A map entry: the row shows `key -> value` alone, without its index. */
|
||||
entry?: true;
|
||||
}
|
||||
|
||||
// `HashMap@10 size=1`, `String[0]@9`: a JVM object as the Java debugger writes it (type, then the object id).
|
||||
const JVM_OBJECT = /^([\w$.]+(?:\[\d*\])*)@(\d+)(?:\s+(.*))?$/s;
|
||||
// A map entry's key, at the start of `"a":"b"` or `1:"b"`.
|
||||
const ENTRY_KEY = /^("(?:[^"\\]|\\.)*"|[^:"]+?):(.*)$/s;
|
||||
|
||||
/**
|
||||
* JVM objects read as `{HashMap@10} size = 1` and a map entry as `"a" -> "b"`; strings show without
|
||||
* a type. Values in other forms keep the adapter's text under `{type}`.
|
||||
*/
|
||||
export function shownValue(value: string, typeName: string | null | undefined): ShownValue {
|
||||
const plain = { label: typeName ? `{${typeName}}` : null, text: value };
|
||||
if (!typeName) return plain;
|
||||
if (typeName === "String" && /^".*"$/s.test(value)) return { label: null, text: value };
|
||||
const object = JVM_OBJECT.exec(value);
|
||||
if (!object) return plain;
|
||||
const [, head, id, rest = ""] = object;
|
||||
const simple = (t: string) => t.replace(/\[\d*\]/g, "[]").replace(/^.*\./, "");
|
||||
if (simple(head!) !== simple(typeName)) return plain;
|
||||
const entry = /(?:^|\$)\w*(?:Entry|Node)$/.test(typeName) ? ENTRY_KEY.exec(rest) : null;
|
||||
if (entry) return { label: null, text: `${entry[1]!.trim()} -> ${entry[2]!.trim()}`, entry: true };
|
||||
const text = rest.replace(/\b(size|length)=(\d+)/g, "$1 = $2") || (/\[0\]/.test(head!) ? "[]" : "");
|
||||
return { label: `{${head!.replace(/^.*\./, "")}@${id}}`, text };
|
||||
}
|
||||
Reference in new issue
Заблокувати користувача