terminal: DECSED, DECSEL parsing, tests

This commit is contained in:
Mitchell Hashimoto
2023-09-25 09:58:30 -07:00
parent 8137a66ef6
commit 5528580a29
2 changed files with 183 additions and 37 deletions

View File

@ -259,45 +259,58 @@ pub fn Stream(comptime Handler: type) type {
) else log.warn("unimplemented CSI callback: {}", .{action}),
// Erase Display
// TODO: test
'J' => if (@hasDecl(T, "eraseDisplay")) try self.handler.eraseDisplay(
switch (action.params.len) {
0 => .below,
1 => mode: {
// TODO: use meta to get enum max
if (action.params[0] > 3) {
log.warn("invalid erase display command: {}", .{action});
return;
}
'J' => if (@hasDecl(T, "eraseDisplay")) {
const protected_: ?bool = switch (action.intermediates.len) {
0 => false,
1 => if (action.intermediates[0] == '?') true else null,
else => null,
};
break :mode @enumFromInt(action.params[0]);
},
else => {
log.warn("invalid erase display command: {}", .{action});
return;
},
},
) else log.warn("unimplemented CSI callback: {}", .{action}),
const protected = protected_ orelse {
log.warn("invalid erase display command: {}", .{action});
return;
};
const mode_: ?csi.EraseDisplay = switch (action.params.len) {
0 => .below,
1 => if (action.params[0] <= 3) @enumFromInt(action.params[0]) else null,
else => null,
};
const mode = mode_ orelse {
log.warn("invalid erase display command: {}", .{action});
return;
};
try self.handler.eraseDisplay(mode, protected);
} else log.warn("unimplemented CSI callback: {}", .{action}),
// Erase Line
'K' => if (@hasDecl(T, "eraseLine")) try self.handler.eraseLine(
switch (action.params.len) {
0 => .right,
1 => mode: {
// TODO: use meta to get enum max
if (action.params[0] > 3) {
log.warn("invalid erase line command: {}", .{action});
return;
}
'K' => if (@hasDecl(T, "eraseLine")) {
const protected_: ?bool = switch (action.intermediates.len) {
0 => false,
1 => if (action.intermediates[0] == '?') true else null,
else => null,
};
break :mode @enumFromInt(action.params[0]);
},
else => {
log.warn("invalid erase line command: {}", .{action});
return;
},
},
) else log.warn("unimplemented CSI callback: {}", .{action}),
const protected = protected_ orelse {
log.warn("invalid erase line command: {}", .{action});
return;
};
const mode_: ?csi.EraseLine = switch (action.params.len) {
0 => .right,
1 => if (action.params[0] < 3) @enumFromInt(action.params[0]) else null,
else => null,
};
const mode = mode_ orelse {
log.warn("invalid erase line command: {}", .{action});
return;
};
try self.handler.eraseLine(mode, protected);
} else log.warn("unimplemented CSI callback: {}", .{action}),
// IL - Insert Lines
// TODO: test
@ -1254,3 +1267,133 @@ test "stream: DECSCA" {
try testing.expectEqual(ansi.ProtectedMode.dec, s.handler.v.?);
}
}
test "stream: DECED, DECSED" {
const H = struct {
const Self = @This();
mode: ?csi.EraseDisplay = null,
protected: ?bool = null,
pub fn eraseDisplay(
self: *Self,
mode: csi.EraseDisplay,
protected: bool,
) !void {
self.mode = mode;
self.protected = protected;
}
};
var s: Stream(H) = .{ .handler = .{} };
{
for ("\x1B[?J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.below, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?0J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.below, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?1J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.above, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?2J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.complete, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?3J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.scrollback, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.below, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[0J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.below, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[1J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.above, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[2J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.complete, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[3J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.scrollback, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
}
test "stream: DECEL, DECSEL" {
const H = struct {
const Self = @This();
mode: ?csi.EraseLine = null,
protected: ?bool = null,
pub fn eraseLine(
self: *Self,
mode: csi.EraseLine,
protected: bool,
) !void {
self.mode = mode;
self.protected = protected;
}
};
var s: Stream(H) = .{ .handler = .{} };
{
for ("\x1B[?K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.right, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?0K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.right, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?1K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.left, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[?2K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.complete, s.handler.mode.?);
try testing.expect(s.handler.protected.?);
}
{
for ("\x1B[K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.right, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[0K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.right, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[1K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.left, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
{
for ("\x1B[2K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.complete, s.handler.mode.?);
try testing.expect(!s.handler.protected.?);
}
}

View File

@ -1304,7 +1304,9 @@ const StreamHandler = struct {
self.terminal.setCursorPos(row, col);
}
pub fn eraseDisplay(self: *StreamHandler, mode: terminal.EraseDisplay) !void {
pub fn eraseDisplay(self: *StreamHandler, mode: terminal.EraseDisplay, protected: bool) !void {
_ = protected;
if (mode == .complete) {
// Whenever we erase the full display, scroll to bottom.
try self.terminal.scrollViewport(.{ .bottom = {} });
@ -1314,7 +1316,8 @@ const StreamHandler = struct {
self.terminal.eraseDisplay(self.alloc, mode);
}
pub fn eraseLine(self: *StreamHandler, mode: terminal.EraseLine) !void {
pub fn eraseLine(self: *StreamHandler, mode: terminal.EraseLine, protected: bool) !void {
_ = protected;
self.terminal.eraseLine(mode);
}