const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const Action = @import("ghostty.zig").Action; const args = @import("args.zig"); const x11_color = @import("../terminal/main.zig").x11_color; const vaxis = @import("vaxis"); const tui = @import("tui.zig"); pub const Options = struct { pub fn deinit(self: Options) void { _ = self; } /// If `true`, print without formatting even if printing to a tty plain: bool = false, /// Enables "-h" and "--help" to work. pub fn help(self: Options) !void { _ = self; return Action.help_error; } }; /// The `list-colors` command is used to list all the named RGB colors in /// Ghostty. /// /// Flags: /// /// * `--plain`: will disable formatting and make the output more /// friendly for Unix tooling. This is default when not printing to a tty. pub fn run(alloc: Allocator) !u8 { var opts: Options = .{}; defer opts.deinit(); { var iter = try args.argsIterator(alloc); defer iter.deinit(); try args.parse(Options, alloc, &opts, &iter); } var keys: std.ArrayList([]const u8) = .empty; defer keys.deinit(alloc); for (x11_color.map.keys()) |key| try keys.append(alloc, key); std.mem.sortUnstable([]const u8, keys.items, {}, struct { fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool { return std.ascii.orderIgnoreCase(lhs, rhs) == .lt; } }.lessThan); // Despite being under the posix namespace, this also works on Windows as of zig 0.13.0 var stdout: std.fs.File = .stdout(); if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) { var arena = std.heap.ArenaAllocator.init(alloc); defer arena.deinit(); return prettyPrint(arena.allocator(), keys.items); } else { var buffer: [4096]u8 = undefined; var stdout_writer = stdout.writer(&buffer); const writer = &stdout_writer.interface; for (keys.items) |name| { const rgb = x11_color.map.get(name).?; try writer.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{ name, rgb.r, rgb.g, rgb.b, }); } } return 0; } fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 { // Set up vaxis var buf: [1024]u8 = undefined; var tty = try vaxis.Tty.init(&buf); defer tty.deinit(); var vx = try vaxis.init(alloc, .{}); defer vx.deinit(alloc, tty.writer()); // We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an // event loop to auto-enable it. vx.caps.unicode = .unicode; try tty.writer().writeAll(vaxis.ctlseqs.unicode_set); defer tty.writer().writeAll(vaxis.ctlseqs.unicode_reset) catch {}; const winsize: vaxis.Winsize = switch (builtin.os.tag) { // We use some default, it doesn't really matter for what // we're doing because we don't do any wrapping. .windows => .{ .rows = 24, .cols = 120, .x_pixel = 1024, .y_pixel = 768, }, else => try vaxis.Tty.getWinsize(tty.fd), }; try vx.resize(alloc, tty.writer(), winsize); const win = vx.window(); var max_name_len: usize = 0; for (keys) |name| { if (name.len > max_name_len) max_name_len = name.len; } // max name length plus " = #RRGGBB XX" plus " " gutter between columns const column_size = max_name_len + 15; // add two to take into account lack of gutter after last column const columns: usize = @divFloor(win.width + 2, column_size); var i: usize = 0; const step = @divFloor(keys.len, columns) + 1; while (i < step) : (i += 1) { win.clear(); var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false }; for (0..columns) |j| { const k = i + (step * j); if (k >= keys.len) continue; const name = keys[k]; const rgb = x11_color.map.get(name).?; const style1: vaxis.Style = .{ .fg = .{ .rgb = .{ rgb.r, rgb.g, rgb.b }, }, }; const style2: vaxis.Style = .{ .fg = .{ .rgb = .{ rgb.r, rgb.g, rgb.b }, }, .bg = .{ .rgb = .{ rgb.r, rgb.g, rgb.b }, }, }; // name of the color result = win.printSegment( .{ .text = name }, .{ .col_offset = result.col }, ); // push the color data to the end of the column for (0..max_name_len - name.len) |_| { result = win.printSegment( .{ .text = " " }, .{ .col_offset = result.col }, ); } result = win.printSegment( .{ .text = " = " }, .{ .col_offset = result.col }, ); // rgb triple result = win.printSegment(.{ .text = try std.fmt.allocPrint( alloc, "#{x:0>2}{x:0>2}{x:0>2}", .{ rgb.r, rgb.g, rgb.b, }, ), .style = style1, }, .{ .col_offset = result.col }); result = win.printSegment( .{ .text = " " }, .{ .col_offset = result.col }, ); // colored block result = win.printSegment( .{ .text = " ", .style = style2, }, .{ .col_offset = result.col }, ); // add the gutter if needed if (j + 1 < columns) { result = win.printSegment( .{ .text = " ", }, .{ .col_offset = result.col }, ); } } // clear the rest of the line while (result.col != 0) { result = win.printSegment( .{ .text = " ", }, .{ .col_offset = result.col }, ); } // output the data try vx.prettyPrint(tty.writer()); } return 0; }