╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │18 // how many diff hunks one window of a commit's diff shows; "next"/"previous"│ │19 // move to the adjacent window. │ │20 const diff_page = 10; │ │21 -// the largest commit message a log page's detail view reads; a longer one is│ │22 -// cut here, with the message page carrying the whole thing. │ │23 -pub const max_message_size = 2 * 1024; │ │24 -// the same for the message page, which only reads the one commit. │ │25 -const max_full_message_size = 100 * 1024; │ │21 +// the largest message read even when it has fewer than the preview's line │ │22 +// limit. │ │23 +const max_message_size = 100 * 1024; │ │24 │ │25 pub const Hunk = struct { │ │26 path: ?[]const u8 = null, // the file this hunk belongs to │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │33 oid: []const u8, │ │34 date: []const u8, // "YYYY-MM-DD" │ │35 message: []const u8, // trimmed, may be multi-line │ │38 - // whether `message` is only the first line of a message too big to read.│ │36 + // whether `message` is a shortened preview. │ │37 message_truncated: bool = false, │ │38 author: ui.Author = .unknown, │ │39 hunks: []const Hunk, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │61 const Self = @This(); │ │62 │ │63 // the diff and the message are alternatives, so a filtered diff can't also be│ │66 -// a message. the message is read whole, however big, since it's all its pane │ │67 -// shows. │ │64 +// a message. the message page reads up to the safety limit above. │ │65 pub const Content = union(enum) { │ │66 diff: struct { │ │67 // the file the top commit's diff is filtered to ("" = every file). │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │133 } │ │134 const md = commit_object.content.commit.metadata; │ │135 @memcpy(&oids[count], &commit_object.oid); │ │139 - // only the message the pane shows is read past the log's limit. │ │140 - const text, const truncated = try readMessage(repo_kind, repo_opts, aa, commit_object, if (root_message and count == 0) max_full_message_size else max_message_size);│ │136 + const text, const truncated = try readMessage(repo_kind, repo_opts, aa, commit_object, root_message and count == 0); │ │137 buf[count] = .{ │ │138 .oid = try aa.dupe(u8, &commit_object.oid), │ │139 .date = try formatDate(aa, md.timestamp), │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │340 }); │ │341 } │ │342 │ │347 -// the commit message, trimmed and read into `aa`, plus whether it ran past │ │348 -// `limit` and was cut short there. │ │343 +// the commit message, trimmed and read into `aa`, plus whether its preview was│ │344 +// cut short. │ │345 fn readMessage( │ │346 comptime repo_kind: rp.RepoKind, │ │347 comptime repo_opts: rp.RepoOpts(repo_kind), │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │348 aa: std.mem.Allocator, │ │349 commit_object: *xit.object.Object(repo_kind, repo_opts), │ │354 - limit: usize, │ │350 + full_message: bool, │ │351 ) !struct { []const u8, bool } { │ │352 var message: std.ArrayList(u8) = .empty; │ │357 - // a message past the limit keeps the bytes read so far │ │358 - const truncated = if (commit_object.readMessage(aa, &message, .limited(limit))) |_| │ │353 + const byte_truncated = if (commit_object.readMessage(aa, &message, .limited(max_message_size))) |_|│ │354 false │ │355 else |err| switch (err) { │ │356 error.StreamTooLong => true, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │357 else => |e| return e, │ │358 }; │ │364 - // the cut lands wherever the limit falls, so a truncated message keeps │ │365 - // only its whole lines: a line break can't fall inside a codepoint, and │ │366 - // the widgets need valid utf-8. a first line past the limit is instead │ │367 - // cut at the last codepoint boundary. │ │368 - const text = if (truncated) │ │369 - (if (std.mem.lastIndexOfScalar(u8, message.items, '\n')) |nl| message.items[0..nl] else trimIncompleteCodepoint(message.items))│ │359 + const preview_end = if (full_message) null else ui.detailPreviewEnd(message.items); │ │360 + const text = if (preview_end) |end| │ │361 + message.items[0..end] │ │362 + else if (byte_truncated) │ │363 + trimIncompleteCodepoint(message.items) │ │364 else │ │365 message.items; │ │372 - return .{ std.mem.trim(u8, text, " \t\r\n"), truncated }; │ │366 + return .{ std.mem.trim(u8, text, " \t\r\n"), byte_truncated or preview_end != null }; │ │367 } │ │368 │ │369 /// drop any incomplete UTF data from the end of a byte array │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯