mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal parser allows colons for SGR
This commit is contained in:
@ -88,6 +88,15 @@ pub const Action = union(enum) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Keeps track of the parameter sep used for CSI params. We allow colons
|
||||||
|
/// to be used ONLY by the 'm' CSI action.
|
||||||
|
const ParamSepState = enum(u8) {
|
||||||
|
none = 0,
|
||||||
|
semicolon = ';',
|
||||||
|
colon = ':',
|
||||||
|
mixed = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/// Maximum number of intermediate characters during parsing.
|
/// Maximum number of intermediate characters during parsing.
|
||||||
const MAX_INTERMEDIATE = 2;
|
const MAX_INTERMEDIATE = 2;
|
||||||
const MAX_PARAMS = 16;
|
const MAX_PARAMS = 16;
|
||||||
@ -102,6 +111,7 @@ intermediates_idx: u8 = 0,
|
|||||||
/// Param tracking, building
|
/// Param tracking, building
|
||||||
params: [MAX_PARAMS]u16 = undefined,
|
params: [MAX_PARAMS]u16 = undefined,
|
||||||
params_idx: u8 = 0,
|
params_idx: u8 = 0,
|
||||||
|
params_sep: ParamSepState = .none,
|
||||||
param_acc: u16 = 0,
|
param_acc: u16 = 0,
|
||||||
param_acc_idx: u8 = 0,
|
param_acc_idx: u8 = 0,
|
||||||
|
|
||||||
@ -193,10 +203,15 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
.param => param: {
|
.param => param: {
|
||||||
// Semicolon separates parameters. If we encounter a semicolon
|
// Semicolon separates parameters. If we encounter a semicolon
|
||||||
// we need to store and move on to the next parameter.
|
// we need to store and move on to the next parameter.
|
||||||
if (c == ';') {
|
if (c == ';' or c == ':') {
|
||||||
// Ignore too many parameters
|
// Ignore too many parameters
|
||||||
if (self.params_idx >= MAX_PARAMS) break :param null;
|
if (self.params_idx >= MAX_PARAMS) break :param null;
|
||||||
|
|
||||||
|
// If this is our first time seeing a parameter, we track
|
||||||
|
// the separator used so that we can't mix separators later.
|
||||||
|
if (self.params_idx == 0) self.params_sep = @intToEnum(ParamSepState, c);
|
||||||
|
if (@intToEnum(ParamSepState, c) != self.params_sep) self.params_sep = .mixed;
|
||||||
|
|
||||||
// Set param final value
|
// Set param final value
|
||||||
self.params[self.params_idx] = self.param_acc;
|
self.params[self.params_idx] = self.param_acc;
|
||||||
self.params_idx += 1;
|
self.params_idx += 1;
|
||||||
@ -228,6 +243,14 @@ fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
|
|||||||
self.params_idx += 1;
|
self.params_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We only allow the colon separator for the 'm' command.
|
||||||
|
switch (self.params_sep) {
|
||||||
|
.none => {},
|
||||||
|
.semicolon => {},
|
||||||
|
.colon => if (c != 'm') break :csi_dispatch null,
|
||||||
|
.mixed => break :csi_dispatch null,
|
||||||
|
}
|
||||||
|
|
||||||
break :csi_dispatch Action{
|
break :csi_dispatch Action{
|
||||||
.csi_dispatch = .{
|
.csi_dispatch = .{
|
||||||
.intermediates = self.intermediates[0..self.intermediates_idx],
|
.intermediates = self.intermediates[0..self.intermediates_idx],
|
||||||
@ -339,6 +362,56 @@ test "csi: ESC [ 1 ; 4 H" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "csi: SGR ESC [ 38 : 2 m" {
|
||||||
|
var p = init();
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
_ = p.next('[');
|
||||||
|
_ = p.next('3');
|
||||||
|
_ = p.next('8');
|
||||||
|
_ = p.next(':');
|
||||||
|
_ = p.next('2');
|
||||||
|
|
||||||
|
{
|
||||||
|
const a = p.next('m');
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1].? == .csi_dispatch);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
|
||||||
|
const d = a[1].?.csi_dispatch;
|
||||||
|
try testing.expect(d.final == 'm');
|
||||||
|
try testing.expect(d.params.len == 2);
|
||||||
|
try testing.expectEqual(@as(u16, 38), d.params[0]);
|
||||||
|
try testing.expectEqual(@as(u16, 2), d.params[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "csi: mixing semicolon/colon" {
|
||||||
|
var p = init();
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
for ("[38:2;4m") |c| {
|
||||||
|
const a = p.next(c);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1] == null);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "csi: colon for non-m final" {
|
||||||
|
var p = init();
|
||||||
|
_ = p.next(0x1B);
|
||||||
|
for ("[38:2h") |c| {
|
||||||
|
const a = p.next(c);
|
||||||
|
try testing.expect(a[0] == null);
|
||||||
|
try testing.expect(a[1] == null);
|
||||||
|
try testing.expect(a[2] == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try testing.expect(p.state == .ground);
|
||||||
|
}
|
||||||
|
|
||||||
test "osc: change window title" {
|
test "osc: change window title" {
|
||||||
var p = init();
|
var p = init();
|
||||||
_ = p.next(0x1B);
|
_ = p.next(0x1B);
|
||||||
|
@ -235,6 +235,7 @@ fn genTable() Table {
|
|||||||
range(&result, 0, 0x17, source, source, .execute);
|
range(&result, 0, 0x17, source, source, .execute);
|
||||||
range(&result, 0x1C, 0x1F, source, source, .execute);
|
range(&result, 0x1C, 0x1F, source, source, .execute);
|
||||||
range(&result, 0x30, 0x39, source, source, .param);
|
range(&result, 0x30, 0x39, source, source, .param);
|
||||||
|
single(&result, 0x3A, source, source, .param);
|
||||||
single(&result, 0x3B, source, source, .param);
|
single(&result, 0x3B, source, source, .param);
|
||||||
single(&result, 0x7F, source, source, .ignore);
|
single(&result, 0x7F, source, source, .ignore);
|
||||||
|
|
||||||
@ -242,7 +243,6 @@ fn genTable() Table {
|
|||||||
range(&result, 0x40, 0x7E, source, .ground, .csi_dispatch);
|
range(&result, 0x40, 0x7E, source, .ground, .csi_dispatch);
|
||||||
|
|
||||||
// => csi_ignore
|
// => csi_ignore
|
||||||
single(&result, 0x3A, source, .csi_ignore, .none);
|
|
||||||
range(&result, 0x3C, 0x3F, source, .csi_ignore, .none);
|
range(&result, 0x3C, 0x3F, source, .csi_ignore, .none);
|
||||||
|
|
||||||
// => csi_intermediate
|
// => csi_intermediate
|
||||||
|
Reference in New Issue
Block a user