Something went wrong. Try again.
A charm-like tui library
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140//! Cell, Style, and Link primitives.//!//! Port of ultraviolet's `cell.go`. The cell grid model: a `Cell` holds a//! grapheme-cluster `content` slice, a cell-width, a style, and an optional//! hyperlink. Wide glyphs occupy N cells where the leading cell has//! `width == N` and the continuation cells are width-0 placeholders (content//! is the empty slice). `empty_cell` is a square space (content " ", width 1).//!//! Style is a value type mirroring `uv.Style`: foreground/background/underline//! colors as optional `infrared.color.Color`, an underline style, and an//! attribute bitmask. SGR sequences are emitted attribute-by-attribute (Brain//! decision #4) rather than as one combined CSI; `styleDiff` mirrors//! `StyleDiff` in `cell.go` including its exact set/reset ordering.
const std = @import("std");const ir = @import("root.zig");
const t = std.testing;const Writer = std.Io.Writer;const Allocator = std.mem.Allocator;const Method = ir.width.Method;const Color = ir.Color;const Style = ir.Style;const Link = ir.Link;
const Cell = @This();
content: []const u8 = "",style: Style = .{},link: Link = .{},width: u8 = 0,
/// Zero value is the wide-cell placeholder: empty content, zero width./// Mirrors Go's zero-valued `Cell`.pub const empty: Cell = .{};
/// A square (1x1) space cell.pub const space: Cell = .{ .content = " ", .width = 1 };
pub fn of(content: []const u8, width: u8) Cell { return .{ .content = content, .width = width };}
pub fn equal(lhs: Cell, rhs: Cell) bool { return lhs.width == rhs.width and std.mem.eql(u8, lhs.content, rhs.content) and lhs.style.equal(rhs.style) and lhs.link.equal(rhs.link);}
pub fn clone(c: Cell) Cell { return c;}
/// Makes the cell an empty (square space) cell.pub fn emptyize(c: *Cell) void { c.content = " "; c.width = 1;}
/// Whether the cell is the continuation column of a wide glyph.pub fn isWidePlaceholder(c: Cell) bool { return c.width == 0;}
pub fn contentSlice(c: Cell) []const u8 { return c.content;}
/// Normalizes a color: `.none` (explicit default/reset) is folded to/// `null` (absent) so infra styles only ever hold real colors or absence./// This avoids spurious style diffs when a `.none` gets compared to `null`.pub fn optColor(c: Color) ?Color { return switch (c) { .none => null, else => c, };}
/// Creates a cell from a single grapheme cluster using the given width/// method. An empty cluster yields the wide placeholder; a single space/// yields `empty_cell`.pub fn new(method: Method, gr: []const u8) Cell { if (gr.len == 0) return .empty; if (gr.len == 1 and gr[0] == ' ') return .space; return .{ .content = gr, .width = @intCast(ir.width.stringWidth(method, gr)) };}
test "empty_cell and placeholder defaults" { const blank_cell: Cell = .space; try t.expect(blank_cell.width == 1); // try t.expectEqual(@as(u8, 1), blank_cell.width); try t.expectEqualStrings(" ", blank_cell.content); const z: Cell = .empty; try t.expect(z.width == 0); // try t.expectEqual(@as(u8, 0), z.width); try t.expect(z.isWidePlaceholder());}
test "newCell widths" { const c = new(.wcwidth, "a"); try t.expect(c.width == 1); // try testing.expectEqual(@as(u8, 1), c.width); const wide = new(.wcwidth, "世"); try t.expect(wide.width == 2); // try testing.expectEqual(@as(u8, 2), wide.width); try t.expectEqualStrings("世", wide.content); const sp = new(.wcwidth, " "); try t.expect(sp.equal(.space)); const ph = new(.wcwidth, ""); try t.expect(ph.isWidePlaceholder());}
test "fuzz cell" { try t.fuzz({}, fuzzCell, .{ .corpus = &.{ "", "a", " ", "世", "e\u{301}", "\xc3\xa9", "\xe4\xb8", "ab", }, });}
fn fuzzCell(_: void, smith: *t.Smith) anyerror!void { var buf: [64]u8 = undefined; const len = smith.slice(&buf); const m: Method = if (smith.value(bool)) .wcwidth else .grapheme; const cell = new(m, buf[0..len]); _ = cell.width; _ = cell.isWidePlaceholder(); _ = cell.contentSlice(); _ = cell.equal(.empty); _ = cell.equal(.space);}