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}), ) else log.warn("unimplemented CSI callback: {}", .{action}),
// Erase Display // Erase Display
// TODO: test 'J' => if (@hasDecl(T, "eraseDisplay")) {
'J' => if (@hasDecl(T, "eraseDisplay")) try self.handler.eraseDisplay( const protected_: ?bool = switch (action.intermediates.len) {
switch (action.params.len) { 0 => false,
0 => .below, 1 => if (action.intermediates[0] == '?') true else null,
1 => mode: { else => null,
// TODO: use meta to get enum max };
if (action.params[0] > 3) {
log.warn("invalid erase display command: {}", .{action});
return;
}
break :mode @enumFromInt(action.params[0]); const protected = protected_ orelse {
},
else => {
log.warn("invalid erase display command: {}", .{action}); log.warn("invalid erase display command: {}", .{action});
return; return;
}, };
},
) else log.warn("unimplemented CSI callback: {}", .{action}), 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 // Erase Line
'K' => if (@hasDecl(T, "eraseLine")) try self.handler.eraseLine( 'K' => if (@hasDecl(T, "eraseLine")) {
switch (action.params.len) { const protected_: ?bool = switch (action.intermediates.len) {
0 => .right, 0 => false,
1 => mode: { 1 => if (action.intermediates[0] == '?') true else null,
// TODO: use meta to get enum max else => null,
if (action.params[0] > 3) { };
log.warn("invalid erase line command: {}", .{action});
return;
}
break :mode @enumFromInt(action.params[0]); const protected = protected_ orelse {
},
else => {
log.warn("invalid erase line command: {}", .{action}); log.warn("invalid erase line command: {}", .{action});
return; return;
}, };
},
) else log.warn("unimplemented CSI callback: {}", .{action}), 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 // IL - Insert Lines
// TODO: test // TODO: test
@ -1254,3 +1267,133 @@ test "stream: DECSCA" {
try testing.expectEqual(ansi.ProtectedMode.dec, s.handler.v.?); 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); 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) { if (mode == .complete) {
// Whenever we erase the full display, scroll to bottom. // Whenever we erase the full display, scroll to bottom.
try self.terminal.scrollViewport(.{ .bottom = {} }); try self.terminal.scrollViewport(.{ .bottom = {} });
@ -1314,7 +1316,8 @@ const StreamHandler = struct {
self.terminal.eraseDisplay(self.alloc, mode); 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); self.terminal.eraseLine(mode);
} }