feat(debugger): Reload Changed Classes in the editor context menu (#1627)

Shown on a Java file while a debug session can reload classes (java-debug hot code replace); it runs the existing debug.reloadChangedClasses action.
This commit is contained in:
l0ng-ai
2026-11-06 05:35:43 +08:00
committed by GitHub
parent 7634ae81f1
commit b4bd0ec1e2
3 changed files with 21 additions and 2 deletions
@@ -30,7 +30,7 @@ vi.mock("@/workbench/contributions", async (importOriginal) => {
import type { DebugCapabilities, DebugSessionId } from "@/platform/ipc";
import { getContext } from "@/platform/context";
import { _resetI18nForTests, setLocale } from "@/platform/i18n";
import { hotSwapMessage, onHotCodeReplace, reloadChangedClasses } from "../hotswap";
import { hotSwapMenuItems, hotSwapMessage, onHotCodeReplace, reloadChangedClasses } from "../hotswap";
import { publishContext, useDebug, type DebugSession } from "../state";
function session(id: number, hotCodeReplace: boolean): DebugSession {
@@ -80,6 +80,13 @@ describe("HotSwap", () => {
expect(getContext().debugCanHotSwap).toBe(false);
});
it("offers the reload in the editor menu of a Java file while a session can reload classes", () => {
expect(hotSwapMenuItems(["/p/app/src/A.java"])).toEqual([{ kind: "action", action: "debug.reloadChangedClasses" }]);
expect(hotSwapMenuItems(["/p/app/pom.xml"])).toEqual([]);
useDebug.setState({ sessions: { 2: session(2, false) }, active: 2 });
expect(hotSwapMenuItems(["/p/app/src/A.java"])).toEqual([]);
});
it("builds, reloads in Java sessions and reports the count", async () => {
await reloadChangedClasses();
expect(ipc.javaBuildWorkspace).toHaveBeenCalledWith("/p/app");
+7
View File
@@ -9,6 +9,7 @@ import { call, commands, isTauri, type DebugEvent, type DebugSessionId } from "@
import { maybeEditorService, notifications } from "@/workbench/contributions";
import { isLive, useDebug, type DebugSession } from "./state";
import { tx } from "@/platform/i18n";
import type { MenuItem } from "@/ui/Menu";
const KEY = "debug.hotswap";
/** `BUILD_COMPLETE` events of our own build are not worth a prompt. */
@@ -18,6 +19,12 @@ export function hotSwapSessions(): DebugSession[] {
return Object.values(useDebug.getState().sessions).filter((s) => isLive(s) && !!s.caps?.hotCodeReplace);
}
/** Editor menu: Reload Changed Classes on a Java file while a debug session can reload classes. */
export function hotSwapMenuItems(paths: readonly string[]): MenuItem[] {
const java = paths.length === 1 && /\.java$/i.test(paths[0]!);
return java && hotSwapSessions().length ? [{ kind: "action", action: "debug.reloadChangedClasses" }] : [];
}
/** "3 classes", translated per count (the sentence around it is translated on its own). */
const classesText = (n: number) => (n === 1 ? tx`${n} class` : tx`${n} classes`);
+6 -1
View File
@@ -24,7 +24,7 @@ import { breakpointGutter } from "./editor/breakpointGutter";
import { executionPoint } from "./editor/execution";
import { runToCursorInlay } from "./editor/runToCursor";
import { installSmartStepPicking, smartStepTargets } from "./editor/smartStep";
import { onHotCodeReplace } from "./hotswap";
import { hotSwapMenuItems, onHotCodeReplace } from "./hotswap";
import {
activeSession,
handleDebugEvent,
@@ -68,6 +68,11 @@ export function activate(): () => void {
menus: ["editor"],
items: () => (activeSession()?.status === "paused" ? [{ kind: "action", action: "debug.evaluate" }] : []),
}),
registerMenuGroup({
id: "debugger.hotswap",
menus: ["editor"],
items: (_menu, { paths }) => hotSwapMenuItems(paths),
}),
// Stop (⌘F2) offers the running debug sessions beside run processes.
registerMenuGroup({
id: "debugger.stop",