跳到主要内容
ansicode
Zig

在 Zig 中使用 ANSI 转义码

Zig 标准库没有颜色辅助,但字节字面量是一等公民 —— `"\x1b[31m"` 是 `*const [5:0]u8`,可直接通过 `std.io.getStdOut().writer().print`(或 `std.debug.print` 用于快速原型)输出,无需任何编码处理。自 Zig 0.11 起,规范的能力门控是 `std.io.tty.detectConfig(std.io.getStdOut())` —— 它返回带标签的联合(`.no_color`、`.escape_codes`、`.windows_api`),遵守 `NO_COLOR`、检查 `isatty(2)`,并在 VT 之前的 Windows Conhost 上选择正确的后端。将写入站点包在 `switch (config) { ... }` 内,仅在 `.escape_codes` 分支输出原始转义。 超出零散 print 的场景:**libvaxis**(rockorager/libvaxis)是现代全屏 TUI 库 —— 纯 Zig 实现、可在支持时使用 kitty 键盘协议、自带渲染管线。**mibu**(xyaman/mibu)是小而精的 ANSI 辅助库,用于非全屏输出(`mibu.color.print(.{ .fg = .red }, "error", .{})`)。**zig-spoon** 提供最小化的终端控制(原始模式 + 光标定位),无需完整 TUI 框架。三者都建立在本站记录的字节序列之上。

推荐库

  • std.io.tty

    标准库能力检测器 —— `std.io.tty.detectConfig(file)` 返回 `.no_color` / `.escape_codes` / `.windows_api`。遵守 `NO_COLOR`、检查 `isatty`、在旧 Conhost 上选择 Win32 控制台 API。自 Zig 0.11 起,每个颜色写入站点都应放在该门控之后。

  • libvaxis

    现代全屏 TUI 库 —— 纯 Zig、无 C 依赖。在支持的终端上支持 kitty 键盘协议、鼠标、粘贴、OSC 8 超链接、sixel 图像。`zit` git TUI、`vaxe` 编辑器以及多个内部工具 TUI 的底层。紧跟 Zig master 分支。

  • mibu

    小型 ANSI 辅助 —— 颜色、光标、屏幕、原始模式 termios 封装成友好的 Zig API。`mibu.color.print(.{ .fg = .red }, "error", .{})`、`mibu.cursor.goto(stdout, x, y)`。与 std.io.tty.detectConfig 搭配:mibu 输出原始转义,你在调用点做门控。

  • zig-spoon

    极简终端控制库 —— 原始模式切换、光标定位、按键解析,无需完整 TUI 框架。在你需要原始键盘输入 + 少量转义写入(文本编辑器、REPL、行内选择器),但不需要 Window / Panel 层级时使用。

常用写法

通过 std.io.getStdOut().writer() 直接写字节字面量
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    // \x1b is the standard hex escape — Zig string literals are []const u8,
    // bytes pass through untouched. No \e shortcut, no encoding work.
    try stdout.print("\x1b[1;31merror:\x1b[0m permission denied\n", .{});
    try stdout.print("\x1b[1;32mok:\x1b[0m all 142 tests passed\n", .{});
}
使用 std.io.tty.detectConfig 做能力门控
const std = @import("std");

pub fn main() !void {
    const stdout_file = std.io.getStdOut();
    const stdout = stdout_file.writer();
    const config = std.io.tty.detectConfig(stdout_file);

    // config is a tagged union: .no_color, .escape_codes, .windows_api.
    // setColor() routes to the right backend automatically.
    try config.setColor(stdout, .bold);
    try config.setColor(stdout, .red);
    try stdout.writeAll("error:");
    try config.setColor(stdout, .reset);
    try stdout.writeAll(" permission denied\n");
}
使用 mibu 实现彩色输出
// build.zig.zon: .mibu = .{ .url = "https://github.com/xyaman/mibu/...", ... }
const std = @import("std");
const mibu = @import("mibu");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try mibu.color.print(stdout, .{ .fg = .red, .style = .bold }, "error:", .{});
    try stdout.writeAll(" permission denied\n");

    // Truecolor:
    try mibu.color.print(stdout, .{
        .fg = .{ .rgb = .{ 203, 166, 247 } },
    }, "lavender truecolor\n", .{});
}
使用 libvaxis 构建全屏 TUI 骨架
// build.zig.zon: .vaxis = .{ .url = "https://github.com/rockorager/libvaxis/...", ... }
const std = @import("std");
const vaxis = @import("vaxis");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

    var vx = try vaxis.init(alloc, .{});
    defer vx.deinit(alloc, std.io.tty.tty());

    var tty = try vaxis.Tty.init();
    defer tty.deinit();

    try vx.enterAltScreen(tty.anyWriter());
    defer vx.exitAltScreen(tty.anyWriter()) catch {};

    var loop: vaxis.Loop(vaxis.Event) = .{ .tty = &tty, .vaxis = &vx };
    try loop.init();
    try loop.start();
    defer loop.stop();

    while (true) {
        const event = loop.nextEvent();
        switch (event) {
            .key_press => |k| if (k.matches('q', .{})) break,
            .winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
            else => {},
        }
        const win = vx.window();
        win.clear();
        _ = try win.printSegment(.{ .text = "press q to quit", .style = .{ .fg = .{ .index = 2 } } }, .{});
        try vx.render(tty.anyWriter());
    }
}

相关序列

其他语言