const std = @import("std");const build_checkpoint = @import("build_checkpoint.zig");const display_width = @import("../../core/shared/display_width.zig");const assistant_pacer = @import("../assistant/pacer.zig"); const Allocator = std.mem.Allocator; /// A pathological combining-mark run gets a physical row boundary at this/// byte count. The source bytes remain exact while every retained row stays/// bounded for compact and full-view projection.pub const literal_command_zero_width_row_byte_limit: usize = 256; const WordBreak = struct { start: usize, end: usize, sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8, has_preceding_word: bool,}; /// The last two space runs on the current row, kept as reflow points for/// overflow wrapping and orphan avoidance.const WordBreaks = struct { last: ?WordBreak = null, previous: ?WordBreak = null, /// Extends `last` when adjacent; otherwise demotes it to `previous`. fn record(self: *WordBreaks, word_break: WordBreak) void { if (self.last) |*last| { if (last.end == word_break.start) { last.end = word_break.end; return; } self.previous = last.*; } self.last = word_break; }}; const Osc8Update = union(enum) { open: []const u8, close,}; const AssistantContinuation = union(enum) { list_indent: usize, blockquote: struct { indent: usize, depth: usize, },}; /// Reflow assistant text to fit within `cols` while preserving inline/// SGR attributes and OSC 8 links across wrap boundaries. Paragraphs wrap at whitespace/// and move a preceding word down when that avoids a single-word tail./// Pre-existing hard newlines in `text` are honoured. The returned slice is/// owned by the caller and freed with `alloc`.pub fn wrapAssistantText(alloc: Allocator, text: []const u8, cols: u16) ![]u8 { return wrapAssistantTextWithBaseGutter(alloc, text, cols, 0, false, false, null);} fn wrapAssistantTextWithBaseGutter( alloc: Allocator, text: []const u8, cols: u16, base_gutter: u16, replace_wide_runes_at_one_content_col: bool, literal_command: bool, checkpoint: ?*build_checkpoint.BuildCheckpoint,) ![]u8 { var out: std.ArrayList(u8) = .empty; errdefer out.deinit(alloc); if (cols == 0) { return out.toOwnedSlice(alloc); } if (text.len == 0) { if (literal_command) { try appendAssistantRowStart( alloc, &out, base_gutter, .{}, null, true, ); } return out.toOwnedSlice(alloc); } var sgr: assistant_pacer.SgrState = .{}; var hyperlink: ?[]const u8 = null; const content_cols = cols -| base_gutter; var col: u16 = base_gutter + 1; var continuation = if (literal_command) null else assistantContinuation(text, 0); var word_breaks: WordBreaks = .{}; var has_word_on_row = false; var row_has_content = false; var zero_width_run_bytes: usize = 0; var i: usize = 0; while (i < text.len) { try build_checkpoint.tick(checkpoint); if (!row_has_content and !literal_command) { if (infeasibleMarkerEnd(text, i, base_gutter, cols)) |marker_end| { i = marker_end; continue; } } const ch = text[i]; if (ch == 0x1b) { const end = display_width.ansiSequenceEnd(text, i); if (end <= i) { // Preserve an incomplete trailing escape without stalling the loop. try out.appendSlice(alloc, text[i..]); break; } const seq = text[i..end]; sgr.apply(seq); if (osc8Update(seq)) |update| switch (update) { .open => |open| hyperlink = open, .close => hyperlink = null, }; if (base_gutter == 0 or row_has_content) try out.appendSlice(alloc, seq); i = end; continue; } if (ch == '\r' or ch == '\n') { if (literal_command and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, true); } if (base_gutter > 0 and row_has_content) try closeAssistantRowStyles(alloc, &out, sgr, hyperlink); try out.append(alloc, ch); col = base_gutter + 1; i += 1; continuation = if (literal_command) null else assistantContinuation(text, i); word_breaks = .{}; has_word_on_row = false; row_has_content = false; zero_width_run_bytes = 0; continue; } if (ch == '\t') { if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } const next_col = display_width.nextTabStopColumn(col, cols); try out.appendNTimes(alloc, ' ', next_col -| col); col = @max(col, next_col); i += 1; zero_width_run_bytes = 0; continue; } if (ch < 32) { if (base_gutter == 0 or row_has_content) try out.append(alloc, ch); i += 1; continue; } const unit = display_width.displayUnitAt(text, i); const w_usize = unit.cell_width; if (w_usize == 0) { if (literal_command and zero_width_run_bytes +| unit.byte_len > literal_command_zero_width_row_byte_limit) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, 1, true, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; zero_width_run_bytes = 0; } if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } try out.appendSlice(alloc, text[i .. i + unit.byte_len]); if (literal_command) zero_width_run_bytes += unit.byte_len; i += unit.byte_len; continue; } else if (literal_command) { zero_width_run_bytes = 0; } const replace_wide_unit = replace_wide_runes_at_one_content_col and content_cols == 1 and w_usize == 2; const w: u16 = if (replace_wide_unit) 1 else @intCast(w_usize); if (ch == ' ' and display_width.shouldWrapAt(col, w, cols)) { var moved_for_orphan = false; if (word_breaks.last) |word_break| { const next_word_start = i + unit.byte_len; if (remainingWordWidth(text, next_word_start) > 0 and isFinalWordOnLine(text, next_word_start) and wordBreakFits(out.items[word_break.end..], text, next_word_start, continuation, cols, base_gutter)) { const next_width = firstVisibleWidth(text[next_word_start..]) orelse w; if (try reflowAtWordBreak( alloc, &out, word_break, continuation, cols, base_gutter, next_width, literal_command, )) |reflowed_col| { col = reflowed_col; word_breaks = .{}; has_word_on_row = true; row_has_content = true; moved_for_orphan = true; } } } if (!moved_for_orphan) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, w, literal_command, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; i += unit.byte_len; continue; } } if (display_width.shouldWrapAt(col, w, cols)) { if (word_breaks.last) |word_break| { var break_to_use = word_break; if (word_breaks.previous) |previous| { if (previous.has_preceding_word and isFinalWordOnLine(text, i) and wordBreakFits(out.items[previous.end..], text, i, continuation, cols, base_gutter)) { break_to_use = previous; } } if (try reflowAtWordBreak( alloc, &out, break_to_use, continuation, cols, base_gutter, w, literal_command, )) |reflowed_col| { col = reflowed_col; word_breaks = .{}; row_has_content = true; } } } if (display_width.shouldWrapAt(col, w, cols)) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, w, literal_command, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; } if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } const space_start = out.items.len; if (replace_wide_unit) { try out.append(alloc, '?'); } else { try out.appendSlice(alloc, text[i .. i + unit.byte_len]); } col += w; if (ch == ' ' and has_word_on_row and !isContinuationMarkerSeparator(col - 1, continuation, base_gutter)) { word_breaks.record(.{ .start = space_start, .end = out.items.len, .sgr = sgr, .hyperlink = hyperlink, .has_preceding_word = word_breaks.last != null, }); } else if (ch != ' ') { has_word_on_row = true; } i += unit.byte_len; } if (literal_command and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, true); } return out.toOwnedSlice(alloc);} fn closeAssistantRowStyles( alloc: Allocator, out: *std.ArrayList(u8), sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8,) !void { if (sgr.isActive()) try out.appendSlice(alloc, "\x1b[0m"); if (hyperlink != null) try out.appendSlice(alloc, "\x1b]8;;\x1b\\");} fn appendAssistantRowStart( alloc: Allocator, out: *std.ArrayList(u8), base_gutter: u16, sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8, literal_command: bool,) !void { if (literal_command and base_gutter >= 2) { try out.appendSlice(alloc, "│ "); try out.appendNTimes(alloc, ' ', base_gutter - 2); } else { try out.appendNTimes(alloc, ' ', base_gutter); } if (sgr.isActive()) { var reopen: [64]u8 = undefined; const n = sgr.writeOpens(reopen[0..]); try out.appendSlice(alloc, reopen[0..n]); } if (hyperlink) |open| try out.appendSlice(alloc, open);} pub fn gutterWidth(cols: u16) u16 { return if (cols >= 3) 2 else if (cols == 2) 1 else 0;} pub fn prefixStructuralRows(alloc: Allocator, bytes: []const u8, gutter: u16) ![]u8 { if (gutter == 0 or bytes.len == 0) return alloc.dupe(u8, bytes); var out: std.ArrayList(u8) = .empty; errdefer out.deinit(alloc); var line_start: usize = 0; while (line_start < bytes.len) { const newline = std.mem.indexOfScalarPos(u8, bytes, line_start, '\n'); const line_end = newline orelse bytes.len; if (firstVisibleWidth(bytes[line_start..line_end]) != null) { try out.appendNTimes(alloc, ' ', gutter); } try out.appendSlice(alloc, bytes[line_start..line_end]); if (newline) |_| try out.append(alloc, '\n'); line_start = line_end + 1; } return out.toOwnedSlice(alloc);} pub fn wrapTranscriptAssistantTextInterruptible( alloc: Allocator, text: []const u8, cols: u16, checkpoint: ?*build_checkpoint.BuildCheckpoint,) ![]u8 { return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutterWidth(cols), true, false, checkpoint, );} /// Reflow canonical command output as literal terminal text. Every physical/// row owns its gutter; Markdown continuation markers are deliberately not/// interpreted, and tabs advance to the terminal's absolute eight-column/// stops. The returned slice is owned by the caller.pub fn wrapLiteralCommandOutput( alloc: Allocator, text: []const u8, cols: u16,) ![]u8 { if (cols == 0) return alloc.alloc(u8, 0); const gutter = if (cols >= 3) @as(u16, 2) else @as(u16, 0); return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutter, true, true, null, );} /// Reflows literal tool detail with the three-cell rail used by Ctrl-O.pub fn wrapLiteralToolOutput( alloc: Allocator, text: []const u8, cols: u16,) ![]u8 { return wrapLiteralToolOutputInterruptible(alloc, text, cols, null) catch |err| switch (err) { error.InputPending => unreachable, else => |other| return other, };} pub fn wrapLiteralToolOutputInterruptible( alloc: Allocator, text: []const u8, cols: u16, checkpoint: ?*build_checkpoint.BuildCheckpoint,) ![]u8 { if (cols == 0) return alloc.alloc(u8, 0); const gutter = if (cols >= 4) @as(u16, 3) else if (cols >= 3) @as(u16, 2) else @as(u16, 0); return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutter, true, true, checkpoint, );} pub const LiteralCommandOutputPrefix = struct { bytes: []u8, has_more: bool, pub fn deinit(self: *LiteralCommandOutputPrefix, alloc: Allocator) void { alloc.free(self.bytes); self.* = undefined; }}; /// Bounded compact projection. It retains three rows of lookahead so the/// assistant wrapper's word/orphan decision for the admitted rows is stable,/// but never scans or allocates in proportion to an oversized record.pub fn wrapLiteralCommandOutputPrefix( alloc: Allocator, text: []const u8, cols: u16, row_limit: usize,) !LiteralCommandOutputPrefix { if (row_limit == 0 or cols == 0) { return .{ .bytes = try alloc.alloc(u8, 0), .has_more = text.len > 0, }; } const lookahead_rows = row_limit +| 3; const source_end = literalLookaheadEnd(text, cols, lookahead_rows); const wrapped = try wrapLiteralCommandOutput(alloc, text[0..source_end], cols); defer alloc.free(wrapped); const prefix = literalPhysicalRowPrefix(wrapped, row_limit); return .{ .bytes = try alloc.dupe(u8, prefix), .has_more = source_end < text.len or literalHardLineCount(wrapped) > row_limit, };} fn literalLookaheadEnd(text: []const u8, cols: u16, row_limit: usize) usize { const gutter: u16 = if (cols >= 3) 2 else 0; var col: u16 = gutter + 1; var rows: usize = 1; var zero_width_run_bytes: usize = 0; var index: usize = 0; while (index < text.len and rows <= row_limit) { const byte = text[index]; if (byte == '\r' or byte == '\n') { rows += 1; col = gutter + 1; zero_width_run_bytes = 0; index += 1;Lines 1–500 of 1532. Load more
const std = @import("std"); const build_checkpoint = @import("build_checkpoint.zig"); const display_width = @import("../../core/shared/display_width.zig"); const assistant_pacer = @import("../assistant/pacer.zig"); const Allocator = std.mem.Allocator; /// A pathological combining-mark run gets a physical row boundary at this /// byte count. The source bytes remain exact while every retained row stays /// bounded for compact and full-view projection. pub const literal_command_zero_width_row_byte_limit: usize = 256; const WordBreak = struct { start: usize, end: usize, sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8, has_preceding_word: bool, }; /// The last two space runs on the current row, kept as reflow points for /// overflow wrapping and orphan avoidance. const WordBreaks = struct { last: ?WordBreak = null, previous: ?WordBreak = null, /// Extends `last` when adjacent; otherwise demotes it to `previous`. fn record(self: *WordBreaks, word_break: WordBreak) void { if (self.last) |*last| { if (last.end == word_break.start) { last.end = word_break.end; return; } self.previous = last.*; } self.last = word_break; } }; const Osc8Update = union(enum) { open: []const u8, close, }; const AssistantContinuation = union(enum) { list_indent: usize, blockquote: struct { indent: usize, depth: usize, }, }; /// Reflow assistant text to fit within `cols` while preserving inline /// SGR attributes and OSC 8 links across wrap boundaries. Paragraphs wrap at whitespace /// and move a preceding word down when that avoids a single-word tail. /// Pre-existing hard newlines in `text` are honoured. The returned slice is /// owned by the caller and freed with `alloc`. pub fn wrapAssistantText(alloc: Allocator, text: []const u8, cols: u16) ![]u8 { return wrapAssistantTextWithBaseGutter(alloc, text, cols, 0, false, false, null); } fn wrapAssistantTextWithBaseGutter( alloc: Allocator, text: []const u8, cols: u16, base_gutter: u16, replace_wide_runes_at_one_content_col: bool, literal_command: bool, checkpoint: ?*build_checkpoint.BuildCheckpoint, ) ![]u8 { var out: std.ArrayList(u8) = .empty; errdefer out.deinit(alloc); if (cols == 0) { return out.toOwnedSlice(alloc); } if (text.len == 0) { if (literal_command) { try appendAssistantRowStart( alloc, &out, base_gutter, .{}, null, true, ); } return out.toOwnedSlice(alloc); } var sgr: assistant_pacer.SgrState = .{}; var hyperlink: ?[]const u8 = null; const content_cols = cols -| base_gutter; var col: u16 = base_gutter + 1; var continuation = if (literal_command) null else assistantContinuation(text, 0); var word_breaks: WordBreaks = .{}; var has_word_on_row = false; var row_has_content = false; var zero_width_run_bytes: usize = 0; var i: usize = 0; while (i < text.len) { try build_checkpoint.tick(checkpoint); if (!row_has_content and !literal_command) { if (infeasibleMarkerEnd(text, i, base_gutter, cols)) |marker_end| { i = marker_end; continue; } } const ch = text[i]; if (ch == 0x1b) { const end = display_width.ansiSequenceEnd(text, i); if (end <= i) { // Preserve an incomplete trailing escape without stalling the loop. try out.appendSlice(alloc, text[i..]); break; } const seq = text[i..end]; sgr.apply(seq); if (osc8Update(seq)) |update| switch (update) { .open => |open| hyperlink = open, .close => hyperlink = null, }; if (base_gutter == 0 or row_has_content) try out.appendSlice(alloc, seq); i = end; continue; } if (ch == '\r' or ch == '\n') { if (literal_command and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, true); } if (base_gutter > 0 and row_has_content) try closeAssistantRowStyles(alloc, &out, sgr, hyperlink); try out.append(alloc, ch); col = base_gutter + 1; i += 1; continuation = if (literal_command) null else assistantContinuation(text, i); word_breaks = .{}; has_word_on_row = false; row_has_content = false; zero_width_run_bytes = 0; continue; } if (ch == '\t') { if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } const next_col = display_width.nextTabStopColumn(col, cols); try out.appendNTimes(alloc, ' ', next_col -| col); col = @max(col, next_col); i += 1; zero_width_run_bytes = 0; continue; } if (ch < 32) { if (base_gutter == 0 or row_has_content) try out.append(alloc, ch); i += 1; continue; } const unit = display_width.displayUnitAt(text, i); const w_usize = unit.cell_width; if (w_usize == 0) { if (literal_command and zero_width_run_bytes +| unit.byte_len > literal_command_zero_width_row_byte_limit) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, 1, true, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; zero_width_run_bytes = 0; } if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } try out.appendSlice(alloc, text[i .. i + unit.byte_len]); if (literal_command) zero_width_run_bytes += unit.byte_len; i += unit.byte_len; continue; } else if (literal_command) { zero_width_run_bytes = 0; } const replace_wide_unit = replace_wide_runes_at_one_content_col and content_cols == 1 and w_usize == 2; const w: u16 = if (replace_wide_unit) 1 else @intCast(w_usize); if (ch == ' ' and display_width.shouldWrapAt(col, w, cols)) { var moved_for_orphan = false; if (word_breaks.last) |word_break| { const next_word_start = i + unit.byte_len; if (remainingWordWidth(text, next_word_start) > 0 and isFinalWordOnLine(text, next_word_start) and wordBreakFits(out.items[word_break.end..], text, next_word_start, continuation, cols, base_gutter)) { const next_width = firstVisibleWidth(text[next_word_start..]) orelse w; if (try reflowAtWordBreak( alloc, &out, word_break, continuation, cols, base_gutter, next_width, literal_command, )) |reflowed_col| { col = reflowed_col; word_breaks = .{}; has_word_on_row = true; row_has_content = true; moved_for_orphan = true; } } } if (!moved_for_orphan) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, w, literal_command, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; i += unit.byte_len; continue; } } if (display_width.shouldWrapAt(col, w, cols)) { if (word_breaks.last) |word_break| { var break_to_use = word_break; if (word_breaks.previous) |previous| { if (previous.has_preceding_word and isFinalWordOnLine(text, i) and wordBreakFits(out.items[previous.end..], text, i, continuation, cols, base_gutter)) { break_to_use = previous; } } if (try reflowAtWordBreak( alloc, &out, break_to_use, continuation, cols, base_gutter, w, literal_command, )) |reflowed_col| { col = reflowed_col; word_breaks = .{}; row_has_content = true; } } } if (display_width.shouldWrapAt(col, w, cols)) { col = try appendAssistantContinuation( alloc, &out, sgr, hyperlink, continuation, cols, base_gutter, w, literal_command, ); word_breaks = .{}; has_word_on_row = false; row_has_content = true; } if (base_gutter > 0 and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, literal_command); row_has_content = true; } const space_start = out.items.len; if (replace_wide_unit) { try out.append(alloc, '?'); } else { try out.appendSlice(alloc, text[i .. i + unit.byte_len]); } col += w; if (ch == ' ' and has_word_on_row and !isContinuationMarkerSeparator(col - 1, continuation, base_gutter)) { word_breaks.record(.{ .start = space_start, .end = out.items.len, .sgr = sgr, .hyperlink = hyperlink, .has_preceding_word = word_breaks.last != null, }); } else if (ch != ' ') { has_word_on_row = true; } i += unit.byte_len; } if (literal_command and !row_has_content) { try appendAssistantRowStart(alloc, &out, base_gutter, sgr, hyperlink, true); } return out.toOwnedSlice(alloc); } fn closeAssistantRowStyles( alloc: Allocator, out: *std.ArrayList(u8), sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8, ) !void { if (sgr.isActive()) try out.appendSlice(alloc, "\x1b[0m"); if (hyperlink != null) try out.appendSlice(alloc, "\x1b]8;;\x1b\\"); } fn appendAssistantRowStart( alloc: Allocator, out: *std.ArrayList(u8), base_gutter: u16, sgr: assistant_pacer.SgrState, hyperlink: ?[]const u8, literal_command: bool, ) !void { if (literal_command and base_gutter >= 2) { try out.appendSlice(alloc, "│ "); try out.appendNTimes(alloc, ' ', base_gutter - 2); } else { try out.appendNTimes(alloc, ' ', base_gutter); } if (sgr.isActive()) { var reopen: [64]u8 = undefined; const n = sgr.writeOpens(reopen[0..]); try out.appendSlice(alloc, reopen[0..n]); } if (hyperlink) |open| try out.appendSlice(alloc, open); } pub fn gutterWidth(cols: u16) u16 { return if (cols >= 3) 2 else if (cols == 2) 1 else 0; } pub fn prefixStructuralRows(alloc: Allocator, bytes: []const u8, gutter: u16) ![]u8 { if (gutter == 0 or bytes.len == 0) return alloc.dupe(u8, bytes); var out: std.ArrayList(u8) = .empty; errdefer out.deinit(alloc); var line_start: usize = 0; while (line_start < bytes.len) { const newline = std.mem.indexOfScalarPos(u8, bytes, line_start, '\n'); const line_end = newline orelse bytes.len; if (firstVisibleWidth(bytes[line_start..line_end]) != null) { try out.appendNTimes(alloc, ' ', gutter); } try out.appendSlice(alloc, bytes[line_start..line_end]); if (newline) |_| try out.append(alloc, '\n'); line_start = line_end + 1; } return out.toOwnedSlice(alloc); } pub fn wrapTranscriptAssistantTextInterruptible( alloc: Allocator, text: []const u8, cols: u16, checkpoint: ?*build_checkpoint.BuildCheckpoint, ) ![]u8 { return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutterWidth(cols), true, false, checkpoint, ); } /// Reflow canonical command output as literal terminal text. Every physical /// row owns its gutter; Markdown continuation markers are deliberately not /// interpreted, and tabs advance to the terminal's absolute eight-column /// stops. The returned slice is owned by the caller. pub fn wrapLiteralCommandOutput( alloc: Allocator, text: []const u8, cols: u16, ) ![]u8 { if (cols == 0) return alloc.alloc(u8, 0); const gutter = if (cols >= 3) @as(u16, 2) else @as(u16, 0); return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutter, true, true, null, ); } /// Reflows literal tool detail with the three-cell rail used by Ctrl-O. pub fn wrapLiteralToolOutput( alloc: Allocator, text: []const u8, cols: u16, ) ![]u8 { return wrapLiteralToolOutputInterruptible(alloc, text, cols, null) catch |err| switch (err) { error.InputPending => unreachable, else => |other| return other, }; } pub fn wrapLiteralToolOutputInterruptible( alloc: Allocator, text: []const u8, cols: u16, checkpoint: ?*build_checkpoint.BuildCheckpoint, ) ![]u8 { if (cols == 0) return alloc.alloc(u8, 0); const gutter = if (cols >= 4) @as(u16, 3) else if (cols >= 3) @as(u16, 2) else @as(u16, 0); return wrapAssistantTextWithBaseGutter( alloc, text, cols, gutter, true, true, checkpoint, ); } pub const LiteralCommandOutputPrefix = struct { bytes: []u8, has_more: bool, pub fn deinit(self: *LiteralCommandOutputPrefix, alloc: Allocator) void { alloc.free(self.bytes); self.* = undefined; } }; /// Bounded compact projection. It retains three rows of lookahead so the /// assistant wrapper's word/orphan decision for the admitted rows is stable, /// but never scans or allocates in proportion to an oversized record. pub fn wrapLiteralCommandOutputPrefix( alloc: Allocator, text: []const u8, cols: u16, row_limit: usize, ) !LiteralCommandOutputPrefix { if (row_limit == 0 or cols == 0) { return .{ .bytes = try alloc.alloc(u8, 0), .has_more = text.len > 0, }; } const lookahead_rows = row_limit +| 3; const source_end = literalLookaheadEnd(text, cols, lookahead_rows); const wrapped = try wrapLiteralCommandOutput(alloc, text[0..source_end], cols); defer alloc.free(wrapped); const prefix = literalPhysicalRowPrefix(wrapped, row_limit); return .{ .bytes = try alloc.dupe(u8, prefix), .has_more = source_end < text.len or literalHardLineCount(wrapped) > row_limit, }; } fn literalLookaheadEnd(text: []const u8, cols: u16, row_limit: usize) usize { const gutter: u16 = if (cols >= 3) 2 else 0; var col: u16 = gutter + 1; var rows: usize = 1; var zero_width_run_bytes: usize = 0; var index: usize = 0; while (index < text.len and rows <= row_limit) { const byte = text[index]; if (byte == '\r' or byte == '\n') { rows += 1; col = gutter + 1; zero_width_run_bytes = 0; index += 1;