1532 lines · 52.3 KB · Zig
RawDownload
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.