Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fill-static-pager-rows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hunkdiff": patch
---

Extend static pager diff-row backgrounds to the edge of host panels such as Lazygit.
24 changes: 18 additions & 6 deletions src/ui/staticDiffPager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ function stripAnsi(text: string) {
return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
}

/** Remove Hunk's intentional SGR color codes while leaving unsafe controls visible. */
function stripColorSgr(text: string) {
return text.replace(/\x1b\[[0-9;]*m/g, "");
/** Remove Hunk's intentional color and line-fill codes while leaving unsafe controls visible. */
function stripIntentionalAnsi(text: string) {
return text.replace(/\x1b(?:\[[0-9;]*m|\[K)/g, "");
}

const OSC52_CLIPBOARD = "\x1b]52;c;SGVsbG8=\x07";
Expand Down Expand Up @@ -95,6 +95,18 @@ describe("static diff pager", () => {
expect(plain).toContain("▌ 1 + const value = 2;");
});

test("extends stacked row backgrounds to the host panel edge", async () => {
const patchText =
"diff --git a/a.ts b/a.ts\n--- a/a.ts\n+++ b/a.ts\n@@ -1 +1 @@\n-short\n+also short\n";

const output = await renderStaticDiffPager(patchText);
const changedLines = output.split("\n").filter((line) => stripAnsi(line).includes("short"));
const backgroundFill = /\x1b\[48;2;\d+;\d+;\d+m\x1b\[K\x1b\[0m$/;

expect(changedLines).toHaveLength(2);
expect(changedLines.every((line) => backgroundFill.test(line))).toBe(true);
});

test("uses configured custom themes in static pager output", async () => {
const patchText =
"diff --git a/a.ts b/a.ts\n--- a/a.ts\n+++ b/a.ts\n@@ -1 +1 @@\n-const value = 1;\n+const value = 2;\n";
Expand Down Expand Up @@ -180,7 +192,7 @@ describe("static diff pager", () => {
"",
].join("\n");

const output = stripColorSgr(
const output = stripIntentionalAnsi(
await renderStaticDiffPager(text, {}, { stderr: { write: () => true } }),
);

Expand All @@ -199,7 +211,7 @@ describe("static diff pager", () => {
"",
].join("\n");

const output = stripColorSgr(await renderStaticDiffPager(patchText));
const output = stripIntentionalAnsi(await renderStaticDiffPager(patchText));

expect(output).toContain("evil");
expect(output).toContain("@@ -1 +1 @@");
Expand All @@ -217,7 +229,7 @@ describe("static diff pager", () => {
"",
].join("\n");

const output = stripColorSgr(await renderStaticDiffPager(patchText));
const output = stripIntentionalAnsi(await renderStaticDiffPager(patchText));

expectNoUnsafeTerminalControls(output);
});
Expand Down
8 changes: 7 additions & 1 deletion src/ui/staticDiffPager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ function colorText(text: string, fg?: string, bg?: string) {
return prefix ? `${prefix}${safeText}${RESET}` : safeText;
}

/** Extend one row background to the host panel edge without assuming the panel width. */
function fillRemainingLine(bg: string) {
const background = ansiColor("bg", bg);
return background ? `${background}\x1b[K${RESET}` : "";
}

/** Serialize highlighted code spans into ANSI text, preserving a row background when present. */
function serializeSpans(spans: RenderSpan[], rowBg: string) {
return spans.map((span) => colorText(span.text, span.fg, span.bg ?? rowBg)).join("");
Expand Down Expand Up @@ -157,7 +163,7 @@ function renderStaticStackRow(
staticStackGutterText(cell, lineNumberWidth, options.lineNumbers !== false),
palette.numberColor,
palette.gutterBg,
)}${serializeSpans(cell.spans, palette.contentBg)}`;
)}${serializeSpans(cell.spans, palette.contentBg)}${fillRemainingLine(palette.contentBg)}`;
}

function renderStaticSplitCell(
Expand Down