fix sgr parsing for underline styles

This commit is contained in:
Mitchell Hashimoto
2022-11-27 15:30:02 -08:00
parent 3a248f6051
commit d7fe6a1c47
3 changed files with 66 additions and 2 deletions

View File

@ -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,
},
},
};
},

View File

@ -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});

View File

@ -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}),