perf: add branch hints based on real world data
+ move stream ESC state entry outside of `nextNonUtf8`pull/9645/head
parent
5744fb042c
commit
212598ed66
|
|
@ -645,6 +645,11 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
try self.handleCodepoint(codepoint);
|
try self.handleCodepoint(codepoint);
|
||||||
}
|
}
|
||||||
if (!consumed) {
|
if (!consumed) {
|
||||||
|
// We optimize for the scenario where the text being
|
||||||
|
// printed in the terminal ISN'T full of ill-formed
|
||||||
|
// UTF-8 sequences.
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
|
||||||
const retry = self.utf8decoder.next(c);
|
const retry = self.utf8decoder.next(c);
|
||||||
// It should be impossible for the decoder
|
// It should be impossible for the decoder
|
||||||
// to not consume the byte twice in a row.
|
// to not consume the byte twice in a row.
|
||||||
|
|
@ -665,12 +670,16 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
// a chain of inline functions.
|
// a chain of inline functions.
|
||||||
@setEvalBranchQuota(100_000);
|
@setEvalBranchQuota(100_000);
|
||||||
|
|
||||||
|
// C0 control
|
||||||
if (c <= 0xF) {
|
if (c <= 0xF) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
try self.execute(@intCast(c));
|
try self.execute(@intCast(c));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// ESC
|
||||||
if (c == 0x1B) {
|
if (c == 0x1B) {
|
||||||
try self.nextNonUtf8(@intCast(c));
|
self.parser.state = .escape;
|
||||||
|
self.parser.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try self.print(@intCast(c));
|
try self.print(@intCast(c));
|
||||||
|
|
@ -681,14 +690,8 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
/// This assumes that we're not in the UTF-8 decoding state. If
|
/// This assumes that we're not in the UTF-8 decoding state. If
|
||||||
/// we may be in the UTF-8 decoding state call nextSlice or next.
|
/// we may be in the UTF-8 decoding state call nextSlice or next.
|
||||||
fn nextNonUtf8(self: *Self, c: u8) !void {
|
fn nextNonUtf8(self: *Self, c: u8) !void {
|
||||||
assert(self.parser.state != .ground or c == 0x1B);
|
assert(self.parser.state != .ground);
|
||||||
|
|
||||||
// Fast path for ESC
|
|
||||||
if (self.parser.state == .ground and c == 0x1B) {
|
|
||||||
self.parser.state = .escape;
|
|
||||||
self.parser.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Fast path for CSI entry.
|
// Fast path for CSI entry.
|
||||||
if (self.parser.state == .escape and c == '[') {
|
if (self.parser.state == .escape and c == '[') {
|
||||||
self.parser.state = .csi_entry;
|
self.parser.state = .csi_entry;
|
||||||
|
|
@ -696,6 +699,11 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
}
|
}
|
||||||
// Fast path for CSI params.
|
// Fast path for CSI params.
|
||||||
if (self.parser.state == .csi_param) csi_param: {
|
if (self.parser.state == .csi_param) csi_param: {
|
||||||
|
// csi_param is the most common parser state
|
||||||
|
// other than ground by a fairly wide margin.
|
||||||
|
//
|
||||||
|
// ref: https://github.com/qwerasd205/asciinema-stats
|
||||||
|
@branchHint(.likely);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// A C0 escape (yes, this is valid):
|
// A C0 escape (yes, this is valid):
|
||||||
0x00...0x0F => try self.execute(c),
|
0x00...0x0F => try self.execute(c),
|
||||||
|
|
@ -814,24 +822,52 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn csiDispatch(self: *Self, input: Parser.Action.CSI) !void {
|
inline fn csiDispatch(self: *Self, input: Parser.Action.CSI) !void {
|
||||||
|
// The branch hints here are based on real world data
|
||||||
|
// which indicates that the most common CSI finals are:
|
||||||
|
//
|
||||||
|
// 1. m
|
||||||
|
// 2. H
|
||||||
|
// 3. K
|
||||||
|
// 4. A
|
||||||
|
// 5. C
|
||||||
|
// 6. X
|
||||||
|
// 7. l
|
||||||
|
// 8. h
|
||||||
|
// 9. r
|
||||||
|
//
|
||||||
|
// Together, these 9 finals make up about 96% of all
|
||||||
|
// CSI sequences encountered in real world scenarios.
|
||||||
|
//
|
||||||
|
// Additionally, within the prongs, unlikely branch
|
||||||
|
// hints have been added to branches that deal with
|
||||||
|
// invalid sequences/commands, this is in order to
|
||||||
|
// optimize for the happy path where we're getting
|
||||||
|
// valid data from the program we're running.
|
||||||
|
//
|
||||||
|
// ref: https://github.com/qwerasd205/asciinema-stats
|
||||||
|
|
||||||
switch (input.final) {
|
switch (input.final) {
|
||||||
// CUU - Cursor Up
|
// CUU - Cursor Up
|
||||||
'A', 'k' => switch (input.intermediates.len) {
|
'A', 'k' => {
|
||||||
0 => try self.handler.vt(.cursor_up, .{
|
@branchHint(.likely);
|
||||||
.value = switch (input.params.len) {
|
switch (input.intermediates.len) {
|
||||||
0 => 1,
|
0 => try self.handler.vt(.cursor_up, .{
|
||||||
1 => input.params[0],
|
.value = switch (input.params.len) {
|
||||||
else => {
|
0 => 1,
|
||||||
log.warn("invalid cursor up command: {f}", .{input});
|
1 => input.params[0],
|
||||||
return;
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid cursor up command: {f}", .{input});
|
||||||
|
return;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
|
||||||
|
|
||||||
else => log.warn(
|
else => log.warn(
|
||||||
"ignoring unimplemented CSI A with intermediates: {s}",
|
"ignoring unimplemented CSI A with intermediates: {s}",
|
||||||
.{input.intermediates},
|
.{input.intermediates},
|
||||||
),
|
),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// CUD - Cursor Down
|
// CUD - Cursor Down
|
||||||
|
|
@ -841,6 +877,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid cursor down command: {f}", .{input});
|
log.warn("invalid cursor down command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -854,22 +891,26 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
// CUF - Cursor Right
|
// CUF - Cursor Right
|
||||||
'C' => switch (input.intermediates.len) {
|
'C' => {
|
||||||
0 => try self.handler.vt(.cursor_right, .{
|
@branchHint(.likely);
|
||||||
.value = switch (input.params.len) {
|
switch (input.intermediates.len) {
|
||||||
0 => 1,
|
0 => try self.handler.vt(.cursor_right, .{
|
||||||
1 => input.params[0],
|
.value = switch (input.params.len) {
|
||||||
else => {
|
0 => 1,
|
||||||
log.warn("invalid cursor right command: {f}", .{input});
|
1 => input.params[0],
|
||||||
return;
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid cursor right command: {f}", .{input});
|
||||||
|
return;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}),
|
|
||||||
|
|
||||||
else => log.warn(
|
else => log.warn(
|
||||||
"ignoring unimplemented CSI C with intermediates: {s}",
|
"ignoring unimplemented CSI C with intermediates: {s}",
|
||||||
.{input.intermediates},
|
.{input.intermediates},
|
||||||
),
|
),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// CUB - Cursor Left
|
// CUB - Cursor Left
|
||||||
|
|
@ -879,6 +920,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid cursor left command: {f}", .{input});
|
log.warn("invalid cursor left command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -899,6 +941,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid cursor up command: {f}", .{input});
|
log.warn("invalid cursor up command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -921,6 +964,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid cursor down command: {f}", .{input});
|
log.warn("invalid cursor down command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -943,6 +987,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid HPA command: {f}", .{input});
|
log.warn("invalid HPA command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -957,24 +1002,28 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
|
|
||||||
// CUP - Set Cursor Position.
|
// CUP - Set Cursor Position.
|
||||||
// TODO: test
|
// TODO: test
|
||||||
'H', 'f' => switch (input.intermediates.len) {
|
'H', 'f' => {
|
||||||
0 => {
|
@branchHint(.likely);
|
||||||
const pos: streampkg.Action.CursorPos = switch (input.params.len) {
|
switch (input.intermediates.len) {
|
||||||
0 => .{ .row = 1, .col = 1 },
|
0 => {
|
||||||
1 => .{ .row = input.params[0], .col = 1 },
|
const pos: streampkg.Action.CursorPos = switch (input.params.len) {
|
||||||
2 => .{ .row = input.params[0], .col = input.params[1] },
|
0 => .{ .row = 1, .col = 1 },
|
||||||
else => {
|
1 => .{ .row = input.params[0], .col = 1 },
|
||||||
log.warn("invalid CUP command: {f}", .{input});
|
2 => .{ .row = input.params[0], .col = input.params[1] },
|
||||||
return;
|
else => {
|
||||||
},
|
@branchHint(.unlikely);
|
||||||
};
|
log.warn("invalid CUP command: {f}", .{input});
|
||||||
try self.handler.vt(.cursor_pos, pos);
|
return;
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
try self.handler.vt(.cursor_pos, pos);
|
||||||
|
},
|
||||||
|
|
||||||
else => log.warn(
|
else => log.warn(
|
||||||
"ignoring unimplemented CSI H with intermediates: {s}",
|
"ignoring unimplemented CSI H with intermediates: {s}",
|
||||||
.{input.intermediates},
|
.{input.intermediates},
|
||||||
),
|
),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// CHT - Cursor Horizontal Tabulation
|
// CHT - Cursor Horizontal Tabulation
|
||||||
|
|
@ -1029,6 +1078,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
|
|
||||||
// Erase Line
|
// Erase Line
|
||||||
'K' => {
|
'K' => {
|
||||||
|
@branchHint(.likely);
|
||||||
const protected_: ?bool = switch (input.intermediates.len) {
|
const protected_: ?bool = switch (input.intermediates.len) {
|
||||||
0 => false,
|
0 => false,
|
||||||
1 => if (input.intermediates[0] == '?') true else null,
|
1 => if (input.intermediates[0] == '?') true else null,
|
||||||
|
|
@ -1036,6 +1086,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
};
|
};
|
||||||
|
|
||||||
const protected = protected_ orelse {
|
const protected = protected_ orelse {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid erase line command: {f}", .{input});
|
log.warn("invalid erase line command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1047,6 +1098,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mode = mode_ orelse {
|
const mode = mode_ orelse {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid erase line command: {f}", .{input});
|
log.warn("invalid erase line command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1056,7 +1108,10 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.left => try self.handler.vt(.erase_line_left, protected),
|
.left => try self.handler.vt(.erase_line_left, protected),
|
||||||
.complete => try self.handler.vt(.erase_line_complete, protected),
|
.complete => try self.handler.vt(.erase_line_complete, protected),
|
||||||
.right_unless_pending_wrap => try self.handler.vt(.erase_line_right_unless_pending_wrap, protected),
|
.right_unless_pending_wrap => try self.handler.vt(.erase_line_right_unless_pending_wrap, protected),
|
||||||
_ => log.warn("invalid erase line mode: {}", .{mode}),
|
_ => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid erase line mode: {}", .{mode});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1189,20 +1244,24 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Erase Characters (ECH)
|
// Erase Characters (ECH)
|
||||||
'X' => switch (input.intermediates.len) {
|
'X' => {
|
||||||
0 => try self.handler.vt(.erase_chars, switch (input.params.len) {
|
@branchHint(.likely);
|
||||||
0 => 1,
|
switch (input.intermediates.len) {
|
||||||
1 => input.params[0],
|
0 => try self.handler.vt(.erase_chars, switch (input.params.len) {
|
||||||
else => {
|
0 => 1,
|
||||||
log.warn("invalid erase characters command: {f}", .{input});
|
1 => input.params[0],
|
||||||
return;
|
else => {
|
||||||
},
|
@branchHint(.unlikely);
|
||||||
}),
|
log.warn("invalid erase characters command: {f}", .{input});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
else => log.warn(
|
else => log.warn(
|
||||||
"ignoring unimplemented CSI X with intermediates: {s}",
|
"ignoring unimplemented CSI X with intermediates: {s}",
|
||||||
.{input.intermediates},
|
.{input.intermediates},
|
||||||
),
|
),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// CHT - Cursor Horizontal Tabulation Back
|
// CHT - Cursor Horizontal Tabulation Back
|
||||||
|
|
@ -1342,6 +1401,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
|
|
||||||
// SM - Set Mode
|
// SM - Set Mode
|
||||||
'h' => mode: {
|
'h' => mode: {
|
||||||
|
@branchHint(.likely);
|
||||||
const ansi_mode = ansi: {
|
const ansi_mode = ansi: {
|
||||||
if (input.intermediates.len == 0) break :ansi true;
|
if (input.intermediates.len == 0) break :ansi true;
|
||||||
if (input.intermediates.len == 1 and
|
if (input.intermediates.len == 1 and
|
||||||
|
|
@ -1362,6 +1422,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
|
|
||||||
// RM - Reset Mode
|
// RM - Reset Mode
|
||||||
'l' => mode: {
|
'l' => mode: {
|
||||||
|
@branchHint(.likely);
|
||||||
const ansi_mode = ansi: {
|
const ansi_mode = ansi: {
|
||||||
if (input.intermediates.len == 0) break :ansi true;
|
if (input.intermediates.len == 0) break :ansi true;
|
||||||
if (input.intermediates.len == 1 and
|
if (input.intermediates.len == 1 and
|
||||||
|
|
@ -1381,81 +1442,86 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
// SGR - Select Graphic Rendition
|
// SGR - Select Graphic Rendition
|
||||||
'm' => switch (input.intermediates.len) {
|
'm' => {
|
||||||
0 => {
|
@branchHint(.likely);
|
||||||
// log.info("parse SGR params={any}", .{input.params});
|
switch (input.intermediates.len) {
|
||||||
var p: sgr.Parser = .{
|
0 => {
|
||||||
.params = input.params,
|
// This is the most common case.
|
||||||
.params_sep = input.params_sep,
|
@branchHint(.likely);
|
||||||
};
|
// log.info("parse SGR params={any}", .{input.params});
|
||||||
while (p.next()) |attr| {
|
var p: sgr.Parser = .{
|
||||||
// log.info("SGR attribute: {}", .{attr});
|
.params = input.params,
|
||||||
try self.handler.vt(.set_attribute, attr);
|
.params_sep = input.params_sep,
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
1 => switch (input.intermediates[0]) {
|
|
||||||
'>' => blk: {
|
|
||||||
if (input.params.len == 0) {
|
|
||||||
// Reset
|
|
||||||
try self.handler.vt(.modify_key_format, .legacy);
|
|
||||||
break :blk;
|
|
||||||
}
|
|
||||||
|
|
||||||
var format: ansi.ModifyKeyFormat = switch (input.params[0]) {
|
|
||||||
0 => .legacy,
|
|
||||||
1 => .cursor_keys,
|
|
||||||
2 => .function_keys,
|
|
||||||
4 => .other_keys_none,
|
|
||||||
else => {
|
|
||||||
log.warn("invalid setModifyKeyFormat: {f}", .{input});
|
|
||||||
break :blk;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
while (p.next()) |attr| {
|
||||||
if (input.params.len > 2) {
|
// log.info("SGR attribute: {}", .{attr});
|
||||||
log.warn("invalid setModifyKeyFormat: {f}", .{input});
|
try self.handler.vt(.set_attribute, attr);
|
||||||
break :blk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.params.len == 2) {
|
|
||||||
switch (format) {
|
|
||||||
// We don't support any of the subparams yet for these.
|
|
||||||
.legacy => {},
|
|
||||||
.cursor_keys => {},
|
|
||||||
.function_keys => {},
|
|
||||||
|
|
||||||
// We only support the numeric form.
|
|
||||||
.other_keys_none => switch (input.params[1]) {
|
|
||||||
2 => format = .other_keys_numeric,
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
.other_keys_numeric_except => {},
|
|
||||||
.other_keys_numeric => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.handler.vt(.modify_key_format, format);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
else => log.warn(
|
1 => switch (input.intermediates[0]) {
|
||||||
"unknown CSI m with intermediate: {}",
|
'>' => blk: {
|
||||||
.{input.intermediates[0]},
|
if (input.params.len == 0) {
|
||||||
),
|
// Reset
|
||||||
},
|
try self.handler.vt(.modify_key_format, .legacy);
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
|
||||||
else => {
|
var format: ansi.ModifyKeyFormat = switch (input.params[0]) {
|
||||||
// Nothing, but I wanted a place to put this comment:
|
0 => .legacy,
|
||||||
// there are others forms of CSI m that have intermediates.
|
1 => .cursor_keys,
|
||||||
// `vim --clean` uses `CSI ? 4 m` and I don't know what
|
2 => .function_keys,
|
||||||
// that means. And there is also `CSI > m` which is used
|
4 => .other_keys_none,
|
||||||
// to control modifier key reporting formats that we don't
|
else => {
|
||||||
// support yet.
|
@branchHint(.unlikely);
|
||||||
log.warn(
|
log.warn("invalid setModifyKeyFormat: {f}", .{input});
|
||||||
"ignoring unimplemented CSI m with intermediates: {s}",
|
break :blk;
|
||||||
.{input.intermediates},
|
},
|
||||||
);
|
};
|
||||||
},
|
|
||||||
|
if (input.params.len > 2) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid setModifyKeyFormat: {f}", .{input});
|
||||||
|
break :blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.params.len == 2) {
|
||||||
|
switch (format) {
|
||||||
|
// We don't support any of the subparams yet for these.
|
||||||
|
.legacy => {},
|
||||||
|
.cursor_keys => {},
|
||||||
|
.function_keys => {},
|
||||||
|
|
||||||
|
// We only support the numeric form.
|
||||||
|
.other_keys_none => switch (input.params[1]) {
|
||||||
|
2 => format = .other_keys_numeric,
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
.other_keys_numeric_except => {},
|
||||||
|
.other_keys_numeric => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.handler.vt(.modify_key_format, format);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => log.warn(
|
||||||
|
"unknown CSI m with intermediate: {}",
|
||||||
|
.{input.intermediates[0]},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
// Nothing, but I wanted a place to put this comment:
|
||||||
|
// there are others forms of CSI m that have intermediates.
|
||||||
|
// `vim --clean` uses `CSI ? 4 m` and I don't know what
|
||||||
|
// that means.
|
||||||
|
log.warn(
|
||||||
|
"ignoring unimplemented CSI m with intermediates: {s}",
|
||||||
|
.{input.intermediates},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
|
|
@ -1622,40 +1688,46 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
'r' => switch (input.intermediates.len) {
|
'r' => {
|
||||||
// DECSTBM - Set Top and Bottom Margins
|
@branchHint(.likely);
|
||||||
0 => switch (input.params.len) {
|
switch (input.intermediates.len) {
|
||||||
0 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = 0, .bottom_right = 0 }),
|
// DECSTBM - Set Top and Bottom Margins
|
||||||
1 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
|
0 => switch (input.params.len) {
|
||||||
2 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
|
0 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = 0, .bottom_right = 0 }),
|
||||||
else => log.warn("invalid DECSTBM command: {f}", .{input}),
|
1 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
|
||||||
},
|
2 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
|
||||||
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid DECSTBM command: {f}", .{input});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
1 => switch (input.intermediates[0]) {
|
1 => switch (input.intermediates[0]) {
|
||||||
// Restore Mode
|
// Restore Mode
|
||||||
'?' => {
|
'?' => {
|
||||||
for (input.params) |mode_int| {
|
for (input.params) |mode_int| {
|
||||||
if (modes.modeFromInt(mode_int, false)) |mode| {
|
if (modes.modeFromInt(mode_int, false)) |mode| {
|
||||||
try self.handler.vt(.restore_mode, .{ .mode = mode });
|
try self.handler.vt(.restore_mode, .{ .mode = mode });
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
log.warn(
|
||||||
"unimplemented restore mode: {}",
|
"unimplemented restore mode: {}",
|
||||||
.{mode_int},
|
.{mode_int},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
else => log.warn(
|
||||||
|
"unknown CSI s with intermediate: {f}",
|
||||||
|
.{input},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => log.warn(
|
else => log.warn(
|
||||||
"unknown CSI s with intermediate: {f}",
|
"ignoring unimplemented CSI s with intermediates: {f}",
|
||||||
.{input},
|
.{input},
|
||||||
),
|
),
|
||||||
},
|
}
|
||||||
|
|
||||||
else => log.warn(
|
|
||||||
"ignoring unimplemented CSI s with intermediates: {f}",
|
|
||||||
.{input},
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
's' => switch (input.intermediates.len) {
|
's' => switch (input.intermediates.len) {
|
||||||
|
|
@ -1866,6 +1938,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
1 => input.params[0],
|
1 => input.params[0],
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid ICH command: {f}", .{input});
|
log.warn("invalid ICH command: {f}", .{input});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -1906,9 +1979,34 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn oscDispatch(self: *Self, cmd: osc.Command) !void {
|
inline fn oscDispatch(self: *Self, cmd: osc.Command) !void {
|
||||||
|
// The branch hints here are based on real world data
|
||||||
|
// which indicates that the most common OSC commands are:
|
||||||
|
//
|
||||||
|
// 1. hyperlink_end
|
||||||
|
// 2. change_window_title
|
||||||
|
// 3. change_window_icon
|
||||||
|
// 4. hyperlink_start
|
||||||
|
// 5. report_pwd
|
||||||
|
// 6. color_operation
|
||||||
|
// 7. prompt_start
|
||||||
|
// 8. prompt_end
|
||||||
|
//
|
||||||
|
// Together, these 8 commands make up about 96% of all
|
||||||
|
// OSC commands encountered in real world scenarios.
|
||||||
|
//
|
||||||
|
// Additionally, within the prongs, unlikely branch
|
||||||
|
// hints have been added to branches that deal with
|
||||||
|
// invalid sequences/commands, this is in order to
|
||||||
|
// optimize for the happy path where we're getting
|
||||||
|
// valid data from the program we're running.
|
||||||
|
//
|
||||||
|
// ref: https://github.com/qwerasd205/asciinema-stats
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
.change_window_title => |title| {
|
.change_window_title => |title| {
|
||||||
|
@branchHint(.likely);
|
||||||
if (!std.unicode.utf8ValidateSlice(title)) {
|
if (!std.unicode.utf8ValidateSlice(title)) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("change title request: invalid utf-8, ignoring request", .{});
|
log.warn("change title request: invalid utf-8, ignoring request", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1917,6 +2015,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.change_window_icon => |icon| {
|
.change_window_icon => |icon| {
|
||||||
|
@branchHint(.likely);
|
||||||
log.info("OSC 1 (change icon) received and ignored icon={s}", .{icon});
|
log.info("OSC 1 (change icon) received and ignored icon={s}", .{icon});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1928,6 +2027,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.prompt_start => |v| {
|
.prompt_start => |v| {
|
||||||
|
@branchHint(.likely);
|
||||||
switch (v.kind) {
|
switch (v.kind) {
|
||||||
.primary, .right => try self.handler.vt(.prompt_start, .{
|
.primary, .right => try self.handler.vt(.prompt_start, .{
|
||||||
.aid = v.aid,
|
.aid = v.aid,
|
||||||
|
|
@ -1939,7 +2039,10 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.prompt_end => try self.handler.vt(.prompt_end, {}),
|
.prompt_end => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
try self.handler.vt(.prompt_end, {});
|
||||||
|
},
|
||||||
|
|
||||||
.end_of_input => try self.handler.vt(.end_of_input, {}),
|
.end_of_input => try self.handler.vt(.end_of_input, {}),
|
||||||
|
|
||||||
|
|
@ -1948,11 +2051,13 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.report_pwd => |v| {
|
.report_pwd => |v| {
|
||||||
|
@branchHint(.likely);
|
||||||
try self.handler.vt(.report_pwd, .{ .url = v.value });
|
try self.handler.vt(.report_pwd, .{ .url = v.value });
|
||||||
},
|
},
|
||||||
|
|
||||||
.mouse_shape => |v| {
|
.mouse_shape => |v| {
|
||||||
const shape = MouseShape.fromString(v.value) orelse {
|
const shape = MouseShape.fromString(v.value) orelse {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("unknown cursor shape: {s}", .{v.value});
|
log.warn("unknown cursor shape: {s}", .{v.value});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1961,6 +2066,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.color_operation => |v| {
|
.color_operation => |v| {
|
||||||
|
@branchHint(.likely);
|
||||||
try self.handler.vt(.color_operation, .{
|
try self.handler.vt(.color_operation, .{
|
||||||
.op = v.op,
|
.op = v.op,
|
||||||
.requests = v.requests,
|
.requests = v.requests,
|
||||||
|
|
@ -1980,6 +2086,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.hyperlink_start => |v| {
|
.hyperlink_start => |v| {
|
||||||
|
@branchHint(.likely);
|
||||||
try self.handler.vt(.start_hyperlink, .{
|
try self.handler.vt(.start_hyperlink, .{
|
||||||
.uri = v.uri,
|
.uri = v.uri,
|
||||||
.id = v.id,
|
.id = v.id,
|
||||||
|
|
@ -1987,6 +2094,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.hyperlink_end => {
|
.hyperlink_end => {
|
||||||
|
@branchHint(.likely);
|
||||||
try self.handler.vt(.end_hyperlink, {});
|
try self.handler.vt(.end_hyperlink, {});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -2004,6 +2112,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
},
|
},
|
||||||
|
|
||||||
.invalid => {
|
.invalid => {
|
||||||
|
@branchHint(.cold);
|
||||||
// This is an invalid internal state, not an invalid OSC
|
// This is an invalid internal state, not an invalid OSC
|
||||||
// string being parsed. We shouldn't see this.
|
// string being parsed. We shouldn't see this.
|
||||||
log.warn("invalid OSC, should never happen", .{});
|
log.warn("invalid OSC, should never happen", .{});
|
||||||
|
|
@ -2029,6 +2138,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
'*' => .G2,
|
'*' => .G2,
|
||||||
'+' => .G3,
|
'+' => .G3,
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid charset intermediate: {any}", .{intermediates});
|
log.warn("invalid charset intermediate: {any}", .{intermediates});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2044,22 +2154,56 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
self: *Self,
|
self: *Self,
|
||||||
action: Parser.Action.ESC,
|
action: Parser.Action.ESC,
|
||||||
) !void {
|
) !void {
|
||||||
|
// The branch hints here are based on real world data
|
||||||
|
// which indicates that the most common ESC finals are:
|
||||||
|
//
|
||||||
|
// 1. B
|
||||||
|
// 2. \
|
||||||
|
// 3. 0
|
||||||
|
// 4. M
|
||||||
|
// 5. 8
|
||||||
|
// 6. 7
|
||||||
|
// 7. >
|
||||||
|
// 8. =
|
||||||
|
//
|
||||||
|
// Together, these 8 finals make up nearly 99% of all
|
||||||
|
// ESC sequences encountered in real world scenarios.
|
||||||
|
//
|
||||||
|
// Additionally, within the prongs, unlikely branch
|
||||||
|
// hints have been added to branches that deal with
|
||||||
|
// invalid sequences/commands, this is in order to
|
||||||
|
// optimize for the happy path where we're getting
|
||||||
|
// valid data from the program we're running.
|
||||||
|
//
|
||||||
|
// ref: https://github.com/qwerasd205/asciinema-stats
|
||||||
|
|
||||||
switch (action.final) {
|
switch (action.final) {
|
||||||
// Charsets
|
// Charsets
|
||||||
'B' => try self.configureCharset(action.intermediates, .ascii),
|
'B' => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
try self.configureCharset(action.intermediates, .ascii);
|
||||||
|
},
|
||||||
'A' => try self.configureCharset(action.intermediates, .british),
|
'A' => try self.configureCharset(action.intermediates, .british),
|
||||||
'0' => try self.configureCharset(action.intermediates, .dec_special),
|
'0' => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
try self.configureCharset(action.intermediates, .dec_special);
|
||||||
|
},
|
||||||
|
|
||||||
// DECSC - Save Cursor
|
// DECSC - Save Cursor
|
||||||
'7' => switch (action.intermediates.len) {
|
'7' => {
|
||||||
0 => try self.handler.vt(.save_cursor, {}),
|
@branchHint(.likely);
|
||||||
else => {
|
switch (action.intermediates.len) {
|
||||||
log.warn("invalid command: {f}", .{action});
|
0 => try self.handler.vt(.save_cursor, {}),
|
||||||
return;
|
else => {
|
||||||
},
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid command: {f}", .{action});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'8' => blk: {
|
'8' => blk: {
|
||||||
|
@branchHint(.likely);
|
||||||
switch (action.intermediates.len) {
|
switch (action.intermediates.len) {
|
||||||
// DECRC - Restore Cursor
|
// DECRC - Restore Cursor
|
||||||
0 => {
|
0 => {
|
||||||
|
|
@ -2087,6 +2231,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
'D' => switch (action.intermediates.len) {
|
'D' => switch (action.intermediates.len) {
|
||||||
0 => try self.handler.vt(.index, {}),
|
0 => try self.handler.vt(.index, {}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid index command: {f}", .{action});
|
log.warn("invalid index command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2096,6 +2241,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
'E' => switch (action.intermediates.len) {
|
'E' => switch (action.intermediates.len) {
|
||||||
0 => try self.handler.vt(.next_line, {}),
|
0 => try self.handler.vt(.next_line, {}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid next line command: {f}", .{action});
|
log.warn("invalid next line command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2105,18 +2251,23 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
'H' => switch (action.intermediates.len) {
|
'H' => switch (action.intermediates.len) {
|
||||||
0 => try self.handler.vt(.tab_set, {}),
|
0 => try self.handler.vt(.tab_set, {}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid tab set command: {f}", .{action});
|
log.warn("invalid tab set command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// RI - Reverse Index
|
// RI - Reverse Index
|
||||||
'M' => switch (action.intermediates.len) {
|
'M' => {
|
||||||
0 => try self.handler.vt(.reverse_index, {}),
|
@branchHint(.likely);
|
||||||
else => {
|
switch (action.intermediates.len) {
|
||||||
log.warn("invalid reverse index command: {f}", .{action});
|
0 => try self.handler.vt(.reverse_index, {}),
|
||||||
return;
|
else => {
|
||||||
},
|
@branchHint(.unlikely);
|
||||||
|
log.warn("invalid reverse index command: {f}", .{action});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// SS2 - Single Shift 2
|
// SS2 - Single Shift 2
|
||||||
|
|
@ -2127,6 +2278,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = true,
|
.locking = true,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid single shift 2 command: {f}", .{action});
|
log.warn("invalid single shift 2 command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2140,6 +2292,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = true,
|
.locking = true,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid single shift 3 command: {f}", .{action});
|
log.warn("invalid single shift 3 command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2179,6 +2332,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = false,
|
.locking = false,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid single shift 2 command: {f}", .{action});
|
log.warn("invalid single shift 2 command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2192,6 +2346,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = false,
|
.locking = false,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid single shift 3 command: {f}", .{action});
|
log.warn("invalid single shift 3 command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2205,6 +2360,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = false,
|
.locking = false,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid locking shift 1 right command: {f}", .{action});
|
log.warn("invalid locking shift 1 right command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2218,6 +2374,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = false,
|
.locking = false,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid locking shift 2 right command: {f}", .{action});
|
log.warn("invalid locking shift 2 right command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
@ -2231,26 +2388,35 @@ pub fn Stream(comptime Handler: type) type {
|
||||||
.locking = false,
|
.locking = false,
|
||||||
}),
|
}),
|
||||||
else => {
|
else => {
|
||||||
|
@branchHint(.unlikely);
|
||||||
log.warn("invalid locking shift 3 right command: {f}", .{action});
|
log.warn("invalid locking shift 3 right command: {f}", .{action});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set application keypad mode
|
// Set application keypad mode
|
||||||
'=' => switch (action.intermediates.len) {
|
'=' => {
|
||||||
0 => try self.handler.vt(.set_mode, .{ .mode = .keypad_keys }),
|
@branchHint(.likely);
|
||||||
else => log.warn("unimplemented setMode: {f}", .{action}),
|
switch (action.intermediates.len) {
|
||||||
|
0 => try self.handler.vt(.set_mode, .{ .mode = .keypad_keys }),
|
||||||
|
else => log.warn("unimplemented setMode: {f}", .{action}),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Reset application keypad mode
|
// Reset application keypad mode
|
||||||
'>' => switch (action.intermediates.len) {
|
'>' => {
|
||||||
0 => try self.handler.vt(.reset_mode, .{ .mode = .keypad_keys }),
|
@branchHint(.likely);
|
||||||
else => log.warn("unimplemented setMode: {f}", .{action}),
|
switch (action.intermediates.len) {
|
||||||
|
0 => try self.handler.vt(.reset_mode, .{ .mode = .keypad_keys }),
|
||||||
|
else => log.warn("unimplemented setMode: {f}", .{action}),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sets ST (string terminator). We don't have to do anything
|
// Sets ST (string terminator). We don't have to do anything
|
||||||
// because our parser always accepts ST.
|
// because our parser always accepts ST.
|
||||||
'\\' => {},
|
'\\' => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
},
|
||||||
|
|
||||||
else => log.warn("unimplemented ESC action: {f}", .{action}),
|
else => log.warn("unimplemented ESC action: {f}", .{action}),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,22 +165,47 @@ pub const StreamHandler = struct {
|
||||||
comptime action: Stream.Action.Tag,
|
comptime action: Stream.Action.Tag,
|
||||||
value: Stream.Action.Value(action),
|
value: Stream.Action.Value(action),
|
||||||
) !void {
|
) !void {
|
||||||
|
// The branch hints here are based on real world data
|
||||||
|
// which indicates that the most common actions are:
|
||||||
|
//
|
||||||
|
// 1. print
|
||||||
|
// 2. set_attribute
|
||||||
|
// 3. carriage_return
|
||||||
|
// 4. line_feed
|
||||||
|
// 5. cursor_pos
|
||||||
|
//
|
||||||
|
// Together, these 5 actions make up nearly 98% of
|
||||||
|
// all actions encountered in real world scenarios.
|
||||||
|
//
|
||||||
|
// ref: https://github.com/qwerasd205/asciinema-stats
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.print => try self.terminal.print(value.cp),
|
.print => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
try self.terminal.print(value.cp);
|
||||||
|
},
|
||||||
.print_repeat => try self.terminal.printRepeat(value),
|
.print_repeat => try self.terminal.printRepeat(value),
|
||||||
.bell => self.bell(),
|
.bell => self.bell(),
|
||||||
.backspace => self.terminal.backspace(),
|
.backspace => self.terminal.backspace(),
|
||||||
.horizontal_tab => try self.horizontalTab(value),
|
.horizontal_tab => try self.horizontalTab(value),
|
||||||
.horizontal_tab_back => try self.horizontalTabBack(value),
|
.horizontal_tab_back => try self.horizontalTabBack(value),
|
||||||
.linefeed => try self.linefeed(),
|
.linefeed => {
|
||||||
.carriage_return => self.terminal.carriageReturn(),
|
@branchHint(.likely);
|
||||||
|
try self.linefeed();
|
||||||
|
},
|
||||||
|
.carriage_return => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
self.terminal.carriageReturn();
|
||||||
|
},
|
||||||
.enquiry => try self.enquiry(),
|
.enquiry => try self.enquiry(),
|
||||||
.invoke_charset => self.terminal.invokeCharset(value.bank, value.charset, value.locking),
|
.invoke_charset => self.terminal.invokeCharset(value.bank, value.charset, value.locking),
|
||||||
.cursor_up => self.terminal.cursorUp(value.value),
|
.cursor_up => self.terminal.cursorUp(value.value),
|
||||||
.cursor_down => self.terminal.cursorDown(value.value),
|
.cursor_down => self.terminal.cursorDown(value.value),
|
||||||
.cursor_left => self.terminal.cursorLeft(value.value),
|
.cursor_left => self.terminal.cursorLeft(value.value),
|
||||||
.cursor_right => self.terminal.cursorRight(value.value),
|
.cursor_right => self.terminal.cursorRight(value.value),
|
||||||
.cursor_pos => self.terminal.setCursorPos(value.row, value.col),
|
.cursor_pos => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
self.terminal.setCursorPos(value.row, value.col);
|
||||||
|
},
|
||||||
.cursor_col => self.terminal.setCursorPos(self.terminal.screens.active.cursor.y + 1, value.value),
|
.cursor_col => self.terminal.setCursorPos(self.terminal.screens.active.cursor.y + 1, value.value),
|
||||||
.cursor_row => self.terminal.setCursorPos(value.value, self.terminal.screens.active.cursor.x + 1),
|
.cursor_row => self.terminal.setCursorPos(value.value, self.terminal.screens.active.cursor.x + 1),
|
||||||
.cursor_col_relative => self.terminal.setCursorPos(
|
.cursor_col_relative => self.terminal.setCursorPos(
|
||||||
|
|
@ -290,10 +315,23 @@ pub const StreamHandler = struct {
|
||||||
.end_of_command => self.endOfCommand(value.exit_code),
|
.end_of_command => self.endOfCommand(value.exit_code),
|
||||||
.mouse_shape => try self.setMouseShape(value),
|
.mouse_shape => try self.setMouseShape(value),
|
||||||
.configure_charset => self.configureCharset(value.slot, value.charset),
|
.configure_charset => self.configureCharset(value.slot, value.charset),
|
||||||
.set_attribute => switch (value) {
|
.set_attribute => {
|
||||||
.unknown => |unk| log.warn("unimplemented or unknown SGR attribute: {any}", .{unk}),
|
@branchHint(.likely);
|
||||||
else => self.terminal.setAttribute(value) catch |err|
|
switch (value) {
|
||||||
log.warn("error setting attribute {}: {}", .{ value, err }),
|
.unknown => |unk| {
|
||||||
|
// We optimize for the happy path scenario here, since
|
||||||
|
// unknown/invalid SGRs aren't that common in the wild.
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
log.warn("unimplemented or unknown SGR attribute: {any}", .{unk});
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
@branchHint(.likely);
|
||||||
|
self.terminal.setAttribute(value) catch |err| {
|
||||||
|
@branchHint(.cold);
|
||||||
|
log.warn("error setting attribute {}: {}", .{ value, err });
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.dcs_hook => try self.dcsHook(value),
|
.dcs_hook => try self.dcsHook(value),
|
||||||
.dcs_put => try self.dcsPut(value),
|
.dcs_put => try self.dcsPut(value),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue