From d7fe6a1c47880efe989aa3c3f601e7447de469cf Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 27 Nov 2022 15:30:02 -0800 Subject: [PATCH] fix sgr parsing for underline styles --- src/terminal/Parser.zig | 9 +++++++ src/terminal/sgr.zig | 57 ++++++++++++++++++++++++++++++++++++++++- src/terminal/stream.zig | 2 +- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 9773da63f..69087dc58 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -79,6 +79,10 @@ pub const Action = union(enum) { intermediates: []u8, params: []u16, final: u8, + sep: Sep, + + /// The separator used for CSI params. + pub const Sep = enum { semicolon, colon }; // Implement formatter for logging pub fn format( @@ -392,6 +396,11 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { .intermediates = self.intermediates[0..self.intermediates_idx], .params = self.params[0..self.params_idx], .final = c, + .sep = switch (self.params_sep) { + .none, .semicolon => .semicolon, + .colon => .colon, + .mixed => unreachable, + }, }, }; }, diff --git a/src/terminal/sgr.zig b/src/terminal/sgr.zig index 8d031c4a1..16620e822 100644 --- a/src/terminal/sgr.zig +++ b/src/terminal/sgr.zig @@ -91,6 +91,9 @@ pub const Parser = struct { params: []const u16, idx: usize = 0, + /// True if the separator is a colon + colon: bool = false, + /// Next returns the next attribute or null if there are no more attributes. pub fn next(self: *Parser) ?Attribute { if (self.idx > self.params.len) return null; @@ -116,7 +119,35 @@ pub const Parser = struct { 3 => return Attribute{ .italic = {} }, - 4 => return Attribute{ .underline = .single }, + 4 => blk: { + if (self.colon) { + switch (slice.len) { + // 0 is unreachable because we're here and we read + // an element to get here. + 0 => unreachable, + + // 1 is unreachable because we can't have a colon + // separator if there are no separators. + 1 => unreachable, + + // 2 means we have a specific underline style. + 2 => { + self.idx += 1; + switch (slice[1]) { + 0 => return Attribute{ .reset_underline = {} }, + 1 => return Attribute{ .underline = .single }, + 2 => return Attribute{ .underline = .double }, + else => break :blk, + } + }, + + // Colon-separated must only be 2. + else => break :blk, + } + } + + return Attribute{ .underline = .single }; + }, 5 => return Attribute{ .blink = {} }, @@ -215,6 +246,11 @@ fn testParse(params: []const u16) Attribute { return p.next().?; } +fn testParseColon(params: []const u16) Attribute { + var p: Parser = .{ .params = params, .colon = true }; + return p.next().?; +} + test "sgr: Parser" { try testing.expect(testParse(&[_]u16{}) == .unset); try testing.expect(testParse(&[_]u16{0}) == .unset); @@ -284,6 +320,25 @@ test "sgr: underline" { } } +test "sgr: underline styles" { + { + const v = testParseColon(&[_]u16{ 4, 2 }); + try testing.expect(v == .underline); + try testing.expect(v.underline == .double); + } + + { + const v = testParseColon(&[_]u16{ 4, 0 }); + try testing.expect(v == .reset_underline); + } + + { + const v = testParseColon(&[_]u16{ 4, 1 }); + try testing.expect(v == .underline); + try testing.expect(v.underline == .single); + } +} + test "sgr: blink" { { const v = testParse(&[_]u16{5}); diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index cdde8addb..eb1a73998 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -383,7 +383,7 @@ pub fn Stream(comptime Handler: type) type { // SGR - Select Graphic Rendition 'm' => if (@hasDecl(T, "setAttribute")) { - var p: sgr.Parser = .{ .params = action.params }; + var p: sgr.Parser = .{ .params = action.params, .colon = action.sep == .colon }; while (p.next()) |attr| try self.handler.setAttribute(attr); } else log.warn("unimplemented CSI callback: {}", .{action}),