| ... | ... | @@ -95,6 +95,62 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc: |
| 95 | 95 | return (try forEnum(out, in, prompt, alloc, enum { y, n }, .y)) == .y; |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | pub fn forStruct(out: anytype, in: anytype, comptime prompt: []const u8, comptime S: type, comptime name: std.meta.FieldEnum(S), items: []const S) !usize { |
| 99 | comptime std.debug.assert(@typeInfo(S) == .@"struct"); |
| 100 | std.debug.assert(items.len > 0); |
| 101 | |
| 102 | try out.print(comptime ansi.color.Fg(.Green, "? "), .{}); |
| 103 | try out.print(comptime ansi.color.Bold(prompt ++ "\n"), .{}); |
| 104 | |
| 105 | const STDIN_FILENO = 0; |
| 106 | const TCSAFLUSH = 2; |
| 107 | const ECHO: std.os.linux.tcflag_t = @bitCast(std.os.linux.tc_lflag_t{ .ECHO = true }); |
| 108 | const ICANON: std.os.linux.tcflag_t = @bitCast(std.os.linux.tc_lflag_t{ .ICANON = true }); |
| 109 | |
| 110 | var orig_termios: struct_termios = undefined; |
| 111 | _ = tcgetattr(STDIN_FILENO, &orig_termios); |
| 112 | defer _ = tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); |
| 113 | |
| 114 | var raw_termios: struct_termios = orig_termios; |
| 115 | raw_termios.c_lflag &= ~(ECHO | ICANON); |
| 116 | _ = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_termios); |
| 117 | |
| 118 | var r: usize = 0; |
| 119 | _ = &r; |
| 120 | while (true) { |
| 121 | for (items, 0..) |itm, i| { |
| 122 | if (i == r) { |
| 123 | try out.print(comptime ansi.color.Fg(.Cyan, "> {s}\n"), .{@field(itm, @tagName(name))}); |
| 124 | } else { |
| 125 | try out.print(" {s}\n", .{@field(itm, @tagName(name))}); |
| 126 | } |
| 127 | } |
| 128 | switch (@as(ansi.ascii, @enumFromInt(try in.readByte()))) { |
| 129 | .LF => { |
| 130 | break; |
| 131 | }, |
| 132 | .ESC => { |
| 133 | std.debug.assert(try in.readByte() == '['); |
| 134 | switch (try in.readByte()) { |
| 135 | 'A' => { //up |
| 136 | r = if (r > 0) r - 1 else items.len - 1; |
| 137 | }, |
| 138 | 'B' => { //down |
| 139 | r += 1; |
| 140 | if (r == items.len) r = 0; |
| 141 | }, |
| 142 | else => {}, |
| 143 | } |
| 144 | }, |
| 145 | else => {}, |
| 146 | } |
| 147 | try clean(out, items.len); |
| 148 | } |
| 149 | try clean(out, items.len + 1); |
| 150 | _ = try answer(out, prompt, []const u8, "{s}", @field(items[r], @tagName(name))); |
| 151 | return r; |
| 152 | } |
| 153 | |
| 98 | 154 | // pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T { |
| 99 | 155 | // try out.print(comptime ansi.color.Fg(.Green, "? "), .{}); |
| 100 | 156 | // try out.print(comptime ansi.color.Bold(prompt ++ " "), .{}); |
| ... | ... | @@ -134,3 +190,11 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc: |
| 134 | 190 | |
| 135 | 191 | // return value; |
| 136 | 192 | // } |
| 193 | |
| 194 | extern fn tcgetattr(fd: c_int, termios_p: *struct_termios) c_int; |
| 195 | extern fn tcsetattr(fd: c_int, optional_actions: c_int, termios_p: *const struct_termios) c_int; |
| 196 | |
| 197 | const impdef = @cImport({ |
| 198 | @cInclude("termios.h"); |
| 199 | }); |
| 200 | const struct_termios = impdef.struct_termios; |