this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

widgets(terminal): use types for c0 and csi

+177 -130
+18 -5
src/widgets/terminal/Parser.zig
··· 3 3 4 4 const std = @import("std"); 5 5 const Reader = std.io.AnyReader; 6 + const ansi = @import("ansi.zig"); 6 7 7 8 /// A terminal event 8 9 const Event = union(enum) { 9 10 print: []const u8, 10 - c0: u8, 11 + c0: ansi.C0, 11 12 escape: []const u8, 12 13 ss2: u8, 13 14 ss3: u8, 14 - csi: []const u8, 15 + csi: ansi.CSI, 15 16 osc: []const u8, 16 17 apc: []const u8, 17 18 }; ··· 52 53 // C0 control 53 54 0x00...0x1a, 54 55 0x1c...0x1f, 55 - => return .{ .c0 = b }, 56 + => return .{ .c0 = @enumFromInt(b) }, 56 57 else => { 57 58 try self.buf.append(b); 58 59 return self.parseGround(reader); ··· 129 130 } 130 131 131 132 inline fn parseCsi(self: *Parser, reader: Reader) !Event { 133 + var intermediate: ?u8 = null; 134 + var pm: ?u8 = null; 135 + 132 136 while (true) { 133 137 const b = try reader.readByte(); 134 - try self.buf.append(b); 135 138 switch (b) { 139 + 0x20...0x2F => intermediate = b, 140 + 0x30...0x3B => try self.buf.append(b), 141 + 0x3C...0x3F => pm = b, // we only allow one 136 142 // Really we should execute C0 controls, but we just ignore them 137 - 0x40...0xFF => return .{ .csi = self.buf.items }, 143 + 0x40...0xFF => return .{ 144 + .csi = .{ 145 + .intermediate = intermediate, 146 + .private_marker = pm, 147 + .params = self.buf.items, 148 + .final = b, 149 + }, 150 + }, 138 151 else => continue, 139 152 } 140 153 }
+28 -81
src/widgets/terminal/Screen.zig
··· 2 2 const assert = std.debug.assert; 3 3 const vaxis = @import("../../main.zig"); 4 4 5 + const ansi = @import("ansi.zig"); 6 + 5 7 const log = std.log.scoped(.terminal); 6 8 7 9 const Screen = @This(); ··· 185 187 self.cursor.row += 1; 186 188 } 187 189 188 - fn Parameter(T: type) type { 189 - return struct { 190 - const Self = @This(); 191 - val: T, 192 - // indicates the next parameter is a sub-parameter 193 - has_sub: bool = false, 194 - is_empty: bool = false, 195 - 196 - const Iterator = struct { 197 - bytes: []const u8, 198 - idx: usize = 0, 199 - 200 - fn next(self: *Iterator) ?Self { 201 - const start = self.idx; 202 - var val: T = 0; 203 - while (self.idx < self.bytes.len) { 204 - defer self.idx += 1; // defer so we trigger on return as well 205 - const b = self.bytes[self.idx]; 206 - switch (b) { 207 - 0x30...0x39 => { 208 - val = (val * 10) + (b - 0x30); 209 - if (self.idx == self.bytes.len - 1) return .{ .val = val }; 210 - }, 211 - ':', ';' => return .{ 212 - .val = val, 213 - .is_empty = self.idx == start, 214 - .has_sub = b == ':', 215 - }, 216 - else => return null, 217 - } 218 - } 219 - return null; 220 - } 221 - }; 222 - }; 223 - } 224 - 225 - pub fn sgr(self: *Screen, seq: []const u8) void { 226 - if (seq.len == 0) { 190 + pub fn sgr(self: *Screen, seq: ansi.CSI) void { 191 + if (seq.params.len == 0) { 227 192 self.cursor.style = .{}; 228 193 return; 229 194 } 230 - switch (seq[0]) { 231 - 0x30...0x39 => {}, 232 - else => return, // TODO: handle private indicator sequences 233 - } 234 195 235 - var iter: Parameter(u8).Iterator = .{ .bytes = seq }; 196 + var iter = seq.iterator(u8); 236 197 while (iter.next()) |ps| { 237 - switch (ps.val) { 198 + switch (ps) { 238 199 0 => self.cursor.style = .{}, 239 200 1 => self.cursor.style.bold = true, 240 201 2 => self.cursor.style.dim = true, 241 202 3 => self.cursor.style.italic = true, 242 203 4 => { 243 - const kind: vaxis.Style.Underline = if (ps.has_sub) blk: { 244 - const ul = iter.next() orelse break :blk .single; 245 - break :blk @enumFromInt(ul.val); 246 - } else .single; 204 + const kind: vaxis.Style.Underline = if (iter.next_is_sub) 205 + @enumFromInt(iter.next() orelse 1) 206 + else 207 + .single; 247 208 self.cursor.style.ul_style = kind; 248 209 }, 249 210 5 => self.cursor.style.blink = true, ··· 261 222 27 => self.cursor.style.reverse = false, 262 223 28 => self.cursor.style.invisible = false, 263 224 29 => self.cursor.style.strikethrough = false, 264 - 30...37 => self.cursor.style.fg = .{ .index = ps.val - 30 }, 225 + 30...37 => self.cursor.style.fg = .{ .index = ps - 30 }, 265 226 38 => { 266 227 // must have another parameter 267 228 const kind = iter.next() orelse return; 268 - switch (kind.val) { 229 + switch (kind) { 269 230 2 => { // rgb 270 231 const r = r: { 271 232 // First param can be empty 272 233 var ps_r = iter.next() orelse return; 273 - while (ps_r.is_empty) { 234 + if (iter.is_empty) 274 235 ps_r = iter.next() orelse return; 275 - } 276 - break :r ps_r.val; 236 + break :r ps_r; 277 237 }; 278 - const g = g: { 279 - const ps_g = iter.next() orelse return; 280 - break :g ps_g.val; 281 - }; 282 - const b = b: { 283 - const ps_b = iter.next() orelse return; 284 - break :b ps_b.val; 285 - }; 238 + const g = iter.next() orelse return; 239 + const b = iter.next() orelse return; 286 240 self.cursor.style.fg = .{ .rgb = .{ r, g, b } }; 287 241 }, 288 242 5 => { 289 243 const idx = iter.next() orelse return; 290 - self.cursor.style.fg = .{ .index = idx.val }; 244 + self.cursor.style.fg = .{ .index = idx }; 291 245 }, // index 292 246 else => return, 293 247 } 294 248 }, 295 249 39 => self.cursor.style.fg = .default, 296 - 40...47 => self.cursor.style.bg = .{ .index = ps.val - 40 }, 250 + 40...47 => self.cursor.style.bg = .{ .index = ps - 40 }, 297 251 48 => { 298 252 // must have another parameter 299 253 const kind = iter.next() orelse return; 300 - switch (kind.val) { 254 + switch (kind) { 301 255 2 => { // rgb 302 256 const r = r: { 303 257 // First param can be empty 304 258 var ps_r = iter.next() orelse return; 305 - while (ps_r.is_empty) { 259 + if (iter.is_empty) 306 260 ps_r = iter.next() orelse return; 307 - } 308 - break :r ps_r.val; 309 - }; 310 - const g = g: { 311 - const ps_g = iter.next() orelse return; 312 - break :g ps_g.val; 313 - }; 314 - const b = b: { 315 - const ps_b = iter.next() orelse return; 316 - break :b ps_b.val; 261 + break :r ps_r; 317 262 }; 263 + const g = iter.next() orelse return; 264 + const b = iter.next() orelse return; 318 265 self.cursor.style.bg = .{ .rgb = .{ r, g, b } }; 319 266 }, 320 - 5 => { // index 267 + 5 => { 321 268 const idx = iter.next() orelse return; 322 - self.cursor.style.bg = .{ .index = idx.val }; 323 - }, 269 + self.cursor.style.bg = .{ .index = idx }; 270 + }, // index 324 271 else => return, 325 272 } 326 273 }, 327 274 49 => self.cursor.style.bg = .default, 328 - 90...97 => self.cursor.style.fg = .{ .index = ps.val - 90 + 8 }, 329 - 100...107 => self.cursor.style.bg = .{ .index = ps.val - 100 + 8 }, 275 + 90...97 => self.cursor.style.fg = .{ .index = ps - 90 + 8 }, 276 + 100...107 => self.cursor.style.bg = .{ .index = ps - 100 + 8 }, 330 277 else => continue, 331 278 } 332 279 }
+24 -44
src/widgets/terminal/Terminal.zig
··· 3 3 4 4 const std = @import("std"); 5 5 const builtin = @import("builtin"); 6 + const ansi = @import("ansi.zig"); 6 7 pub const Command = @import("Command.zig"); 7 8 const Parser = @import("Parser.zig"); 8 9 const Pty = @import("Pty.zig"); ··· 185 186 }, 186 187 .c0 => |b| try self.handleC0(b), 187 188 .csi => |seq| { 188 - const final = seq[seq.len - 1]; 189 - switch (final) { 189 + switch (seq.final) { 190 190 'B' => { // CUD 191 - switch (seq.len) { 192 - 0 => unreachable, 193 - 1 => self.back_screen.cursor.row += 1, 194 - else => { 195 - const delta = parseParam(u16, seq[2 .. seq.len - 1], 1) orelse 1; 196 - self.back_screen.cursor.row = @min(self.back_screen.height - 1, self.back_screen.cursor.row + delta); 197 - }, 198 - } 191 + var iter = seq.iterator(u16); 192 + const delta = iter.next() orelse 1; 193 + self.back_screen.cursor.row = @min(self.back_screen.height - 1, self.back_screen.cursor.row + delta); 199 194 }, 200 195 'H' => { // CUP 201 - const delim = std.mem.indexOfScalar(u8, seq, ';') orelse { 202 - switch (seq.len) { 203 - 0 => unreachable, 204 - 1 => { 205 - self.back_screen.cursor.row = 0; 206 - self.back_screen.cursor.col = 0; 207 - }, 208 - else => { 209 - const row = parseParam(u16, seq[0 .. seq.len - 1], 1) orelse 1; 210 - self.back_screen.cursor.row = row - 1; 211 - }, 212 - } 213 - continue; 214 - }; 215 - const row = parseParam(u16, seq[0..delim], 1) orelse 1; 216 - const col = parseParam(u16, seq[delim + 1 .. seq.len - 1], 1) orelse 1; 196 + var iter = seq.iterator(u16); 197 + const row = iter.next() orelse 1; 198 + const col = iter.next() orelse 1; 217 199 self.back_screen.cursor.col = col - 1; 218 200 self.back_screen.cursor.row = row - 1; 219 201 }, 220 - 'm' => self.back_screen.sgr(seq[0 .. seq.len - 1]), 202 + 'm' => { 203 + if (seq.intermediate == null and seq.private_marker == null) { 204 + self.back_screen.sgr(seq); 205 + } 206 + }, 221 207 else => {}, 222 208 } 223 209 }, ··· 226 212 } 227 213 } 228 214 229 - inline fn handleC0(self: *Terminal, b: u8) !void { 215 + inline fn handleC0(self: *Terminal, b: ansi.C0) !void { 230 216 switch (b) { 231 - 0x00, 0x01, 0x02 => {}, // NUL, SOH, STX 232 - 0x05 => {}, // ENQ 233 - 0x07 => self.pending_events.bell.store(true, .unordered), // BEL 234 - 0x08 => self.back_screen.cursorLeft(1), // BS 235 - 0x09 => {}, // TODO: HT 236 - 0x0a, 0x0b, 0x0c => try self.back_screen.index(), // LF, VT, FF 237 - 0x0d => { // CR 217 + .NUL, .SOH, .STX => {}, 218 + .ENQ => {}, 219 + .BEL => self.pending_events.bell.store(true, .unordered), 220 + .BS => self.back_screen.cursorLeft(1), 221 + .HT => {}, // TODO: HT 222 + .LF, .VT, .FF => try self.back_screen.index(), 223 + .CR => { 238 224 self.back_screen.cursor.pending_wrap = false; 239 225 self.back_screen.cursor.col = if (self.mode.origin) 240 226 self.back_screen.scrolling_region.left ··· 243 229 else 244 230 0; 245 231 }, 246 - 0x0e => {}, // TODO: Charset shift out 247 - 0x0f => {}, // TODO: Charset shift in 248 - else => log.warn("unhandled C0: 0x{x}", .{b}), 232 + .SO => {}, // TODO: Charset shift out 233 + .SI => {}, // TODO: Charset shift in 234 + else => log.warn("unhandled C0: 0x{x}", .{@intFromEnum(b)}), 249 235 } 250 236 } 251 - 252 - /// Parse a param buffer, returning a default value if the param was empty 253 - inline fn parseParam(comptime T: type, buf: []const u8, default: ?T) ?T { 254 - if (buf.len == 0) return default; 255 - return std.fmt.parseUnsigned(T, buf, 10) catch return null; 256 - }
+107
src/widgets/terminal/ansi.zig
··· 1 + /// Control bytes. See man 7 ascii 2 + pub const C0 = enum(u8) { 3 + NUL = 0x00, 4 + SOH = 0x01, 5 + STX = 0x02, 6 + ETX = 0x03, 7 + EOT = 0x04, 8 + ENQ = 0x05, 9 + ACK = 0x06, 10 + BEL = 0x07, 11 + BS = 0x08, 12 + HT = 0x09, 13 + LF = 0x0a, 14 + VT = 0x0b, 15 + FF = 0x0c, 16 + CR = 0x0d, 17 + SO = 0x0e, 18 + SI = 0x0f, 19 + DLE = 0x10, 20 + DC1 = 0x11, 21 + DC2 = 0x12, 22 + DC3 = 0x13, 23 + DC4 = 0x14, 24 + NAK = 0x15, 25 + SYN = 0x16, 26 + ETB = 0x17, 27 + CAN = 0x18, 28 + EM = 0x19, 29 + SUB = 0x1a, 30 + ESC = 0x1b, 31 + FS = 0x1c, 32 + GS = 0x1d, 33 + RS = 0x1e, 34 + US = 0x1f, 35 + }; 36 + 37 + pub const CSI = struct { 38 + intermediate: ?u8 = null, 39 + private_marker: ?u8 = null, 40 + 41 + final: u8, 42 + params: []const u8, 43 + 44 + pub fn hasIntermediate(self: CSI, b: u8) bool { 45 + return b == self.intermediate orelse return false; 46 + } 47 + 48 + pub fn hasPrivateMarker(self: CSI, b: u8) bool { 49 + return b == self.private_marker orelse return false; 50 + } 51 + 52 + pub fn iterator(self: CSI, comptime T: type) ParamIterator(T) { 53 + return .{ .bytes = self.params }; 54 + } 55 + }; 56 + 57 + pub fn ParamIterator(T: type) type { 58 + return struct { 59 + const Self = @This(); 60 + 61 + bytes: []const u8, 62 + idx: usize = 0, 63 + /// indicates the next parameter will be a sub parameter of the current 64 + next_is_sub: bool = false, 65 + /// indicates the current parameter was an empty string 66 + is_empty: bool = false, 67 + 68 + pub fn next(self: *Self) ?T { 69 + // reset state 70 + self.next_is_sub = false; 71 + self.is_empty = false; 72 + 73 + const start = self.idx; 74 + var val: T = 0; 75 + while (self.idx < self.bytes.len) { 76 + defer self.idx += 1; // defer so we trigger on return as well 77 + const b = self.bytes[self.idx]; 78 + switch (b) { 79 + 0x30...0x39 => { 80 + val = (val * 10) + (b - 0x30); 81 + if (self.idx == self.bytes.len - 1) return val; 82 + }, 83 + ':', ';' => { 84 + self.next_is_sub = b == ':'; 85 + self.is_empty = self.idx == start; 86 + return val; 87 + }, 88 + else => return null, 89 + } 90 + } 91 + return null; 92 + } 93 + 94 + /// verifies there are at least n more parameters 95 + pub fn hasAtLeast(self: *Self, n: usize) bool { 96 + const start = self.idx; 97 + defer self.idx = start; 98 + 99 + var i: usize = 0; 100 + while (self.next()) |_| { 101 + i += 1; 102 + if (i >= n) return true; 103 + } 104 + return i >= n; 105 + } 106 + }; 107 + }