chor: use new Writer interface for terminal's Writer; fix test cases
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 3m38s

This commit is contained in:
2025-10-01 10:59:29 +02:00
parent aa17e13b99
commit 832fc45c3e
6 changed files with 46 additions and 44 deletions

View File

@@ -13,7 +13,7 @@ pub fn reset(this: *Cell) void {
this.cp = ' ';
}
pub fn value(this: Cell, writer: anytype) !void {
pub fn value(this: Cell, writer: *std.Io.Writer) !void {
try this.style.value(writer, this.cp);
}
@@ -29,17 +29,15 @@ test "ascii styled text" {
.{ .cp = 's', .style = .{ .fg = .light_green, .bg = .black, .emphasis = &.{.underline} } },
};
var string = try std.ArrayList(u8).initCapacity(std.testing.allocator, 4);
defer string.deinit(std.testing.allocator);
const writer = string.writer(std.testing.allocator);
var writer = std.Io.Writer.Allocating.init(std.testing.allocator);
defer writer.deinit();
for (cells) |cell| {
try cell.value(writer);
try cell.value(&writer.writer);
}
try std.testing.expectEqualSlices(
u8,
"\x1b[38;5;10;48;5;8;59mY\x1b[0m\x1b[39;49;59;1;4mv\x1b[0m\x1b[39;49;59;3me\x1b[0m\x1b[38;5;2;48;5;16;59;4ms\x1b[0m",
string.items,
writer.writer.buffer[0..writer.writer.end],
);
}
@@ -51,16 +49,14 @@ test "utf-8 styled text" {
.{ .cp = '┘', .style = .{ .fg = .light_green, .bg = .black, .emphasis = &.{.underline} } },
};
var string = try std.ArrayList(u8).initCapacity(std.testing.allocator, 4);
defer string.deinit(std.testing.allocator);
const writer = string.writer(std.testing.allocator);
var writer = std.Io.Writer.Allocating.init(std.testing.allocator);
defer writer.deinit();
for (cells) |cell| {
try cell.value(writer);
try cell.value(&writer.writer);
}
try std.testing.expectEqualSlices(
u8,
"\x1b[38;5;10;48;5;8;59m╭\x1b[0m\x1b[39;49;59m─\x1b[0m\x1b[39;49;59m┄\x1b[0m\x1b[38;5;2;48;5;16;59;4m┘\x1b[0m",
string.items,
writer.writer.buffer[0..writer.writer.end],
);
}

View File

@@ -20,22 +20,21 @@ pub const Color = enum(u8) {
// TODO might be useful to use the std.ascii stuff!
pub inline fn write(this: Color, writer: anytype, comptime coloring: enum { fg, bg, ul }) !void {
pub inline fn write(this: Color, writer: *std.Io.Writer, comptime coloring: enum { fg, bg, ul }) !void {
if (this == .default) {
switch (coloring) {
.fg => try format(writer, "39", .{}),
.bg => try format(writer, "49", .{}),
.ul => try format(writer, "59", .{}),
.fg => try writer.printAscii("39", .{}),
.bg => try writer.printAscii("49", .{}),
.ul => try writer.printAscii("59", .{}),
}
} else {
switch (coloring) {
.fg => try format(writer, "38;5;{d}", .{@intFromEnum(this)}),
.bg => try format(writer, "48;5;{d}", .{@intFromEnum(this)}),
.ul => try format(writer, "58;5;{d}", .{@intFromEnum(this)}),
.fg => try writer.print("38;5;{d}", .{@intFromEnum(this)}),
.bg => try writer.print("48;5;{d}", .{@intFromEnum(this)}),
.ul => try writer.print("58;5;{d}", .{@intFromEnum(this)}),
}
}
}
};
const std = @import("std");
const format = std.fmt.format;

View File

@@ -88,7 +88,7 @@ pub const Buffered = struct {
try terminal.hideCursor();
// TODO measure timings of rendered frames?
var cursor_position: ?Point = null;
const writer = terminal.writer();
var writer = terminal.writer();
const s = this.screen;
const vs = this.virtual_screen;
for (0..this.size.y) |row| {
@@ -110,7 +110,7 @@ pub const Buffered = struct {
// render differences found in virtual screen
try terminal.setCursorPosition(.{ .y = @truncate(row), .x = @truncate(col) });
try cvs.value(writer);
try cvs.value(&writer);
// update screen to be the virtual screen for the next frame
s[idx] = vs[idx];
}

View File

@@ -42,27 +42,27 @@ pub fn eql(this: Style, other: Style) bool {
// TODO might be useful to use the std.ascii stuff!
pub fn value(this: Style, writer: anytype, cp: u21) !void {
pub fn value(this: Style, writer: *std.Io.Writer, cp: u21) !void {
var buffer: [4]u8 = undefined;
const bytes = try unicode.utf8Encode(cp, &buffer);
assert(bytes > 0);
// build ansi sequence for 256 colors ...
// foreground
try format(writer, "\x1b[", .{});
try writer.printAscii("\x1b[", .{});
try this.fg.write(writer, .fg);
// background
try format(writer, ";", .{});
try writer.printAsciiChar(';', .{});
try this.bg.write(writer, .bg);
// underline
// FIX assert that if the underline property is set that the ul style and the attribute for underlining is available
try format(writer, ";", .{});
try writer.printAsciiChar(';', .{});
try this.ul.write(writer, .ul);
// append styles (aka attributes like bold, italic, strikethrough, etc.)
for (this.emphasis) |attribute| try format(writer, ";{d}", .{@intFromEnum(attribute)});
try format(writer, "m", .{});
for (this.emphasis) |attribute| try writer.print(";{d}", .{@intFromEnum(attribute)});
try writer.printAsciiChar('m', .{});
// content
try format(writer, "{s}", .{buffer[0..bytes]});
try format(writer, "\x1b[0m", .{});
try writer.printAscii(buffer[0..bytes], .{});
try writer.printAscii("\x1b[0m", .{});
}
// TODO implement helper functions for terminal capabilities:
@@ -73,6 +73,5 @@ const std = @import("std");
const unicode = std.unicode;
const meta = std.meta;
const assert = std.debug.assert;
const format = std.fmt.format;
const Color = @import("color.zig").Color;
const Style = @This();

View File

@@ -62,17 +62,24 @@ pub fn write(buf: []const u8) !usize {
return try posix.write(posix.STDIN_FILENO, buf);
}
fn contextWrite(context: *const anyopaque, data: []const u8) anyerror!usize {
_ = context;
return try posix.write(posix.STDOUT_FILENO, data);
fn drainFn(w: *std.Io.Writer, data: []const []const u8, splat: usize) error{WriteFailed}!usize {
_ = w;
if (data.len == 0 or splat == 0) return 0;
var len: usize = 0;
for (data) |bytes| len += posix.write(posix.STDOUT_FILENO, bytes) catch return error.WriteFailed;
return len;
}
const Writer = std.io.AnyWriter;
pub fn writer() Writer {
// TODO I now need to add that much, for just the one function above?
pub fn writer() std.Io.Writer {
return .{
.context = &.{},
.writeFn = contextWrite,
.vtable = &.{
.drain = drainFn,
.flush = std.Io.Writer.noopFlush,
},
.buffer = &.{},
};
}
@@ -231,6 +238,7 @@ const log = std.log.scoped(.terminal);
const std = @import("std");
const mem = std.mem;
const posix = std.posix;
const assert = std.debug.assert;
const ctlseqs = @import("ctlseqs.zig");
const input = @import("input.zig");
const Key = input.Key;

View File

@@ -167,10 +167,10 @@ pub fn expectEqualCells(origin: Point, size: Point, expected: []const Cell, actu
var actual_cps = try std.ArrayList(Cell).initCapacity(allocator, size.x);
defer actual_cps.deinit(allocator);
var output = try std.ArrayList(u8).initCapacity(allocator, expected_cps.capacity * actual_cps.capacity + 5 * size.y);
defer output.deinit(allocator);
var allocating_writer = std.Io.Writer.Allocating.init(allocator);
defer allocating_writer.deinit();
const writer = output.writer(allocator);
var writer = &allocating_writer.writer;
var differ = false;
const expected_centered = try center(allocator, "Expected Screen", size.x, " ");
@@ -212,7 +212,7 @@ pub fn expectEqualCells(origin: Point, size: Point, expected: []const Cell, actu
var stdout_buffer: [1024]u8 = undefined;
var stdout = std.fs.File.stdout().writer(&stdout_buffer);
const stdout_writer = &stdout.interface;
try stdout_writer.writeAll(output.items);
try stdout_writer.writeAll(writer.buffer[0..writer.end]);
try stdout_writer.flush();
return error.TestExpectEqualCells;
}