| ... | @@ -95,6 +95,62 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc: | ... | @@ -95,6 +95,62 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc: |
| 95 | return (try forEnum(out, in, prompt, alloc, enum { y, n }, .y)) == .y; | 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 | // pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T { | 154 | // pub fn forNumber(comptime prompt: []const u8, alloc: *std.mem.Allocator, comptime T: type, default: ?T) !T { |
| 99 | // try out.print(comptime ansi.color.Fg(.Green, "? "), .{}); | 155 | // try out.print(comptime ansi.color.Fg(.Green, "? "), .{}); |
| 100 | // try out.print(comptime ansi.color.Bold(prompt ++ " "), .{}); | 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,3 +190,11 @@ pub fn forConfirm(out: anytype, in: anytype, comptime prompt: []const u8, alloc: |
| 134 | | 190 | |
| 135 | // return value; | 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; |