mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-19 18:26:13 +03:00
setting and clearing tab stops
This commit is contained in:
@ -773,3 +773,11 @@ pub fn setCursorStyle(
|
||||
pub fn decaln(self: *Window) !void {
|
||||
self.terminal.decaln();
|
||||
}
|
||||
|
||||
pub fn tabClear(self: *Window, cmd: terminal.TabClear) !void {
|
||||
self.terminal.tabClear(cmd);
|
||||
}
|
||||
|
||||
pub fn tabSet(self: *Window) !void {
|
||||
self.terminal.tabSet();
|
||||
}
|
||||
|
@ -83,6 +83,20 @@ pub fn set(self: *Tabstops, col: usize) void {
|
||||
self.dynamic_stops[dynamic_i] |= masks[idx];
|
||||
}
|
||||
|
||||
/// Unset the tabstop at a certain column. The columns are 0-indexed.
|
||||
pub fn unset(self: *Tabstops, col: usize) void {
|
||||
const i = entry(col);
|
||||
const idx = index(col);
|
||||
if (i < prealloc_count) {
|
||||
self.prealloc_stops[i] ^= masks[idx];
|
||||
return;
|
||||
}
|
||||
|
||||
const dynamic_i = i - prealloc_count;
|
||||
assert(dynamic_i < self.dynamic_stops.len);
|
||||
self.dynamic_stops[dynamic_i] ^= masks[idx];
|
||||
}
|
||||
|
||||
/// Get the value of a tabstop at a specific column. The columns are 0-indexed.
|
||||
pub fn get(self: Tabstops, col: usize) bool {
|
||||
const i = entry(col);
|
||||
@ -160,6 +174,11 @@ test "Tabstops: basic" {
|
||||
|
||||
t.reset(0);
|
||||
try testing.expect(!t.get(4));
|
||||
|
||||
t.set(4);
|
||||
try testing.expect(t.get(4));
|
||||
t.unset(4);
|
||||
try testing.expect(!t.get(4));
|
||||
}
|
||||
|
||||
test "Tabstops: dynamic allocations" {
|
||||
|
@ -19,6 +19,9 @@ const Screen = @import("Screen.zig");
|
||||
|
||||
const log = std.log.scoped(.terminal);
|
||||
|
||||
/// Default tabstop interval
|
||||
const TABSTOP_INTERVAL = 8;
|
||||
|
||||
/// Screen is the current screen state.
|
||||
screen: Screen,
|
||||
|
||||
@ -64,7 +67,7 @@ pub fn init(alloc: Allocator, cols: usize, rows: usize) !Terminal {
|
||||
.rows = rows,
|
||||
.screen = try Screen.init(alloc, rows, cols),
|
||||
.cursor = .{ .x = 0, .y = 0 },
|
||||
.tabstops = try Tabstops.init(alloc, cols, 8),
|
||||
.tabstops = try Tabstops.init(alloc, cols, TABSTOP_INTERVAL),
|
||||
.scrolling_region = .{
|
||||
.top = 0,
|
||||
.bottom = rows - 1,
|
||||
@ -487,6 +490,22 @@ pub fn horizontalTab(self: *Terminal) !void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear tab stops.
|
||||
/// TODO: test
|
||||
pub fn tabClear(self: *Terminal, cmd: csi.TabClear) void {
|
||||
switch (cmd) {
|
||||
.current => self.tabstops.unset(self.cursor.x - 1),
|
||||
.all => self.tabstops.reset(0),
|
||||
else => log.warn("invalid or unknown tab clear setting: {}", .{cmd}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a tab stop on the current cursor.
|
||||
/// TODO: test
|
||||
pub fn tabSet(self: *Terminal) void {
|
||||
self.tabstops.set(self.cursor.x - 1);
|
||||
}
|
||||
|
||||
/// Carriage return moves the cursor to the first column.
|
||||
pub fn carriageReturn(self: *Terminal) void {
|
||||
const tracy = trace(@src());
|
||||
|
@ -17,3 +17,13 @@ pub const EraseLine = enum(u8) {
|
||||
// user-generated.
|
||||
_,
|
||||
};
|
||||
|
||||
// Modes for the TBC (tab clear) command.
|
||||
pub const TabClear = enum(u8) {
|
||||
current = 0,
|
||||
all = 3,
|
||||
|
||||
// Non-exhaustive so that @intToEnum never fails since the inputs are
|
||||
// user-generated.
|
||||
_,
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ pub const DeviceStatusReq = ansi.DeviceStatusReq;
|
||||
pub const Mode = ansi.Mode;
|
||||
pub const EraseDisplay = csi.EraseDisplay;
|
||||
pub const EraseLine = csi.EraseLine;
|
||||
pub const TabClear = csi.TabClear;
|
||||
pub const Attribute = sgr.Attribute;
|
||||
|
||||
// Not exported because they're just used for tests.
|
||||
|
@ -299,6 +299,18 @@ pub fn Stream(comptime Handler: type) type {
|
||||
},
|
||||
) else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||
|
||||
// TBC - Tab Clear
|
||||
// TODO: test
|
||||
'g' => if (@hasDecl(T, "tabClear")) try self.handler.tabClear(
|
||||
switch (action.params.len) {
|
||||
1 => @intToEnum(csi.TabClear, action.params[0]),
|
||||
else => {
|
||||
log.warn("invalid tab clear command: {}", .{action});
|
||||
return;
|
||||
},
|
||||
},
|
||||
) else log.warn("unimplemented CSI callback: {}", .{action}),
|
||||
|
||||
// SM - Set Mode
|
||||
'h' => if (@hasDecl(T, "setMode")) {
|
||||
for (action.params) |mode|
|
||||
@ -399,6 +411,12 @@ pub fn Stream(comptime Handler: type) type {
|
||||
},
|
||||
} else log.warn("unimplemented ESC callback: {}", .{action}),
|
||||
|
||||
// HTS - Horizontal Tab Set
|
||||
'H' => if (@hasDecl(T, "tabSet"))
|
||||
try self.handler.tabSet()
|
||||
else
|
||||
log.warn("unimplemented tab set callback: {}", .{action}),
|
||||
|
||||
// RI - Reverse Index
|
||||
'M' => if (@hasDecl(T, "reverseIndex")) switch (action.intermediates.len) {
|
||||
0 => try self.handler.reverseIndex(),
|
||||
|
Reference in New Issue
Block a user