perf: small sgr parser improvements
parent
6d5b4a3426
commit
9ab9bc8e19
|
|
@ -1772,10 +1772,6 @@ pub fn setAttribute(self: *Screen, attr: sgr.Attribute) !void {
|
||||||
self.cursor.style.flags.underline = v;
|
self.cursor.style.flags.underline = v;
|
||||||
},
|
},
|
||||||
|
|
||||||
.reset_underline => {
|
|
||||||
self.cursor.style.flags.underline = .none;
|
|
||||||
},
|
|
||||||
|
|
||||||
.underline_color => |rgb| {
|
.underline_color => |rgb| {
|
||||||
self.cursor.style.underline_color = .{ .rgb = .{
|
self.cursor.style.underline_color = .{ .rgb = .{
|
||||||
.r = rgb.r,
|
.r = rgb.r,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ pub const Attribute = union(Tag) {
|
||||||
|
|
||||||
/// Underline the text
|
/// Underline the text
|
||||||
underline: Underline,
|
underline: Underline,
|
||||||
reset_underline,
|
|
||||||
underline_color: color.RGB,
|
underline_color: color.RGB,
|
||||||
@"256_underline_color": u8,
|
@"256_underline_color": u8,
|
||||||
reset_underline_color,
|
reset_underline_color,
|
||||||
|
|
@ -92,7 +91,6 @@ pub const Attribute = union(Tag) {
|
||||||
"reset_italic",
|
"reset_italic",
|
||||||
"faint",
|
"faint",
|
||||||
"underline",
|
"underline",
|
||||||
"reset_underline",
|
|
||||||
"underline_color",
|
"underline_color",
|
||||||
"256_underline_color",
|
"256_underline_color",
|
||||||
"reset_underline_color",
|
"reset_underline_color",
|
||||||
|
|
@ -186,15 +184,16 @@ pub const Parser = struct {
|
||||||
/// Next returns the next attribute or null if there are no more attributes.
|
/// Next returns the next attribute or null if there are no more attributes.
|
||||||
pub fn next(self: *Parser) ?Attribute {
|
pub fn next(self: *Parser) ?Attribute {
|
||||||
if (self.idx >= self.params.len) {
|
if (self.idx >= self.params.len) {
|
||||||
// If we're at index zero it means we must have an empty
|
// We're more likely to not be done than to be done.
|
||||||
// list and an empty list implicitly means unset.
|
@branchHint(.unlikely);
|
||||||
if (self.idx == 0) {
|
|
||||||
// Add one to ensure we don't loop on unset
|
|
||||||
self.idx += 1;
|
|
||||||
return .unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
// Add one to ensure we don't loop on unset
|
||||||
|
defer self.idx += 1;
|
||||||
|
|
||||||
|
// If we're at index zero it means we must have an empty list
|
||||||
|
// and an empty list implicitly means unset, otherwise we're
|
||||||
|
// done and return null.
|
||||||
|
return if (self.idx == 0) .unset else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const slice = self.params[self.idx..self.params.len];
|
const slice = self.params[self.idx..self.params.len];
|
||||||
|
|
@ -206,20 +205,30 @@ pub const Parser = struct {
|
||||||
|
|
||||||
// If we have a colon separator then we need to ensure we're
|
// If we have a colon separator then we need to ensure we're
|
||||||
// parsing a value that allows it.
|
// parsing a value that allows it.
|
||||||
if (colon) switch (slice[0]) {
|
if (colon) {
|
||||||
4, 38, 48, 58 => {},
|
// Colons are fairly rare in the wild.
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
|
||||||
else => {
|
switch (slice[0]) {
|
||||||
// Consume all the colon separated values.
|
4, 38, 48, 58 => {},
|
||||||
const start = self.idx;
|
|
||||||
while (self.params_sep.isSet(self.idx)) self.idx += 1;
|
else => {
|
||||||
self.idx += 1;
|
// In real world use it's very rare
|
||||||
return .{ .unknown = .{
|
// that we receive an invalid sequence.
|
||||||
.full = self.params,
|
@branchHint(.cold);
|
||||||
.partial = slice[0..@min(self.idx - start + 1, slice.len)],
|
|
||||||
} };
|
// Consume all the colon separated
|
||||||
},
|
// values and return them as unknown.
|
||||||
};
|
const start = self.idx;
|
||||||
|
while (self.params_sep.isSet(self.idx)) self.idx += 1;
|
||||||
|
self.idx += 1;
|
||||||
|
return .{ .unknown = .{
|
||||||
|
.full = self.params,
|
||||||
|
.partial = slice[0..@min(self.idx - start + 1, slice.len)],
|
||||||
|
} };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (slice[0]) {
|
switch (slice[0]) {
|
||||||
0 => return .unset,
|
0 => return .unset,
|
||||||
|
|
@ -232,25 +241,37 @@ pub const Parser = struct {
|
||||||
|
|
||||||
4 => underline: {
|
4 => underline: {
|
||||||
if (colon) {
|
if (colon) {
|
||||||
|
// Colons are fairly rare in the wild.
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
|
||||||
assert(slice.len >= 2);
|
assert(slice.len >= 2);
|
||||||
if (self.isColon()) {
|
if (self.isColon()) {
|
||||||
|
// Invalid/unknown SGRs are just not very likely.
|
||||||
|
@branchHint(.cold);
|
||||||
|
|
||||||
self.consumeUnknownColon();
|
self.consumeUnknownColon();
|
||||||
break :underline;
|
break :underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
switch (slice[1]) {
|
return .{
|
||||||
0 => return .reset_underline,
|
.underline = switch (slice[1]) {
|
||||||
1 => return .{ .underline = .single },
|
0 => .none,
|
||||||
2 => return .{ .underline = .double },
|
1 => .single,
|
||||||
3 => return .{ .underline = .curly },
|
2 => .double,
|
||||||
4 => return .{ .underline = .dotted },
|
3 => .curly,
|
||||||
5 => return .{ .underline = .dashed },
|
4 => .dotted,
|
||||||
|
5 => .dashed,
|
||||||
|
|
||||||
// For unknown underline styles, just render
|
// For unknown underline styles,
|
||||||
// a single underline.
|
// just render a single underline.
|
||||||
else => return .{ .underline = .single },
|
else => single: {
|
||||||
}
|
// This is quite a rare condition.
|
||||||
|
@branchHint(.cold);
|
||||||
|
break :single .single;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return .{ .underline = .single };
|
return .{ .underline = .single };
|
||||||
|
|
@ -272,7 +293,7 @@ pub const Parser = struct {
|
||||||
|
|
||||||
23 => return .reset_italic,
|
23 => return .reset_italic,
|
||||||
|
|
||||||
24 => return .reset_underline,
|
24 => return .{ .underline = .none },
|
||||||
|
|
||||||
25 => return .reset_blink,
|
25 => return .reset_blink,
|
||||||
|
|
||||||
|
|
@ -286,23 +307,32 @@ pub const Parser = struct {
|
||||||
.@"8_fg" = @enumFromInt(slice[0] - 30),
|
.@"8_fg" = @enumFromInt(slice[0] - 30),
|
||||||
},
|
},
|
||||||
|
|
||||||
38 => if (slice.len >= 2) switch (slice[1]) {
|
38 => if (slice.len >= 2) {
|
||||||
// `2` indicates direct-color (r, g, b).
|
// We are very likely to have enough parameters.
|
||||||
// We need at least 3 more params for this to make sense.
|
@branchHint(.likely);
|
||||||
2 => if (self.parseDirectColor(
|
|
||||||
.direct_color_fg,
|
|
||||||
slice,
|
|
||||||
colon,
|
|
||||||
)) |v| return v,
|
|
||||||
|
|
||||||
// `5` indicates indexed color.
|
switch (slice[1]) {
|
||||||
5 => if (slice.len >= 3) {
|
// `2` indicates direct-color (r, g, b).
|
||||||
self.idx += 2;
|
// We need at least 3 more params for this to make sense.
|
||||||
return .{
|
2 => if (self.parseDirectColor(
|
||||||
.@"256_fg" = @truncate(slice[2]),
|
.direct_color_fg,
|
||||||
};
|
slice,
|
||||||
},
|
colon,
|
||||||
else => {},
|
)) |v| return v,
|
||||||
|
|
||||||
|
// `5` indicates indexed color.
|
||||||
|
5 => if (slice.len >= 3) {
|
||||||
|
// We are very likely to have enough parameters.
|
||||||
|
@branchHint(.likely);
|
||||||
|
|
||||||
|
self.idx += 2;
|
||||||
|
return .{
|
||||||
|
.@"256_fg" = @truncate(slice[2]),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
39 => return .reset_fg,
|
39 => return .reset_fg,
|
||||||
|
|
@ -311,23 +341,32 @@ pub const Parser = struct {
|
||||||
.@"8_bg" = @enumFromInt(slice[0] - 40),
|
.@"8_bg" = @enumFromInt(slice[0] - 40),
|
||||||
},
|
},
|
||||||
|
|
||||||
48 => if (slice.len >= 2) switch (slice[1]) {
|
48 => if (slice.len >= 2) {
|
||||||
// `2` indicates direct-color (r, g, b).
|
// We are very likely to have enough parameters.
|
||||||
// We need at least 3 more params for this to make sense.
|
@branchHint(.likely);
|
||||||
2 => if (self.parseDirectColor(
|
|
||||||
.direct_color_bg,
|
|
||||||
slice,
|
|
||||||
colon,
|
|
||||||
)) |v| return v,
|
|
||||||
|
|
||||||
// `5` indicates indexed color.
|
switch (slice[1]) {
|
||||||
5 => if (slice.len >= 3) {
|
// `2` indicates direct-color (r, g, b).
|
||||||
self.idx += 2;
|
// We need at least 3 more params for this to make sense.
|
||||||
return .{
|
2 => if (self.parseDirectColor(
|
||||||
.@"256_bg" = @truncate(slice[2]),
|
.direct_color_bg,
|
||||||
};
|
slice,
|
||||||
},
|
colon,
|
||||||
else => {},
|
)) |v| return v,
|
||||||
|
|
||||||
|
// `5` indicates indexed color.
|
||||||
|
5 => if (slice.len >= 3) {
|
||||||
|
// We are very likely to have enough parameters.
|
||||||
|
@branchHint(.likely);
|
||||||
|
|
||||||
|
self.idx += 2;
|
||||||
|
return .{
|
||||||
|
.@"256_bg" = @truncate(slice[2]),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
49 => return .reset_bg,
|
49 => return .reset_bg,
|
||||||
|
|
@ -335,23 +374,31 @@ pub const Parser = struct {
|
||||||
53 => return .overline,
|
53 => return .overline,
|
||||||
55 => return .reset_overline,
|
55 => return .reset_overline,
|
||||||
|
|
||||||
58 => if (slice.len >= 2) switch (slice[1]) {
|
58 => if (slice.len >= 2) {
|
||||||
// `2` indicates direct-color (r, g, b).
|
// We are very likely to have enough parameters.
|
||||||
// We need at least 3 more params for this to make sense.
|
@branchHint(.likely);
|
||||||
2 => if (self.parseDirectColor(
|
|
||||||
.underline_color,
|
|
||||||
slice,
|
|
||||||
colon,
|
|
||||||
)) |v| return v,
|
|
||||||
|
|
||||||
// `5` indicates indexed color.
|
switch (slice[1]) {
|
||||||
5 => if (slice.len >= 3) {
|
// `2` indicates direct-color (r, g, b).
|
||||||
self.idx += 2;
|
// We need at least 3 more params for this to make sense.
|
||||||
return .{
|
2 => if (self.parseDirectColor(
|
||||||
.@"256_underline_color" = @truncate(slice[2]),
|
.underline_color,
|
||||||
};
|
slice,
|
||||||
},
|
colon,
|
||||||
else => {},
|
)) |v| return v,
|
||||||
|
|
||||||
|
// `5` indicates indexed color.
|
||||||
|
5 => if (slice.len >= 3) {
|
||||||
|
// We are very likely to have enough parameters.
|
||||||
|
@branchHint(.likely);
|
||||||
|
|
||||||
|
self.idx += 2;
|
||||||
|
return .{
|
||||||
|
.@"256_underline_color" = @truncate(slice[2]),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
59 => return .reset_underline_color,
|
59 => return .reset_underline_color,
|
||||||
|
|
@ -389,6 +436,9 @@ pub const Parser = struct {
|
||||||
// If we don't have a colon, then we expect exactly 3 semicolon
|
// If we don't have a colon, then we expect exactly 3 semicolon
|
||||||
// separated values.
|
// separated values.
|
||||||
if (!colon) {
|
if (!colon) {
|
||||||
|
// Semicolons are much more common than colons.
|
||||||
|
@branchHint(.likely);
|
||||||
|
|
||||||
self.idx += 4;
|
self.idx += 4;
|
||||||
return @unionInit(Attribute, @tagName(tag), .{
|
return @unionInit(Attribute, @tagName(tag), .{
|
||||||
.r = @truncate(slice[2]),
|
.r = @truncate(slice[2]),
|
||||||
|
|
@ -402,6 +452,9 @@ pub const Parser = struct {
|
||||||
const count = self.countColon();
|
const count = self.countColon();
|
||||||
switch (count) {
|
switch (count) {
|
||||||
3 => {
|
3 => {
|
||||||
|
// This is the much more common case in the wild.
|
||||||
|
@branchHint(.likely);
|
||||||
|
|
||||||
self.idx += 4;
|
self.idx += 4;
|
||||||
return @unionInit(Attribute, @tagName(tag), .{
|
return @unionInit(Attribute, @tagName(tag), .{
|
||||||
.r = @truncate(slice[2]),
|
.r = @truncate(slice[2]),
|
||||||
|
|
@ -420,6 +473,9 @@ pub const Parser = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {
|
else => {
|
||||||
|
// Invalid/unknown SGRs just don't happen very often at all.
|
||||||
|
@branchHint(.cold);
|
||||||
|
|
||||||
self.consumeUnknownColon();
|
self.consumeUnknownColon();
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
@ -560,7 +616,8 @@ test "sgr: underline" {
|
||||||
|
|
||||||
{
|
{
|
||||||
const v = testParse(&[_]u16{24});
|
const v = testParse(&[_]u16{24});
|
||||||
try testing.expect(v == .reset_underline);
|
try testing.expect(v == .underline);
|
||||||
|
try testing.expect(v.underline == .none);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -573,7 +630,8 @@ test "sgr: underline styles" {
|
||||||
|
|
||||||
{
|
{
|
||||||
const v = testParseColon(&[_]u16{ 4, 0 });
|
const v = testParseColon(&[_]u16{ 4, 0 });
|
||||||
try testing.expect(v == .reset_underline);
|
try testing.expect(v == .underline);
|
||||||
|
try testing.expect(v.underline == .none);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue