terminal: Coordinate uses CellCountInt

This commit is contained in:
Mitchell Hashimoto
2024-04-26 20:52:08 -07:00
parent 2b67eaa18d
commit 3f16234f72
9 changed files with 111 additions and 53 deletions

View File

@ -1908,7 +1908,7 @@ fn rebuildCells2(
// font shaping by row. In the future, we will also do dirty tracking
// by row.
var row_it = screen.pages.rowIterator(.right_down, .{ .viewport = .{} }, null);
var y: usize = 0;
var y: terminal.size.CellCountInt = 0;
while (row_it.next()) |row| {
defer y += 1;

View File

@ -232,7 +232,10 @@ test Contents {
// Assert that get returns null for everything.
for (0..rows) |y| {
for (0..cols) |x| {
try testing.expect(c.get(.bg, .{ .x = x, .y = y }) == null);
try testing.expect(c.get(.bg, .{
.x = @intCast(x),
.y = @intCast(y),
}) == null);
}
}
@ -250,7 +253,10 @@ test Contents {
c.clear(1);
for (0..rows) |y| {
for (0..cols) |x| {
try testing.expect(c.get(.bg, .{ .x = x, .y = y }) == null);
try testing.expect(c.get(.bg, .{
.x = @intCast(x),
.y = @intCast(y),
}) == null);
}
}
}

View File

@ -287,12 +287,12 @@ fn initPostPipeline(
/// This is a single parameter for the terminal cell shader.
pub const CellText = extern struct {
mode: Mode,
grid_pos: [2]u16,
glyph_pos: [2]u32 = .{ 0, 0 },
glyph_size: [2]u32 = .{ 0, 0 },
glyph_offset: [2]i32 = .{ 0, 0 },
color: [4]u8,
bg_color: [4]u8,
grid_pos: [2]u16,
cell_width: u8,
pub const Mode = enum(u8) {

View File

@ -1421,7 +1421,7 @@ fn resizeWithoutReflowGrowCols(
// Keeps track of all our copied rows. Assertions at the end is that
// we copied exactly our page size.
var copied: usize = 0;
var copied: size.CellCountInt = 0;
// This function has an unfortunate side effect in that it causes memory
// fragmentation on rows if the columns are increasing in a way that
@ -2545,7 +2545,7 @@ pub fn cellIterator(
pub const RowIterator = struct {
page_it: PageIterator,
chunk: ?PageIterator.Chunk = null,
offset: usize = 0,
offset: size.CellCountInt = 0,
pub fn next(self: *RowIterator) ?Pin {
const chunk = self.chunk orelse return null;
@ -2767,8 +2767,8 @@ pub const PageIterator = struct {
pub const Chunk = struct {
page: *List.Node,
start: usize,
end: usize,
start: size.CellCountInt,
end: size.CellCountInt,
pub fn rows(self: Chunk) []Row {
const rows_ptr = self.page.data.rows.ptr(self.page.data.memory);
@ -2944,8 +2944,8 @@ fn growRows(self: *PageList, n: usize) !void {
/// should limit the number of active pins as much as possible.
pub const Pin = struct {
page: *List.Node,
y: usize = 0,
x: usize = 0,
y: size.CellCountInt = 0,
x: size.CellCountInt = 0,
pub fn rowAndCell(self: Pin) struct {
row: *pagepkg.Row,
@ -3104,7 +3104,7 @@ pub const Pin = struct {
pub fn left(self: Pin, n: usize) Pin {
assert(n <= self.x);
var result = self;
result.x -= n;
result.x -= std.math.cast(size.CellCountInt, n) orelse result.x;
return result;
}
@ -3112,7 +3112,8 @@ pub const Pin = struct {
pub fn right(self: Pin, n: usize) Pin {
assert(self.x + n < self.page.data.size.cols);
var result = self;
result.x += n;
result.x +|= std.math.cast(size.CellCountInt, n) orelse
std.math.maxInt(size.CellCountInt);
return result;
}
@ -3147,7 +3148,8 @@ pub const Pin = struct {
const rows = self.page.data.size.rows - (self.y + 1);
if (n <= rows) return .{ .offset = .{
.page = self.page,
.y = n + self.y,
.y = std.math.cast(size.CellCountInt, self.y + n) orelse
std.math.maxInt(size.CellCountInt),
.x = self.x,
} };
@ -3165,7 +3167,8 @@ pub const Pin = struct {
} };
if (n_left <= page.data.size.rows) return .{ .offset = .{
.page = page,
.y = n_left - 1,
.y = std.math.cast(size.CellCountInt, n_left - 1) orelse
std.math.maxInt(size.CellCountInt),
.x = self.x,
} };
n_left -= page.data.size.rows;
@ -3184,7 +3187,8 @@ pub const Pin = struct {
// Index fits within this page
if (n <= self.y) return .{ .offset = .{
.page = self.page,
.y = self.y - n,
.y = std.math.cast(size.CellCountInt, self.y - n) orelse
std.math.maxInt(size.CellCountInt),
.x = self.x,
} };
@ -3198,7 +3202,8 @@ pub const Pin = struct {
} };
if (n_left <= page.data.size.rows) return .{ .offset = .{
.page = page,
.y = page.data.size.rows - n_left,
.y = std.math.cast(size.CellCountInt, page.data.size.rows - n_left) orelse
std.math.maxInt(size.CellCountInt),
.x = self.x,
} };
n_left -= page.data.size.rows;
@ -3210,8 +3215,8 @@ const Cell = struct {
page: *List.Node,
row: *pagepkg.Row,
cell: *pagepkg.Cell,
row_idx: usize,
col_idx: usize,
row_idx: size.CellCountInt,
col_idx: size.CellCountInt,
/// Get the cell style.
///
@ -3231,7 +3236,7 @@ const Cell = struct {
/// this file then consider a different approach and ask yourself very
/// carefully if you really need this.
pub fn screenPoint(self: Cell) point.Point {
var y: usize = self.row_idx;
var y: size.CellCountInt = self.row_idx;
var page = self.page;
while (page.prev) |prev| {
y += prev.data.size.rows;
@ -3402,7 +3407,7 @@ test "PageList pointFromPin traverse pages" {
try testing.expectEqual(point.Point{
.screen = .{
.y = expected_y,
.y = @intCast(expected_y),
.x = 2,
},
}, s.pointFromPin(.screen, .{
@ -5629,7 +5634,7 @@ test "PageList resize (no reflow) more rows adds blank rows if cursor at bottom"
// Go through our active, we should get only 3,4,5
for (0..3) |y| {
const get = s.getCell(.{ .active = .{ .y = y } }).?;
const get = s.getCell(.{ .active = .{ .y = @intCast(y) } }).?;
const expected: u21 = @intCast(y + 2);
try testing.expectEqual(expected, get.cell.content.codepoint);
}
@ -6557,7 +6562,7 @@ test "PageList resize reflow less cols no wrapped rows" {
while (it.next()) |offset| {
for (0..4) |x| {
var offset_copy = offset;
offset_copy.x = x;
offset_copy.x = @intCast(x);
const rac = offset_copy.rowAndCell();
const cells = offset.page.data.getCells(rac.row);
try testing.expectEqual(@as(usize, 5), cells.len);
@ -7247,7 +7252,7 @@ test "PageList resize reflow less cols copy style" {
while (it.next()) |offset| {
for (0..s.cols - 1) |x| {
var offset_copy = offset;
offset_copy.x = x;
offset_copy.x = @intCast(x);
const rac = offset_copy.rowAndCell();
const style_id = rac.cell.style_id;
try testing.expect(style_id != 0);

View File

@ -1412,8 +1412,8 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
if (mapbuilder) |*b| {
for (0..encode_len) |_| try b.append(.{
.page = chunk.page,
.y = y,
.x = x,
.y = @intCast(y),
.x = @intCast(x),
});
}
}
@ -1425,8 +1425,8 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
if (mapbuilder) |*b| {
for (0..encode_len) |_| try b.append(.{
.page = chunk.page,
.y = y,
.x = x,
.y = @intCast(y),
.x = @intCast(x),
});
}
}
@ -1441,7 +1441,7 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
try strbuilder.append('\n');
if (mapbuilder) |*b| try b.append(.{
.page = chunk.page,
.y = y,
.y = @intCast(y),
.x = chunk.page.data.size.cols - 1,
});
}
@ -3959,7 +3959,10 @@ test "Screen: resize (no reflow) less rows trims blank lines" {
// Write only a background color into the remaining rows
for (1..s.pages.rows) |y| {
const list_cell = s.pages.getCell(.{ .active = .{ .x = 0, .y = y } }).?;
const list_cell = s.pages.getCell(.{ .active = .{
.x = 0,
.y = @intCast(y),
} }).?;
list_cell.cell.* = .{
.content_tag = .bg_color_rgb,
.content = .{ .color_rgb = .{ .r = 0xFF, .g = 0, .b = 0 } },
@ -3991,7 +3994,10 @@ test "Screen: resize (no reflow) more rows trims blank lines" {
// Write only a background color into the remaining rows
for (1..s.pages.rows) |y| {
const list_cell = s.pages.getCell(.{ .active = .{ .x = 0, .y = y } }).?;
const list_cell = s.pages.getCell(.{ .active = .{
.x = 0,
.y = @intCast(y),
} }).?;
list_cell.cell.* = .{
.content_tag = .bg_color_rgb,
.content = .{ .color_rgb = .{ .r = 0xFF, .g = 0, .b = 0 } },
@ -4118,7 +4124,10 @@ test "Screen: resize (no reflow) more rows with soft wrapping" {
// Every second row should be wrapped
for (0..6) |y| {
const list_cell = s.pages.getCell(.{ .screen = .{ .x = 0, .y = y } }).?;
const list_cell = s.pages.getCell(.{ .screen = .{
.x = 0,
.y = @intCast(y),
} }).?;
const row = list_cell.row;
const wrapped = (y % 2 == 0);
try testing.expectEqual(wrapped, row.wrap);
@ -4135,7 +4144,10 @@ test "Screen: resize (no reflow) more rows with soft wrapping" {
// Every second row should be wrapped
for (0..6) |y| {
const list_cell = s.pages.getCell(.{ .screen = .{ .x = 0, .y = y } }).?;
const list_cell = s.pages.getCell(.{ .screen = .{
.x = 0,
.y = @intCast(y),
} }).?;
const row = list_cell.row;
const wrapped = (y % 2 == 0);
try testing.expectEqual(wrapped, row.wrap);

View File

@ -435,7 +435,7 @@ pub fn adjust(
const cells = next.page.data.getCells(rac.row);
if (page.Cell.hasTextAny(cells)) {
end_pin.* = next;
end_pin.x = cells.len - 1;
end_pin.x = @intCast(cells.len - 1);
break;
}
}

View File

@ -4169,7 +4169,10 @@ test "Terminal: insertLines colors with bg color" {
}
for (0..t.cols) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 1 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 1,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -5297,7 +5300,10 @@ test "Terminal: index bottom of primary screen background sgr" {
defer testing.allocator.free(str);
try testing.expectEqualStrings("\n\n\nA", str);
for (0..5) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 4 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 4,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -5349,7 +5355,10 @@ test "Terminal: index bottom of scroll region with background SGR" {
}
for (0..t.cols) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 2 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 2,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -5961,7 +5970,10 @@ test "Terminal: deleteLines colors with bg color" {
}
for (0..t.cols) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 4 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 4,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -6148,7 +6160,10 @@ test "Terminal: deleteLines resets wrap" {
}
for (0..t.rows) |y| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = 0, .y = y } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = 0,
.y = @intCast(y),
} }).?;
const row = list_cell.row;
try testing.expect(!row.wrap);
}
@ -7183,7 +7198,10 @@ test "Terminal: deleteChars preserves background sgr" {
try testing.expectEqualStrings("AB23", str);
}
for (t.cols - 2..t.cols) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 0 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 0,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -7573,7 +7591,10 @@ test "Terminal: eraseLine right preserves background sgr" {
defer testing.allocator.free(str);
try testing.expectEqualStrings("A", str);
for (1..5) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 0 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 0,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -7727,7 +7748,10 @@ test "Terminal: eraseLine left preserves background sgr" {
defer testing.allocator.free(str);
try testing.expectEqualStrings(" CDE", str);
for (0..2) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 0 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 0,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -7847,7 +7871,10 @@ test "Terminal: eraseLine complete preserves background sgr" {
defer testing.allocator.free(str);
try testing.expectEqualStrings("", str);
for (0..5) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 0 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 0,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -8096,7 +8123,10 @@ test "Terminal: eraseDisplay erase below preserves SGR bg" {
defer testing.allocator.free(str);
try testing.expectEqualStrings("ABC\nD", str);
for (1..5) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 1 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 1,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,
@ -8271,7 +8301,10 @@ test "Terminal: eraseDisplay erase above preserves SGR bg" {
defer testing.allocator.free(str);
try testing.expectEqualStrings("\n F\nGHI", str);
for (0..2) |x| {
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 1 } }).?;
const list_cell = t.screen.pages.getCell(.{ .active = .{
.x = @intCast(x),
.y = 1,
} }).?;
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
try testing.expectEqual(Cell.RGB{
.r = 0xFF,

View File

@ -5,6 +5,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
const terminal = @import("../main.zig");
const point = @import("../point.zig");
const size = @import("../size.zig");
const command = @import("graphics_command.zig");
const PageList = @import("../PageList.zig");
const Screen = @import("../Screen.zig");
@ -265,13 +266,13 @@ pub const ImageStorage = struct {
);
},
.intersect_cell => |v| {
.intersect_cell => |v| intersect_cell: {
self.deleteIntersecting(
alloc,
t,
.{ .active = .{
.x = v.x,
.y = v.y,
.x = std.math.cast(size.CellCountInt, v.x) orelse break :intersect_cell,
.y = std.math.cast(size.CellCountInt, v.y) orelse break :intersect_cell,
} },
v.delete,
{},
@ -279,13 +280,13 @@ pub const ImageStorage = struct {
);
},
.intersect_cell_z => |v| {
.intersect_cell_z => |v| intersect_cell_z: {
self.deleteIntersecting(
alloc,
t,
.{ .active = .{
.x = v.x,
.y = v.y,
.x = std.math.cast(size.CellCountInt, v.x) orelse break :intersect_cell_z,
.y = std.math.cast(size.CellCountInt, v.y) orelse break :intersect_cell_z,
} },
v.delete,
v.z,
@ -317,7 +318,7 @@ pub const ImageStorage = struct {
// v.y is in active coords so we want to convert it to a pin
// so we can compare by page offsets.
const target_pin = t.screen.pages.pin(.{ .active = .{
.y = v.y,
.y = std.math.cast(size.CellCountInt, v.y) orelse break :row,
} }) orelse break :row;
var it = self.placements.iterator();

View File

@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const size = @import("size.zig");
/// The possible reference locations for a point. When someone says "(42, 80)" in the context of a terminal, that could mean multiple
/// things: it is in the current visible viewport? the current active
@ -65,8 +66,8 @@ pub const Point = union(Tag) {
};
pub const Coordinate = struct {
x: usize = 0,
y: usize = 0,
x: size.CellCountInt = 0,
y: size.CellCountInt = 0,
pub fn eql(self: Coordinate, other: Coordinate) bool {
return self.x == other.x and self.y == other.y;