mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
terminal: DECSED, DECSEL parsing, tests
This commit is contained in:
@ -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.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user