mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
fix sgr parsing for underline styles
This commit is contained in:
@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -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});
|
||||
|
@ -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}),
|
||||
|
||||
|
Reference in New Issue
Block a user