Порівняти коміти

...
Автор SHA1 Повідомлення Дата
l0ng-ai 93458329c5 feat(debugger): Focus On Breakpoint toggle for the Threads & Variables tab
A breakpoint or exception hit always brought Threads & Variables to the
front, so watching program output in the Console meant switching back after
every hit. Right-clicking the Threads & Variables tab now offers Focus On
Breakpoint (on by default, a client setting debugger.variablesOnBreakpoint);
turned off, a hit keeps the tab the user is on. Finishing a step already kept
the current tab.
2026-11-14 23:34:04 +08:00
7 змінених файлів з 67 додано та 4 видалено

No files matched your search

+3
Переглянути файл
@@ -260,6 +260,8 @@ pub struct DebuggerSettings {
pub show_on_breakpoint: bool,
pub focus_app_on_breakpoint: bool,
pub hide_on_termination: bool,
/// A breakpoint or exception hit brings the Debug tool window's Threads & Variables tab to the front.
pub variables_on_breakpoint: bool,
pub breakpoints_any_file: bool,
/// While suspended, hovering the start of a line shows a Run to Cursor button.
pub run_to_cursor_inlay: bool,
@@ -285,6 +287,7 @@ impl Default for DebuggerSettings {
show_on_breakpoint: true,
focus_app_on_breakpoint: false,
hide_on_termination: false,
variables_on_breakpoint: true,
breakpoints_any_file: false,
run_to_cursor_inlay: true,
inline_values: true,
+2
Переглянути файл
@@ -4059,6 +4059,8 @@ export type DebuggerSettings = {
showOnBreakpoint?: boolean,
focusAppOnBreakpoint?: boolean,
hideOnTermination?: boolean,
/** A breakpoint or exception hit brings the Debug tool window's Threads & Variables tab to the front. */
variablesOnBreakpoint?: boolean,
breakpointsAnyFile?: boolean,
/** While suspended, hovering the start of a line shows a Run to Cursor button. */
runToCursorInlay?: boolean,
+27
Переглянути файл
@@ -71,6 +71,7 @@ import { setLocale } from "@/platform/i18n";
import { ToolWindowShownProvider } from "@/workbench/contributions";
import { handleDebugEvent, setVariableValue, useDebug, type DebugSession } from "../state";
import { DebugTabs, DebugToolWindow } from "../ui/DebugToolWindow";
import { hideContextMenu, useContextMenuStore } from "@/ui/Menu";
import { DebuggerOverlays } from "../ui/Overlays";
import { useOverlays } from "../ui/popoverStore";
@@ -240,6 +241,32 @@ describe("debug tool window", () => {
expect(console.getAttribute("aria-selected")).toBe("true");
});
it("keeps the chosen tab on a breakpoint hit when Focus On Breakpoint is off", () => {
const settings = useProject.getState().settings;
useDebug.setState({ sessions: { 1: { ...session, status: "running" } }, active: 1 });
render(<DebugToolWindow />);
const variables = screen.getByRole("tab", { name: "Threads & Variables" });
fireEvent.contextMenu(variables);
const item = useContextMenuStore.getState().menu?.items[0];
expect(item).toMatchObject({ label: "Focus On Breakpoint", checked: true });
act(() => hideContextMenu());
try {
act(() =>
useProject.setState({
settings: { debugger: { variablesOnBreakpoint: false } } as ReturnType<
typeof useProject.getState
>["settings"],
}),
);
const console = screen.getByRole("tab", { name: "Console" });
fireEvent.click(console);
act(() => useDebug.setState({ sessions: { 1: session } }));
expect(console.getAttribute("aria-selected")).toBe("true");
} finally {
act(() => useProject.setState({ settings }));
}
});
it("opens on Console for a running session, and on the view last picked when shown again", () => {
useDebug.setState({ sessions: { 1: { ...session, status: "running" } }, active: 1 });
const first = render(<DebugToolWindow />);
+30 -4
Переглянути файл
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useRef, useState, type KeyboardEvent } from "react"
import clsx from "clsx";
import { executeAction } from "@/platform/actions";
import { basename, useProject } from "@/platform/project";
import { call, commands, type Scope, type Variable } from "@/platform/ipc";
import { call, commands, isTauri, type Scope, type Variable } from "@/platform/ipc";
import {
EmptyState,
Icon,
@@ -109,6 +109,14 @@ type Tab = "variables" | "console" | "memory" | "network";
/** The view the user last picked; the tool window opens on it again (Console until one is picked). */
let chosenTab: Tab = "console";
/** Threads & Variables › Focus On Breakpoint: whether a breakpoint hit selects that tab. */
async function setVariablesOnBreakpoint(on: boolean) {
if (!isTauri()) return;
useProject.setState({
settings: await call(commands.settingsSet("global", "debugger.variablesOnBreakpoint", JSON.stringify(on))),
});
}
function statusText(s: DebugSession): string {
if (s.status === "terminated") return s.exitCode != null ? `Finished · exit code ${s.exitCode}` : "Finished";
if (s.status === "starting") return "Starting…";
@@ -160,12 +168,16 @@ export function DebugToolWindow() {
setView({
session: active.startedAt,
status: active.status,
// A breakpoint or exception brings Threads & Variables to the front; finishing a step keeps
// the tab the user is on (e.g. Console, to watch the output while stepping), and so does Memory.
// A breakpoint or exception brings Threads & Variables to the front unless its Focus On Breakpoint
// is off; finishing a step keeps the tab the user is on (e.g. Console, to watch the output while
// stepping), and so does Memory.
tab:
active.status === "starting"
? "console"
: active.status === "paused" && view.tab !== "memory" && active.stopReason !== "step"
: active.status === "paused" &&
view.tab !== "memory" &&
active.stopReason !== "step" &&
workflowSettings().debugger.variablesOnBreakpoint
? "variables"
: view.tab,
});
@@ -247,6 +259,20 @@ export function DebugToolWindow() {
chosenTab = id;
setView({ session: active.startedAt, status: active.status, tab: id });
}}
onContextMenu={
id === "variables"
? (e) => {
e.preventDefault();
showContextMenu(e, [
{
label: "Focus On Breakpoint",
checked: workflowSettings().debugger.variablesOnBreakpoint,
run: () => void setVariablesOnBreakpoint(!workflowSettings().debugger.variablesOnBreakpoint),
},
]);
}
: undefined
}
>
{tx(label)}
</button>
+1
Переглянути файл
@@ -75,6 +75,7 @@ export const CLIENT_SETTINGS_KEYS = [
"debugger.showOnBreakpoint",
"debugger.focusAppOnBreakpoint",
"debugger.hideOnTermination",
"debugger.variablesOnBreakpoint",
"debugger.runToCursorInlay",
"debugger.inlineValues",
"debugger.sortValues",
+1
Переглянути файл
@@ -7529,6 +7529,7 @@
"Current file": "当前文件",
"Uncommitted files": "未提交的文件",
"Threads & Variables": "线程和变量",
"Focus On Breakpoint": "命中断点时聚焦",
"Usage Limit": "用量上限",
"Dev Containers": "开发容器",
"Containers": "容器",
+3
Переглянути файл
@@ -97,6 +97,7 @@ export interface DebuggerSettingsValid {
showOnBreakpoint: boolean;
focusAppOnBreakpoint: boolean;
hideOnTermination: boolean;
variablesOnBreakpoint: boolean;
breakpointsAnyFile: boolean;
runToCursorInlay: boolean;
inlineValues: boolean;
@@ -290,6 +291,7 @@ export const DEFAULT_WORKFLOW: WorkflowSettings = {
showOnBreakpoint: true,
focusAppOnBreakpoint: false,
hideOnTermination: false,
variablesOnBreakpoint: true,
breakpointsAnyFile: false,
runToCursorInlay: true,
inlineValues: true,
@@ -449,6 +451,7 @@ export function validateDebugger(raw: unknown): DebuggerSettingsValid {
showOnBreakpoint: bool(v.showOnBreakpoint, d.showOnBreakpoint),
focusAppOnBreakpoint: bool(v.focusAppOnBreakpoint, d.focusAppOnBreakpoint),
hideOnTermination: bool(v.hideOnTermination, d.hideOnTermination),
variablesOnBreakpoint: bool(v.variablesOnBreakpoint, d.variablesOnBreakpoint),
breakpointsAnyFile: bool(v.breakpointsAnyFile, d.breakpointsAnyFile),
runToCursorInlay: bool(v.runToCursorInlay, d.runToCursorInlay),
inlineValues: bool(v.inlineValues, d.inlineValues),