mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-14 16:07:52 +00:00
fix(vcs): align the diff titles and find bar with the panes, and title revisions by commit and path (#2778)
After the side-by-side divider was dragged, the right half of the title row and of the find bar stayed at 50% and no longer lined up with the right pane. Both now follow the right pane's actual start. A revision side's title shows its short commit id (HEAD resolved) and the file's path in the repository. Co-authored-by: PLZY <41029728+PLZY@users.noreply.github.com>
This commit is contained in:
@@ -71,6 +71,17 @@
|
||||
.label + .label {
|
||||
border-left: 1px solid var(--border);
|
||||
}
|
||||
/* Side by side, the right label starts at the right pane (`--diff-b-start`, set by the viewer). */
|
||||
.labels > .label:first-child:not(:only-child) {
|
||||
flex: 0 0 var(--diff-b-start, 50%);
|
||||
}
|
||||
.labelFile {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--fg-subtle);
|
||||
font-family: var(--font-ui);
|
||||
}
|
||||
.badge {
|
||||
padding: 0 5px;
|
||||
border-radius: var(--radius-small);
|
||||
@@ -93,13 +104,14 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
.findRowSplit > div {
|
||||
flex: 0 0 50%;
|
||||
flex: 0 0 var(--diff-b-start, 50%);
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.findRowRight {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.findRowRight > div {
|
||||
flex-basis: calc(100% - var(--diff-b-start, 50%));
|
||||
border-right: none;
|
||||
border-left: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ import { inclusionGutter } from "./hunkInclusion";
|
||||
import { hunksOverlapping } from "./hunks";
|
||||
import { effectiveKind, stripSlash } from "./model";
|
||||
import { errorMessage, logConsole, readWorkingText, runOp, saveAll } from "./ops";
|
||||
import { refreshNow, useVcs } from "./store";
|
||||
import { refreshNow, repoOf, useVcs } from "./store";
|
||||
import styles from "./DiffEditor.module.css";
|
||||
|
||||
/** Commands of the focused diff viewer (diff.* actions, vcs.nextChange / prevChange). */
|
||||
@@ -252,12 +252,14 @@ export function DiffEditor({
|
||||
const [chunkInfo, setChunkInfo] = useState({ count: 0, current: -1, moves: 0, whitespaceOnly: false });
|
||||
const [busy, setBusy] = useState(false);
|
||||
const hostRef = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const viewsRef = useRef<Views | null>(null);
|
||||
const staticsRef = useRef(statics);
|
||||
const focusedSide = useRef<Side>("b");
|
||||
/** Bumped when the views are rebuilt, so the find bar re-applies its query. */
|
||||
const [viewsGen, setViewsGen] = useState(0);
|
||||
const version = useVcs((s) => s.version);
|
||||
const heads = useVcs((s) => s.heads);
|
||||
const leftKey = sideKey(left);
|
||||
const rightKey = sideKey(right);
|
||||
const contextKey = JSON.stringify(data.context ?? null);
|
||||
@@ -577,6 +579,27 @@ export function DiffEditor({
|
||||
views = { a: null, b: view, merge: null, panes: null, destroy: () => view.destroy() };
|
||||
if (scrollTop != null) requestAnimationFrame(() => (view.scrollDOM.scrollTop = scrollTop));
|
||||
}
|
||||
// The labels and the find bar above side-by-side panes start the right side where its pane
|
||||
// does, wherever the divider (dragged, see UnalignedPanes) and the window put it.
|
||||
const root = rootRef.current;
|
||||
if (views.a && root && typeof ResizeObserver !== "undefined") {
|
||||
const paneB = views.b.dom;
|
||||
const align = () =>
|
||||
root.style.setProperty(
|
||||
"--diff-b-start",
|
||||
`${paneB.getBoundingClientRect().left - root.getBoundingClientRect().left}px`,
|
||||
);
|
||||
const observer = new ResizeObserver(align);
|
||||
observer.observe(root);
|
||||
observer.observe(paneB);
|
||||
align();
|
||||
const destroyPanes = views.destroy;
|
||||
views.destroy = () => {
|
||||
observer.disconnect();
|
||||
root.style.removeProperty("--diff-b-start");
|
||||
destroyPanes();
|
||||
};
|
||||
}
|
||||
viewsRef.current = views;
|
||||
const built = viewerHost(views, dataRef.current, opts.mode, () => focusedSide.current);
|
||||
const detach = contribs.map((c) => c.attach?.(built));
|
||||
@@ -1106,8 +1129,23 @@ export function DiffEditor({
|
||||
const canStage = editableB && isWorktree(right) && left.type === "rev" && !!useVcs.getState().root;
|
||||
const hasSource = !!source;
|
||||
|
||||
/** IDEA's title of a revision: its short commit id (HEAD resolved) and the file's path in the repository. */
|
||||
const revTitle = (s: DiffSide): { label: string; file: string | null } => {
|
||||
if (s.type !== "rev") return { label: sideLabel(s), file: null };
|
||||
const file = s.path ?? path;
|
||||
const root = repoOf(file);
|
||||
const commit = !s.label && s.rev === "HEAD" && root ? heads[root]?.headCommit : null;
|
||||
return {
|
||||
label: commit ? commit.slice(0, 8) : sideLabel(s),
|
||||
file: root && file.startsWith(`${root}/`) ? file.slice(root.length + 1) : file,
|
||||
};
|
||||
};
|
||||
const titleA = revTitle(left);
|
||||
const titleB = revTitle(right);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className={styles.root}
|
||||
onKeyDown={onKeyDown}
|
||||
onFocusCapture={() => {
|
||||
@@ -1278,8 +1316,9 @@ export function DiffEditor({
|
||||
</div>
|
||||
{opts.mode === "split" ? (
|
||||
<div className={styles.labels}>
|
||||
<span className={styles.label} title={sideLabel(left)}>
|
||||
{sideLabel(left)}
|
||||
<span className={styles.label} title={[titleA.label, titleA.file].filter(Boolean).join(" ")}>
|
||||
{titleA.label}
|
||||
{titleA.file && <span className={styles.labelFile}>{titleA.file}</span>}
|
||||
{eolDiffers && eolA && <span className={styles.badge}>{badgeA}</span>}
|
||||
{editableA && (
|
||||
<span className={styles.badge}>
|
||||
@@ -1287,8 +1326,9 @@ export function DiffEditor({
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className={styles.label} title={sideLabel(right)}>
|
||||
{sideLabel(right)}
|
||||
<span className={styles.label} title={[titleB.label, titleB.file].filter(Boolean).join(" ")}>
|
||||
{titleB.label}
|
||||
{titleB.file && <span className={styles.labelFile}>{titleB.file}</span>}
|
||||
{eolDiffers && eolB && <span className={styles.badge}>{badgeB}</span>}
|
||||
{editableB && (
|
||||
<span className={styles.badge}>{left.type === "rev" ? tx("working copy") : tx("editable")}</span>
|
||||
|
||||
@@ -51,6 +51,7 @@ import { DiffEditorTab } from "../diff-editor-tab";
|
||||
import { diffInput, withChain, type DiffData } from "../diffInput";
|
||||
import { useToolWindowLayout } from "@/workbench/toolwindows/store";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { useVcs } from "../store";
|
||||
import { applyKeymapSettings, installKeyboardDispatcher, useKeymap } from "@/platform/keymap";
|
||||
import { setContext } from "@/platform/context";
|
||||
import { registerCompareActions } from "../compare";
|
||||
@@ -159,6 +160,18 @@ describe("Diff toolbar", () => {
|
||||
expect(screen.queryByText("Contents differ only in line separators")).toBeNull();
|
||||
});
|
||||
|
||||
it("titles a revision with its short commit id, HEAD resolved, and the file's repository path", async () => {
|
||||
const before = useVcs.getState();
|
||||
useVcs.setState({ roots: ["/p"], heads: { "/p": { head: "main", headCommit: "81a179900123456789abcdef" } } });
|
||||
const mounted = render(
|
||||
<DiffEditor input={{ ...input, data: { ...(input.data as object), path: "/p/src/a.ts", mode: "split" } }} />,
|
||||
);
|
||||
await waitFor(() => expect(mounted.container.querySelectorAll(".cm-editor")).toHaveLength(2));
|
||||
expect(screen.getByTitle("81a17990 src/a.ts")).toBeInTheDocument();
|
||||
expect(screen.getByTitle("abc123 src/a.ts")).toBeInTheDocument();
|
||||
mounted.unmount();
|
||||
useVcs.setState({ roots: before.roots, heads: before.heads });
|
||||
});
|
||||
it("puts Jump to Source right after Next Difference, in the navigation group", async () => {
|
||||
render(<DiffEditor input={input} />);
|
||||
await screen.findByRole("button", { name: "Jump to Source" });
|
||||
|
||||
Reference in New Issue
Block a user