mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
fix sgr parsing for underline styles
This commit is contained in:
@ -79,6 +79,10 @@ pub const Action = union(enum) {
|
|||||||
intermediates: []u8,
|
intermediates: []u8,
|
||||||
params: []u16,
|
params: []u16,
|
||||||
final: u8,
|
final: u8,
|
||||||
|
sep: Sep,
|
||||||
|
|
||||||
|
/// The separator used for CSI params.
|
||||||
|
pub const Sep = enum { semicolon, colon };
|
||||||
|
|
||||||
// Implement formatter for logging
|
// Implement formatter for logging
|
||||||
pub fn format(
|
pub fn format(
|
||||||
@ -392,6 +396,11 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
.intermediates = self.intermediates[0..self.intermediates_idx],
|
.intermediates = self.intermediates[0..self.intermediates_idx],
|
||||||
.params = self.params[0..self.params_idx],
|
.params = self.params[0..self.params_idx],
|
||||||
.final = c,
|
.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,
|
params: []const u16,
|
||||||
idx: usize = 0,
|
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.
|
/// 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) return null;
|
if (self.idx > self.params.len) return null;
|
||||||
@ -116,7 +119,35 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
3 => return Attribute{ .italic = {} },
|
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 = {} },
|
5 => return Attribute{ .blink = {} },
|
||||||
|
|
||||||
@ -215,6 +246,11 @@ fn testParse(params: []const u16) Attribute {
|
|||||||
return p.next().?;
|
return p.next().?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn testParseColon(params: []const u16) Attribute {
|
||||||
|
var p: Parser = .{ .params = params, .colon = true };
|
||||||
|
return p.next().?;
|
||||||
|
}
|
||||||
|
|
||||||
test "sgr: Parser" {
|
test "sgr: Parser" {
|
||||||
try testing.expect(testParse(&[_]u16{}) == .unset);
|
try testing.expect(testParse(&[_]u16{}) == .unset);
|
||||||
try testing.expect(testParse(&[_]u16{0}) == .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" {
|
test "sgr: blink" {
|
||||||
{
|
{
|
||||||
const v = testParse(&[_]u16{5});
|
const v = testParse(&[_]u16{5});
|
||||||
|
@ -383,7 +383,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
|
|
||||||
// SGR - Select Graphic Rendition
|
// SGR - Select Graphic Rendition
|
||||||
'm' => if (@hasDecl(T, "setAttribute")) {
|
'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);
|
while (p.next()) |attr| try self.handler.setAttribute(attr);
|
||||||
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user