diff --git a/src/features/vcs/GitLog.tsx b/src/features/vcs/GitLog.tsx index 9fac5b34..958176d5 100644 --- a/src/features/vcs/GitLog.tsx +++ b/src/features/vcs/GitLog.tsx @@ -18,6 +18,7 @@ import clsx from "clsx"; import { useVirtualizer } from "@tanstack/react-virtual"; import type { Branch, ChangedFile, Commit, RevisionChange } from "@/bindings"; import { call, commands } from "@/platform/ipc"; +import { maybeEditorService, type EditorInput } from "@/workbench/contributions"; import { EmptyState, Icon, @@ -32,6 +33,7 @@ import { fileIconName, showContextMenu, type MenuItem, + type TreeHandle, T, copyText, } from "@/ui"; @@ -76,6 +78,7 @@ import { } from "./branches"; import { chooseDateRange, chooseMany, promptText } from "./dialogs"; import { buildFileTree, dirKeys, type FileTreeNode } from "./fileTree"; +import { DIFF_KIND, diffInput } from "./diffInput"; import { absolutePath, filesUnder, @@ -1236,6 +1239,9 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { const groupByDirectory = useLogOptions((s) => s.groupByDirectory); const [selectedKeys, setSelectedKeys] = useState>(() => new Set()); + const treeRef = useRef(null); + const [diffToReveal, setDiffToReveal] = useState<{ key: string } | null>(null); + const syncingDiff = useRef(false); const changes = useMemo( () => (files && commit ? toLogChanges(files, many ? null : commit, commitRoot) : []), [files, commit, many, commitRoot], @@ -1256,6 +1262,7 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { useEffect(() => () => clearTimeout(previewTimer.current), []); const previewSelection = (sel: ReadonlySet) => { clearTimeout(previewTimer.current); + if (syncingDiff.current) return; if (!logOptions().diffPreview || sel.size !== 1) return; const file = fileNodeAt(tree, [...sel][0]!)?.file; // A preview that cannot open (no editor yet, the file went away) is just skipped. @@ -1263,6 +1270,36 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { previewTimer.current = setTimeout(() => void showChangesDiff([file], true).catch(() => undefined), PREVIEW_DELAY); }; + // Follow only this pane's exact diff (path AND revisions); another commit or + // Compare with Local must not move its selection. Never take focus from F7. + useEffect(() => { + const editor = maybeEditorService(); + const followDiff = (input: EditorInput | null) => { + if (input?.kind !== DIFF_KIND) return; + const change = ordered.find((c) => diffInput(c.diff).key === input.key); + if (!change) return; + clearTimeout(previewTimer.current); + const rel = relTo(root, change.path); + setExpanded((current) => { + const next = new Set(current ?? allDirs); + for (const key of allDirs) if (rel.startsWith(key.slice(2) + "/")) next.add(key); + return next; + }); + setDiffToReveal({ key: `f:${rel}` }); + }; + followDiff(editor?.activeInput?.() ?? null); + return editor?.onActiveInputChange?.(followDiff); + }, [ordered, allDirs, root]); + + // Tree.reveal needs the expanded rows to have rendered. Suppress the reverse + // preview update so it cannot reopen the diff and lose its requested chunk. + useLayoutEffect(() => { + if (!diffToReveal) return; + syncingDiff.current = true; + treeRef.current?.reveal(diffToReveal.key); + syncingDiff.current = false; + }, [diffToReveal]); + useEffect(() => { useLogChanges.setState({ commits: chosen, all: ordered, selected: chosenChanges }); }, [chosen, ordered, chosenChanges]); @@ -1344,6 +1381,7 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { )} {files && ( > + handleRef={treeRef} roots={tree} getKey={(n) => n.key} getLabel={(n) => n.name}