From e6ff64843b4a8986e9740c576f346690217cf5a7 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 01:14:37 -0300 Subject: A +Pager pane whose text is a diff is drawn and looked at as a diff, as a diff command pane is: git diff, show and log -p page into review With git paging into +Pager, a diff arrived as plain text: no colour for its sides and a right click on a hunk line searched the word. A +Pager pane now notes, as its text is set, whether its first 64 KiB holds a diff header or a unified hunk, and such a pane takes the diff highlighting and the diff look every diff pane has. Co-Authored-By: Claude Opus 5.5 --- src/File.zig | 33 +++++++++++++++++++++++++++++++++ src/panes.zig | 2 +- src/pardes.zig | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/File.zig b/src/File.zig index d16d4fc0..c738ee58 100644 --- a/src/File.zig +++ b/src/File.zig @@ -72,6 +72,9 @@ pub const State = struct { /// Its file was deleted on disk: said once (`deleted`) until it is /// written or found again. disk_gone: bool = false, + /// A +Pager pane (`pardes -`) whose text is a diff (`git diff`, `show`, + /// `log -p`): drawn and looked at as a diff (Pane.colorAlgo). + pager_diff: bool = false, /// Seconds of the last content change, for the filesystem's stat; zero until edited. mtime: u32 = 0, /// Non-null for a generated output buffer rather than an on-disk file. @@ -998,6 +1001,36 @@ pub fn setContentSpan(p: *Pardes, f: *State, new: []u8, span: ?Span) void { f.highlights = &.{}; f.highlight_start = 0; f.syntax_dirty = true; + if (std.mem.endsWith(u8, f.path, "/+Pager")) f.pager_diff = looksLikeDiff(new[0..@min(new.len, 64 * 1024)]); +} + +/// Whether `head`, a text's first bytes, holds a diff: a `diff` header, or +/// a unified hunk's `---`, `+++` and `@@` lines. +pub fn looksLikeDiff(head: []const u8) bool { + if (std.mem.startsWith(u8, head, "diff ") or std.mem.indexOf(u8, head, "\ndiff ") != null) return true; + const minus = std.mem.indexOf(u8, head, "--- ") orelse return false; + const plus = std.mem.indexOfPos(u8, head, minus, "\n+++ ") orelse return false; + return std.mem.indexOfPos(u8, head, plus, "\n@@ ") != null; +} + +test "a +Pager pane holding a diff is drawn and looked at as one; holding other text, as text" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + const pane = try p.setTestFile(""); + const f = &pane.file.?; + p.gpa.free(f.path); + f.path = try p.gpa.dupe(u8, "/tmp/+Pager"); + setContent(p, f, try p.gpa.dupe(u8, "commit abc\n\ndiff --git a/x b/x\n--- a/x\n+++ b/x\n@@ -1 +1 @@\n-a\n+b\n")); + try std.testing.expect(pane.colorAlgo() == .diff); + try std.testing.expect(pardes.look.diffLines(p, pane) != null); + setContent(p, f, try p.gpa.dupe(u8, "commit abc\n\n a plain log\n")); + try std.testing.expect(pane.colorAlgo() != .diff); +} + +test "a pager's text is a diff when it holds a diff header or a unified hunk" { + try std.testing.expect(looksLikeDiff("commit abc\nAuthor: x\n\n msg\n\ndiff --git a/f b/f\n")); + try std.testing.expect(looksLikeDiff("--- a/f\n+++ b/f\n@@ -1 +1 @@\n-a\n+b\n")); + try std.testing.expect(!looksLikeDiff("commit abc\nAuthor: x\n\n plain log\n")); } pub fn restoreSnap(pane: *Pane, f: *State, snap: Snapshot) void { diff --git a/src/panes.zig b/src/panes.zig index a395d6ed..9d1b7571 100644 --- a/src/panes.zig +++ b/src/panes.zig @@ -448,7 +448,7 @@ pub const Pane = struct { if (pane.isTerminal()) return .tty; if (pane.file) |f| { if (f.mini != null) return .source; - if (std.mem.endsWith(u8, f.path, ".diff") or std.mem.endsWith(u8, f.path, ".patch")) return .diff; + if (std.mem.endsWith(u8, f.path, ".diff") or std.mem.endsWith(u8, f.path, ".patch") or f.pager_diff) return .diff; if (f.output != null) { const tr = Output.fileTraits(f.output); if (tr.locations) return .locations; diff --git a/src/pardes.zig b/src/pardes.zig index 84b891e5..5d0ab7e8 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4640,6 +4640,7 @@ pub const Pardes = struct { const named = try std.fs.path.join(gpa, &.{ dir, "+Pager" }); gpa.free(shown.file.?.path); shown.file.?.path = named; + shown.file.?.pager_diff = panes.File.looksLikeDiff(content[0..@min(content.len, 64 * 1024)]); p.oneColumn(); }, .embedded => { -- cgit v1.3