terminal sgr attribute parsing

This commit is contained in:
Mitchell Hashimoto
2022-05-11 21:40:54 -07:00
parent 21be62f780
commit 5743d2a232
2 changed files with 116 additions and 0 deletions

View File

@ -1,6 +1,7 @@
const stream = @import("stream.zig");
const ansi = @import("ansi.zig");
const csi = @import("csi.zig");
const sgr = @import("sgr.zig");
pub const Terminal = @import("Terminal.zig");
pub const Parser = @import("Parser.zig");
@ -8,12 +9,14 @@ pub const Stream = stream.Stream;
pub const Mode = ansi.Mode;
pub const EraseDisplay = csi.EraseDisplay;
pub const EraseLine = csi.EraseLine;
pub const Attribute = sgr.Attribute;
// Not exported because they're just used for tests.
test {
_ = ansi;
_ = csi;
_ = sgr;
_ = stream;
_ = Parser;
_ = Terminal;

113
src/terminal/sgr.zig Normal file
View File

@ -0,0 +1,113 @@
//! SGR (Select Graphic Rendition) attribute parsing and types.
const std = @import("std");
const testing = std.testing;
/// Attribute type for SGR
pub const Attribute = union(enum) {
/// Unset all attributes
unset: void,
/// Unknown attribute, the raw CSI command parameters are here.
unknown: []const u16,
/// Set foreground color as RGB values.
direct_color_fg: RGB,
/// Set background color as RGB values.
direct_color_bg: RGB,
pub const RGB = struct {
r: u8,
g: u8,
b: u8,
};
};
/// Parse a set of parameters to a SGR command into an attribute.
pub fn parse(params: []const u16) Attribute {
// No parameters means unset
if (params.len == 0) return .{ .unset = {} };
switch (params[0]) {
0 => if (params.len == 1) return .{ .unset = {} },
38 => if ((params.len == 5 or params.len == 6) and params[1] == 2) {
// In the 6-len form, ignore the 3rd param.
const rgb = params[params.len - 3 .. params.len];
// We use @truncate because the value should be 0 to 255. If
// it isn't, the behavior is undefined so we just... truncate it.
return .{
.direct_color_fg = .{
.r = @truncate(u8, rgb[0]),
.g = @truncate(u8, rgb[1]),
.b = @truncate(u8, rgb[2]),
},
};
},
48 => if ((params.len == 5 or params.len == 6) and params[1] == 2) {
// In the 6-len form, ignore the 3rd param.
const rgb = params[params.len - 3 .. params.len];
// We use @truncate because the value should be 0 to 255. If
// it isn't, the behavior is undefined so we just... truncate it.
return .{
.direct_color_bg = .{
.r = @truncate(u8, rgb[0]),
.g = @truncate(u8, rgb[1]),
.b = @truncate(u8, rgb[2]),
},
};
},
else => {},
}
return .{ .unknown = params };
}
test "sgr: parse" {
try testing.expect(parse(&[_]u16{}) == .unset);
try testing.expect(parse(&[_]u16{0}) == .unset);
try testing.expect(parse(&[_]u16{ 0, 1 }) == .unknown);
{
const v = parse(&[_]u16{ 38, 2, 40, 44, 52 });
try testing.expect(v == .direct_color_fg);
try testing.expectEqual(@as(u8, 40), v.direct_color_fg.r);
try testing.expectEqual(@as(u8, 44), v.direct_color_fg.g);
try testing.expectEqual(@as(u8, 52), v.direct_color_fg.b);
}
{
const v = parse(&[_]u16{ 38, 2, 22, 40, 44, 52 });
try testing.expect(v == .direct_color_fg);
try testing.expectEqual(@as(u8, 40), v.direct_color_fg.r);
try testing.expectEqual(@as(u8, 44), v.direct_color_fg.g);
try testing.expectEqual(@as(u8, 52), v.direct_color_fg.b);
}
try testing.expect(parse(&[_]u16{ 38, 2, 44, 52 }) == .unknown);
try testing.expect(parse(&[_]u16{ 38, 2, 22, 22, 40, 44, 52 }) == .unknown);
{
const v = parse(&[_]u16{ 48, 2, 40, 44, 52 });
try testing.expect(v == .direct_color_bg);
try testing.expectEqual(@as(u8, 40), v.direct_color_bg.r);
try testing.expectEqual(@as(u8, 44), v.direct_color_bg.g);
try testing.expectEqual(@as(u8, 52), v.direct_color_bg.b);
}
{
const v = parse(&[_]u16{ 48, 2, 22, 40, 44, 52 });
try testing.expect(v == .direct_color_bg);
try testing.expectEqual(@as(u8, 40), v.direct_color_bg.r);
try testing.expectEqual(@as(u8, 44), v.direct_color_bg.g);
try testing.expectEqual(@as(u8, 52), v.direct_color_bg.b);
}
try testing.expect(parse(&[_]u16{ 48, 2, 44, 52 }) == .unknown);
try testing.expect(parse(&[_]u16{ 48, 2, 22, 22, 40, 44, 52 }) == .unknown);
}