From e7952ce3d880869200a9817b556c684355528302 Mon Sep 17 00:00:00 2001 From: DomKing Date: Thu, 5 Nov 2026 14:48:59 +0800 Subject: [PATCH] fix(vcs): show the diff of the file selected in the changes tree (#1480) Show Diff (Cmd+D) in the Commit window or the Git window's Local Changes opened the file still selected in the Project view, which keeps its selection while another view has focus, instead of the change selected in the tree. Show Diff and Compare with Branch or Revision now take the action's paths first, then the changes tree's selection while it has focus, then the Project view's selection, then the active file. Co-authored-by: DomKing <7285294+DomKing@users.noreply.github.com> --- .../vcs/__tests__/show-diff-chain.test.ts | 26 ++++++++++++++-- src/features/vcs/actions.tsx | 30 ++++++++++++++----- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/features/vcs/__tests__/show-diff-chain.test.ts b/src/features/vcs/__tests__/show-diff-chain.test.ts index 5a79094fe..b917a11e6 100644 --- a/src/features/vcs/__tests__/show-diff-chain.test.ts +++ b/src/features/vcs/__tests__/show-diff-chain.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, expect, it, vi } from "vitest"; import type { FileStatus } from "@/bindings"; import { getAction } from "@/platform/actions"; +import { setContext } from "@/platform/context"; import { registerVcsActions } from "../actions"; import { useVcs } from "../store"; import { useCommit } from "../commitStore"; @@ -8,10 +9,11 @@ import { initialChangelists, useChangelists } from "../changelists"; import { chainNeighbour } from "../diffInput"; const opened = vi.hoisted(() => vi.fn()); +const project = vi.hoisted(() => ({ paths: [] as string[] })); vi.mock("@/workbench/contributions", async (original) => ({ ...(await original()), editorService: () => ({ openInput: opened }), - projectViewSelection: () => [], + projectViewSelection: () => project.paths, })); vi.mock("../comparePaths", () => ({ showDirectoryWorkingDiff: async () => false })); @@ -29,9 +31,11 @@ let unregister: () => void; beforeEach(() => { opened.mockClear(); + project.paths = []; + setContext("activeToolWindow", ""); const files = [status("z.ts"), status("b.ts"), status("a.ts"), status("other.ts")]; useVcs.setState({ root: "/repo", roots: ["/repo"], status: files, byPath: new Map(files.map((f) => [f.path, f])) }); - useCommit.setState({ groupByDir: false }); + useCommit.setState({ groupByDir: false, selection: [] }); const state = initialChangelists(); state.lists.push({ id: "other", name: "Other", comment: "" }); state.files = { "other.ts": "other" }; @@ -41,6 +45,7 @@ beforeEach(() => { afterEach(() => { unregister(); + setContext("activeToolWindow", ""); useVcs.setState(vcs); useCommit.setState(commit); useChangelists.setState(lists); @@ -90,3 +95,20 @@ it("keeps repository grouping in a changelist's chain", async () => { "/repo/module/b.ts", ]); }); + +it("shows the selected change rather than a Project view selection kept from earlier", async () => { + // The Project view keeps its selection while the Commit window has focus: ⌘D must open + // the file selected in the changes tree, not the one still selected there. + project.paths = ["/repo/z.ts"]; + setContext("activeToolWindow", "commit"); + useCommit.setState({ selection: ["/repo/b.ts"] }); + await getAction("vcs.showDiff")!.run({ arg: undefined, ctx: { vcsAvailable: true } }); + expect(opened.mock.calls[0]![0].data.path).toBe("/repo/b.ts"); +}); + +it("shows a Project view selection outside the changes tree", async () => { + project.paths = ["/repo/z.ts"]; + setContext("activeToolWindow", "project"); + await getAction("vcs.showDiff")!.run({ arg: undefined, ctx: { vcsAvailable: true } }); + expect(opened.mock.calls[0]![0].data.path).toBe("/repo/z.ts"); +}); diff --git a/src/features/vcs/actions.tsx b/src/features/vcs/actions.tsx index 032119654..a1ca25733 100644 --- a/src/features/vcs/actions.tsx +++ b/src/features/vcs/actions.tsx @@ -75,16 +75,34 @@ export const commitWindowFocused = () => { return active === "commit" || (active === "git" && useGitLog.getState().tab === "changes"); }; -/** Files the current context refers to: argument, commit view selection, or the active file. */ -function contextPaths(arg: unknown): string[] { +/** The action's own paths, or the changes tree's selection while the tree has focus. */ +function changedPaths(arg: unknown): string[] { const explicit = argPaths(arg); if (explicit?.length) return explicit; const sel = useCommit.getState().selection; - if (commitWindowFocused() && sel.length) return sel; + return commitWindowFocused() ? sel : []; +} + +/** Files the current context refers to: argument, commit view selection, or the active file. */ +function contextPaths(arg: unknown): string[] { + const changed = changedPaths(arg); + if (changed.length) return changed; const active = maybeEditorService()?.activePath(); return active ? [active] : []; } +/** + * Paths a selection-based action targets: the argument, else the changes tree's selection, + * else the Project view's — it keeps its selection while another view has focus — else the + * active file. + */ +function selectedPaths(arg: unknown): string[] { + const changed = changedPaths(arg); + if (changed.length) return changed; + const project = projectViewSelection(); + return project.length ? project : contextPaths(arg); +} + function showGitWindow() { toolWindowService().show("git", true); } @@ -321,8 +339,7 @@ export function registerVcsActions(): () => void { if (ctx.shelfFocus && arg === undefined) return showShelfDiff(); const line = arg && typeof arg === "object" && "line" in arg ? Number((arg as { line: unknown }).line) : undefined; - const selection = arg === undefined ? projectViewSelection() : []; - const paths = selection.length ? selection : contextPaths(arg); + const paths = selectedPaths(arg); if (!paths.length) { notifications().notify({ group: "vcs", level: "info", title: "Select a changed file to show its diff" }); return; @@ -354,8 +371,7 @@ export function registerVcsActions(): () => void { icon: "diff", when: VCS, run: async ({ arg }) => { - const selection = arg === undefined ? projectViewSelection() : []; - const paths = selection.length ? selection : contextPaths(arg); + const paths = selectedPaths(arg); if (!paths.length) { notifications().notify({ group: "vcs", level: "info", title: "Open or select a file or folder to compare" }); return;