From 7e1cbba9eacbe0edcdff89f85455dcb658e0fc71 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Fri, 23 Oct 2026 09:50:05 +0100 Subject: [PATCH] web/repo: utils to find hidden diff lines Signed-off-by: oppiliappan --- web/src/lib/components/repo/fileDiff.test.ts | 37 +++++++++++++++++++- web/src/lib/components/repo/fileDiff.ts | 37 +++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/repo/fileDiff.test.ts b/web/src/lib/components/repo/fileDiff.test.ts index 8035e313a..d5903576a 100644 --- a/web/src/lib/components/repo/fileDiff.test.ts +++ b/web/src/lib/components/repo/fileDiff.test.ts @@ -1,5 +1,6 @@ +import type { Hunk } from "@pierre/diffs"; import { describe, expect, it } from "vitest"; -import { reservedRows } from "#lib/components/repo/fileDiff.ts"; +import { hiddenLine, newSideLine, reservedRows } from "#lib/components/repo/fileDiff.ts"; describe("reservedRows", () => { const file = (lines: number, insertions: number, deletions: number) => ({ @@ -25,3 +26,37 @@ describe("reservedRows", () => { expect(reservedRows({ stat: { insertions: 10, deletions: 6 } }, false)).toBe(22); }); }); + +const hunk = ( + deletionStart: number, + deletionCount: number, + additionStart: number, + additionCount: number +): Hunk => ({ deletionStart, deletionCount, additionStart, additionCount }) as Hunk; + +const hunks = [hunk(10, 5, 10, 7), hunk(40, 3, 41, 0)]; + +describe("hiddenLine", () => { + it("shows lines inside a hunk", () => { + expect(hiddenLine(hunks, 12, "additions")).toBeUndefined(); + expect(hiddenLine(hunks, 14, "deletions")).toBeUndefined(); + expect(hiddenLine(hunks, 41, "deletions")).toBeUndefined(); + }); + + it("maps hidden lines to the new side", () => { + expect(hiddenLine(hunks, 3, "additions")).toBe(3); + expect(hiddenLine(hunks, 3, "deletions")).toBe(3); + expect(hiddenLine(hunks, 20, "deletions")).toBe(22); + expect(hiddenLine(hunks, 30, "additions")).toBe(30); + expect(hiddenLine(hunks, 50, "deletions")).toBe(49); + expect(hiddenLine(hunks, 49, "additions")).toBe(49); + }); +}); + +describe("newSideLine", () => { + it("puts a removed line where its hunk starts", () => { + expect(newSideLine(hunks, 41, "deletions")).toBe(42); + expect(newSideLine(hunks, 20, "deletions")).toBe(22); + expect(newSideLine(hunks, 7, "additions")).toBe(7); + }); +}); diff --git a/web/src/lib/components/repo/fileDiff.ts b/web/src/lib/components/repo/fileDiff.ts index e0969dfea..9f94f0e63 100644 --- a/web/src/lib/components/repo/fileDiff.ts +++ b/web/src/lib/components/repo/fileDiff.ts @@ -1,4 +1,4 @@ -import type { FileDiffMetadata } from "@pierre/diffs"; +import type { FileDiffMetadata, Hunk, SelectionSide } from "@pierre/diffs"; export type DiffEntry = FileDiffMetadata | { note: string }; @@ -10,3 +10,38 @@ export const reservedRows = ( ? (file.lines ?? file.stat.insertions + file.stat.deletions) : (file.lines ?? file.stat.insertions + file.stat.deletions) + Math.min(file.stat.insertions, file.stat.deletions); + +const sideStart = (start: number, count: number): number => (count === 0 ? start + 1 : start); + +export const hiddenLine = ( + hunks: readonly Hunk[], + line: number, + side: SelectionSide +): number | undefined => { + let shift = 0; + for (const hunk of hunks) { + const deletionFrom = sideStart(hunk.deletionStart, hunk.deletionCount); + const additionFrom = sideStart(hunk.additionStart, hunk.additionCount); + const from = side === "deletions" ? deletionFrom : additionFrom; + const count = side === "deletions" ? hunk.deletionCount : hunk.additionCount; + const delta = additionFrom - deletionFrom; + if (line < from) return side === "deletions" ? line + delta : line; + if (line < from + count) return undefined; + shift = delta + hunk.additionCount - hunk.deletionCount; + } + return side === "deletions" ? line + shift : line; +}; + +export const newSideLine = (hunks: readonly Hunk[], line: number, side: SelectionSide): number => { + if (side === "additions") return line; + const hidden = hiddenLine(hunks, line, side); + if (hidden !== undefined) return hidden; + const hunk = hunks.find( + (candidate) => + line >= sideStart(candidate.deletionStart, candidate.deletionCount) && + line < + sideStart(candidate.deletionStart, candidate.deletionCount) + + candidate.deletionCount + ); + return hunk ? sideStart(hunk.additionStart, hunk.additionCount) : line; +}; -- 2.54.0