mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 08:16:13 +03:00
implement CSI ESC [ <n> b for repeating previously printed char
This commit is contained in:
@ -60,6 +60,10 @@ scrolling_region: ScrollingRegion,
|
|||||||
/// The charset state
|
/// The charset state
|
||||||
charset: CharsetState = .{},
|
charset: CharsetState = .{},
|
||||||
|
|
||||||
|
/// The previous printed character. This is used for the repeat previous
|
||||||
|
/// char CSI (ESC [ <n> b).
|
||||||
|
previous_char: ?u21 = null,
|
||||||
|
|
||||||
/// Modes - This isn't exhaustive, since some modes (i.e. cursor origin)
|
/// Modes - This isn't exhaustive, since some modes (i.e. cursor origin)
|
||||||
/// are applied to the cursor and others aren't boolean yes/no.
|
/// are applied to the cursor and others aren't boolean yes/no.
|
||||||
modes: packed struct {
|
modes: packed struct {
|
||||||
@ -571,6 +575,9 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have a printable character, save it
|
||||||
|
self.previous_char = c;
|
||||||
|
|
||||||
// If we're soft-wrapping, then handle that first.
|
// If we're soft-wrapping, then handle that first.
|
||||||
if (self.screen.cursor.pending_wrap and self.modes.autowrap)
|
if (self.screen.cursor.pending_wrap and self.modes.autowrap)
|
||||||
try self.printWrap();
|
try self.printWrap();
|
||||||
@ -699,6 +706,15 @@ fn clearWideSpacerHead(self: *Terminal) void {
|
|||||||
cell.attrs.wide_spacer_head = false;
|
cell.attrs.wide_spacer_head = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print the previous printed character a repeated amount of times.
|
||||||
|
pub fn printRepeat(self: *Terminal, count: usize) !void {
|
||||||
|
// TODO: test
|
||||||
|
if (self.previous_char) |c| {
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < count) : (i += 1) try self.print(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resets all margins and fills the whole screen with the character 'E'
|
/// Resets all margins and fills the whole screen with the character 'E'
|
||||||
///
|
///
|
||||||
/// Sets the cursor to the top left corner.
|
/// Sets the cursor to the top left corner.
|
||||||
@ -1353,6 +1369,7 @@ pub fn fullReset(self: *Terminal) void {
|
|||||||
self.screen.cursor = .{};
|
self.screen.cursor = .{};
|
||||||
self.screen.saved_cursor = .{};
|
self.screen.saved_cursor = .{};
|
||||||
self.scrolling_region = .{ .top = 0, .bottom = self.rows - 1 };
|
self.scrolling_region = .{ .top = 0, .bottom = self.rows - 1 };
|
||||||
|
self.previous_char = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Terminal: input with no control characters" {
|
test "Terminal: input with no control characters" {
|
||||||
|
@ -314,6 +314,18 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
},
|
},
|
||||||
) else log.warn("unimplemented CSI callback: {}", .{action}),
|
) else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
|
// Repeat Previous Char (REP)
|
||||||
|
'b' => if (@hasDecl(T, "printRepeat")) try self.handler.printRepeat(
|
||||||
|
switch (action.params.len) {
|
||||||
|
0 => 1,
|
||||||
|
1 => action.params[0],
|
||||||
|
else => {
|
||||||
|
log.warn("invalid print repeat command: {}", .{action});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
) else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
// c - Device Attributes (DA1)
|
// c - Device Attributes (DA1)
|
||||||
'c' => if (@hasDecl(T, "deviceAttributes")) {
|
'c' => if (@hasDecl(T, "deviceAttributes")) {
|
||||||
const req: ansi.DeviceAttributeReq = switch (action.intermediates.len) {
|
const req: ansi.DeviceAttributeReq = switch (action.intermediates.len) {
|
||||||
@ -408,11 +420,6 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
else => log.warn("invalid DECSTBM command: {}", .{action}),
|
else => log.warn("invalid DECSTBM command: {}", .{action}),
|
||||||
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
} else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||||
|
|
||||||
else => if (@hasDecl(T, "csiUnimplemented"))
|
|
||||||
try self.handler.csiUnimplemented(action)
|
|
||||||
else
|
|
||||||
log.warn("unimplemented CSI action: {}", .{action}),
|
|
||||||
|
|
||||||
// ICH - Insert Blanks
|
// ICH - Insert Blanks
|
||||||
// TODO: test
|
// TODO: test
|
||||||
'@' => if (@hasDecl(T, "insertBlanks")) switch (action.params.len) {
|
'@' => if (@hasDecl(T, "insertBlanks")) switch (action.params.len) {
|
||||||
@ -440,6 +447,11 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
|
|
||||||
if (!success) log.warn("unimplemented CSI callback: {}", .{action});
|
if (!success) log.warn("unimplemented CSI callback: {}", .{action});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
else => if (@hasDecl(T, "csiUnimplemented"))
|
||||||
|
try self.handler.csiUnimplemented(action)
|
||||||
|
else
|
||||||
|
log.warn("unimplemented CSI action: {}", .{action}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,6 +478,10 @@ const StreamHandler = struct {
|
|||||||
try self.terminal.print(ch);
|
try self.terminal.print(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn printRepeat(self: *StreamHandler, count: usize) !void {
|
||||||
|
try self.terminal.printRepeat(count);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bell(self: StreamHandler) !void {
|
pub fn bell(self: StreamHandler) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
log.info("BELL", .{});
|
log.info("BELL", .{});
|
||||||
|
Reference in New Issue
Block a user