diff --git a/src/features/debugger/__tests__/watchStorage.test.ts b/src/features/debugger/__tests__/watchStorage.test.ts new file mode 100644 index 000000000..48386436f --- /dev/null +++ b/src/features/debugger/__tests__/watchStorage.test.ts @@ -0,0 +1,49 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { addWatch, loadWatches, useDebug } from "../state"; + +const SHARED = "openidea.debugger.watches"; +const of = (root: string) => `${SHARED}:${root}`; +const watches = () => useDebug.getState().watches; + +beforeEach(() => { + localStorage.clear(); + loadWatches(null); +}); + +describe("watch storage", () => { + it("loads a project's own watches", () => { + localStorage.setItem(of("/a"), '["x"]'); + loadWatches("/a"); + expect(watches()).toEqual(["x"]); + }); + + it("starts a project from the shared list once", () => { + localStorage.setItem(SHARED, '["legacy"]'); + loadWatches("/b"); + expect(watches()).toEqual(["legacy"]); + expect(localStorage.getItem(of("/b"))).toBe('["legacy"]'); + expect(localStorage.getItem(SHARED)).toBe('["legacy"]'); + }); + + it("keeps projects apart", () => { + loadWatches("/a"); + addWatch("y"); + loadWatches("/b"); + expect(watches()).not.toContain("y"); + loadWatches("/a"); + expect(watches()).toContain("y"); + }); + + it("an emptied list stays empty", () => { + localStorage.setItem(SHARED, '["legacy"]'); + localStorage.setItem(of("/a"), "[]"); + loadWatches("/a"); + expect(watches()).toEqual([]); + }); + + it("without a project nothing is stored", () => { + loadWatches(null); + addWatch("z"); + expect(localStorage.length).toBe(0); + }); +}); diff --git a/src/features/debugger/index.ts b/src/features/debugger/index.ts index 9bbbf8405..2128bf1b1 100644 --- a/src/features/debugger/index.ts +++ b/src/features/debugger/index.ts @@ -57,7 +57,7 @@ export function activate(): () => void { useDebugRunSessions.setState({ Console: lazyComponent(() => import("./ui/DebugConsole").then((m) => m.DebugServiceConsole)), }); - loadWatches(); + loadWatches(useProject.getState().project?.root ?? null); publishContext(); loadBreakpoints(useProject.getState().project?.root ?? null); loadExceptionBreakpoints(useProject.getState().project?.root ?? null); @@ -136,6 +136,7 @@ export function activate(): () => void { const root = s.project?.root ?? null; if (root !== (prev.project?.root ?? null) || s.project?.trusted !== prev.project?.trusted) { resetSessions(); + loadWatches(root); loadBreakpoints(root); loadExceptionBreakpoints(root); loadSpecialBreakpoints(root); diff --git a/src/features/debugger/state.ts b/src/features/debugger/state.ts index a2a783afe..8a6f7cca7 100644 --- a/src/features/debugger/state.ts +++ b/src/features/debugger/state.ts @@ -1025,25 +1025,40 @@ export function removeWatch(expression: string) { persistWatches(); } -const WATCH_KEY = "openidea.debugger.watches"; +/** Watches used to be one list for every project; it is only read now, as a project's first list. */ +const SHARED_WATCH_KEY = "openidea.debugger.watches"; +const watchKey = (root: string) => `${SHARED_WATCH_KEY}:${root}`; + +/** The project whose watches are loaded; without one they aren't stored. */ +let watchRoot: string | null = null; function persistWatches() { + if (watchRoot == null) return; try { - localStorage.setItem(WATCH_KEY, JSON.stringify(useDebug.getState().watches)); + localStorage.setItem(watchKey(watchRoot), JSON.stringify(useDebug.getState().watches)); } catch { // ignore } } -export function loadWatches() { +function storedWatches(key: string): string[] | null { try { - const raw = JSON.parse(localStorage.getItem(WATCH_KEY) ?? "[]") as unknown; - if (Array.isArray(raw)) useDebug.setState({ watches: raw.filter((w): w is string => typeof w === "string") }); + const stored = localStorage.getItem(key); + const raw = stored == null ? null : (JSON.parse(stored) as unknown); + return Array.isArray(raw) ? raw.filter((w): w is string => typeof w === "string") : null; } catch { - // ignore + return null; } } +/** The watches of the project at `root`. */ +export function loadWatches(root: string | null) { + watchRoot = root; + const own = root == null ? [] : storedWatches(watchKey(root)); + useDebug.setState({ watches: own ?? storedWatches(SHARED_WATCH_KEY) ?? [] }); + if (own == null) persistWatches(); +} + /** * "Set Value" (F2 in the variables tree): `setVariable` in `container`, or * `setExpression` on the evaluate name. Refreshes variables, watches and inline values.