terminal: clear tabstop bit on unset instead of toggling (#13900)

Make `Tabstops.unset` (backing [Tab Clear
(TBC)](https://ghostty.org/docs/vt/csi/tbc) with `n=0`) not set a tab
stop when clearing a column that has no tab stop.
This commit is contained in:
Mitchell Hashimoto
2026-08-18 17:58:55 -07:00
committed by GitHub

View File

@@ -93,13 +93,13 @@ 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];
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];
self.dynamic_stops[dynamic_i] &= ~masks[idx];
}
/// Get the value of a tabstop at a specific column. The columns are 0-indexed.
@@ -198,6 +198,10 @@ test "Tabstops: basic" {
try testing.expect(t.get(4));
t.unset(4);
try testing.expect(!t.get(4));
// Unsetting a column with no tabstop should be a no-op, not a toggle.
t.unset(4);
try testing.expect(!t.get(4));
}
test "Tabstops: dynamic allocations" {
@@ -214,6 +218,10 @@ test "Tabstops: dynamic allocations" {
try testing.expect(t.get(cap + 4));
try testing.expect(!t.get(cap + 3));
// Unsetting a column with no tabstop should be a no-op, not a toggle.
t.unset(cap + 3);
try testing.expect(!t.get(cap + 3));
// Growing again preserves existing stops and clears the new unit.
try t.resize(testing.allocator, cap + unit_bits + 1);
try testing.expect(t.get(cap + 4));