mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
feat(vcs): Preview the selected file's diff in the Git Log
Selecting a single changed file in the Git Log's changes pane shows its diff in the editor's preview tab without taking focus, so arrow keys walk through the files. View Options › Show Diff Preview turns it off; double-click and Show Diff still open a regular diff tab.
This commit is contained in:
1 parent
c6621c5c36
commit
f1ff2731f0
7 files changed
+58
-6
No files matched your search
@@ -56,7 +56,7 @@ import {
|
||||
updateLogOptions,
|
||||
useGitLog,
|
||||
} from "./logStore";
|
||||
import { refLabels, useLogOptions, type LogOptions } from "./logOptions";
|
||||
import { logOptions, refLabels, useLogOptions, type LogOptions } from "./logOptions";
|
||||
import { KIND_COLOR, baseName, dirName, fileCount, formatTime, relTo, type EffectiveKind } from "./model";
|
||||
import { clearConsole, useVcsConsole } from "./ops";
|
||||
import { currentRepo, loadBranches, useVcs } from "./store";
|
||||
@@ -420,6 +420,19 @@ export function BranchesPanel() {
|
||||
// --- filters ----------------------------------------------------------------
|
||||
|
||||
const DAY = 86_400_000;
|
||||
/** Keyboard moves through several files quickly: preview the one the selection settles on. */
|
||||
const PREVIEW_DELAY = 120;
|
||||
|
||||
function fileNodeAt<F>(nodes: readonly FileTreeNode<F>[], key: string): FileTreeNode<F> | null {
|
||||
for (const n of nodes) {
|
||||
if (n.key === key) return n.file ? n : null;
|
||||
if (!n.file) {
|
||||
const hit = fileNodeAt(n.children, key);
|
||||
if (hit) return hit;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const DATE_OPTIONS: { label: string; days: number }[] = [
|
||||
{ label: "Last 24 Hours", days: 1 },
|
||||
{ label: "Last 7 Days", days: 7 },
|
||||
@@ -1103,6 +1116,16 @@ export function CommitDetails({ commits }: { commits: Commit[] }) {
|
||||
const ordered = useMemo(() => filesUnder(tree), [tree]);
|
||||
const chosenChanges = useMemo(() => filesUnder(tree, selectedKeys), [tree, selectedKeys]);
|
||||
|
||||
// Show Diff Preview: a single selected file follows into the editor's preview tab.
|
||||
const previewTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
useEffect(() => () => clearTimeout(previewTimer.current), []);
|
||||
const previewSelection = (sel: ReadonlySet<string>) => {
|
||||
clearTimeout(previewTimer.current);
|
||||
if (!logOptions().diffPreview || sel.size !== 1) return;
|
||||
const file = fileNodeAt(tree, [...sel][0]!)?.file;
|
||||
if (file) previewTimer.current = setTimeout(() => void showChangesDiff([file], true), PREVIEW_DELAY);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
useLogChanges.setState({ commits: chosen, all: ordered, selected: chosenChanges });
|
||||
}, [chosen, ordered, chosenChanges]);
|
||||
@@ -1121,6 +1144,8 @@ export function CommitDetails({ commits }: { commits: Commit[] }) {
|
||||
menuBelow(e, [
|
||||
{ kind: "header", label: "Group By" },
|
||||
{ kind: "action", action: "vcs.log.changes.groupByDirectory" },
|
||||
{ kind: "separator" },
|
||||
{ kind: "action", action: "vcs.log.changes.diffPreview" },
|
||||
]);
|
||||
|
||||
const onFileMenu = (node: FileTreeNode<LogChange> | null, sel: ReadonlySet<string>, e: ReactMouseEvent) => {
|
||||
@@ -1190,7 +1215,10 @@ export function CommitDetails({ commits }: { commits: Commit[] }) {
|
||||
expanded={expanded ?? new Set(allDirs)}
|
||||
onExpandedChange={(next) => setExpanded(next)}
|
||||
selected={selectedKeys}
|
||||
onSelectionChange={(sel) => setSelectedKeys(sel)}
|
||||
onSelectionChange={(sel) => {
|
||||
setSelectedKeys(sel);
|
||||
previewSelection(sel);
|
||||
}}
|
||||
renderNode={(n) => <ChangedFileRow node={n} flat={!groupByDirectory} />}
|
||||
onActivate={(n) => n.file && void showChangesDiff([n.file])}
|
||||
onContextMenu={onFileMenu}
|
||||
|
||||
@@ -121,6 +121,12 @@ describe("log options", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("previews the selected file's diff by default and remembers turning it off", () => {
|
||||
expect(DEFAULT_LOG_OPTIONS.diffPreview).toBe(true);
|
||||
expect(parseLogOptions({ diffPreview: false }).diffPreview).toBe(false);
|
||||
expect(setLogOptions({ diffPreview: false })).toBe(false);
|
||||
});
|
||||
|
||||
it("reloads only for options that change the query", () => {
|
||||
expect(setLogOptions({ compactRefs: false })).toBe(false);
|
||||
expect(setLogOptions({ noMerges: true })).toBe(true);
|
||||
|
||||
@@ -117,8 +117,8 @@ export function diffTitle(data: DiffData): string {
|
||||
return `${baseName(data.path)} (${sideLabel(data.left)} ↔ ${sideLabel(data.right)})`;
|
||||
}
|
||||
|
||||
export async function openDiff(data: DiffData, opts: { preview?: boolean } = {}) {
|
||||
await editorService().openInput(diffInput(data), { preview: opts.preview, focus: true });
|
||||
export async function openDiff(data: DiffData, opts: { preview?: boolean; focus?: boolean } = {}) {
|
||||
await editorService().openInput(diffInput(data), { preview: opts.preview, focus: opts.focus ?? true });
|
||||
}
|
||||
|
||||
/** Default local-changes diff for a working file: HEAD (or empty) vs the working copy. */
|
||||
|
||||
@@ -124,8 +124,11 @@ export function changesArg(arg: unknown): LogChange[] {
|
||||
return useLogChanges.getState().selected;
|
||||
}
|
||||
|
||||
/** Show Diff: the first file, with every file of the pane in the chain (F7 moves along). */
|
||||
export async function showChangesDiff(changes: readonly LogChange[]) {
|
||||
/**
|
||||
* Show Diff: the first file, with every file of the pane in the chain (F7 moves along).
|
||||
* `preview`: in the editor's preview tab without taking focus (selection in the pane).
|
||||
*/
|
||||
export async function showChangesDiff(changes: readonly LogChange[], preview = false) {
|
||||
const first = changes[0];
|
||||
if (!first) return;
|
||||
const all = useLogChanges.getState().all;
|
||||
@@ -135,6 +138,7 @@ export async function showChangesDiff(changes: readonly LogChange[]) {
|
||||
chain.map((c) => c.diff),
|
||||
Math.max(0, chain.indexOf(first)),
|
||||
),
|
||||
preview ? { preview: true, focus: false } : {},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ export interface LogOptions {
|
||||
showBranches: boolean;
|
||||
/** Changed files grouped by directory (else a flat list). */
|
||||
groupByDirectory: boolean;
|
||||
/** Selecting a changed file shows its diff in the editor's preview tab. */
|
||||
diffPreview: boolean;
|
||||
}
|
||||
|
||||
/** IDEA's defaults. */
|
||||
@@ -47,6 +49,7 @@ export const DEFAULT_LOG_OPTIONS: LogOptions = {
|
||||
highlightCurrentBranch: true,
|
||||
showBranches: true,
|
||||
groupByDirectory: true,
|
||||
diffPreview: true,
|
||||
};
|
||||
|
||||
/** Options that change which commits the backend returns (a reload is needed). */
|
||||
|
||||
@@ -207,5 +207,14 @@ export function registerLogPaneActions(): () => void {
|
||||
toggled: () => logOptions().groupByDirectory,
|
||||
run: () => updateLogOptions({ groupByDirectory: !logOptions().groupByDirectory }),
|
||||
},
|
||||
{
|
||||
id: "vcs.log.changes.diffPreview",
|
||||
title: "Show Diff Preview",
|
||||
category: "Git Log",
|
||||
description: "Selecting a changed file shows its diff in the editor's preview tab",
|
||||
when: VCS,
|
||||
toggled: () => logOptions().diffPreview,
|
||||
run: () => updateLogOptions({ diffPreview: !logOptions().diffPreview }),
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -1637,6 +1637,8 @@
|
||||
"Select a problem to see its details": "选择一个问题以查看详情",
|
||||
"Select an item to preview": "选择一项以预览",
|
||||
"Severity": "严重性",
|
||||
"Selecting a changed file shows its diff in the editor's preview tab": "选中已更改的文件时,在编辑器的预览标签页中显示其差异",
|
||||
"Show Diff Preview": "显示差异预览",
|
||||
"Show Quick Fixes": "显示快速修复",
|
||||
"Snappy": "迅捷",
|
||||
"Sort By": "排序依据",
|
||||
|
||||
Reference in new issue
Block a user