mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
start changing getRow and rowIndex to use tagged indexes
Lots of bugs here, but it MATCHES the bugs!
This commit is contained in:
@ -86,12 +86,33 @@ pub const RowIterator = struct {
|
|||||||
|
|
||||||
pub fn next(self: *RowIterator) ?Row {
|
pub fn next(self: *RowIterator) ?Row {
|
||||||
if (self.index >= self.screen.rows) return null;
|
if (self.index >= self.screen.rows) return null;
|
||||||
const res = self.screen.getRow(self.index);
|
const res = self.screen.getRow(.{ .viewport = self.index });
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// RowIndex represents a row within the screen. There are various meanings
|
||||||
|
/// of a row index and this union represents the available types. For example,
|
||||||
|
/// when talking about row "0" you may want the first row in the viewport,
|
||||||
|
/// the first row in the scrollback, or the first row in the active area.
|
||||||
|
///
|
||||||
|
/// All row indexes are 0-indexed.
|
||||||
|
pub const RowIndex = union(enum) {
|
||||||
|
/// The index is from the top of the screen. The screen includes all
|
||||||
|
/// the history.
|
||||||
|
screen: usize,
|
||||||
|
|
||||||
|
/// The index is from the top of the viewport. Therefore, depending
|
||||||
|
/// on where the user has scrolled the viewport, "0" is different.
|
||||||
|
viewport: usize,
|
||||||
|
|
||||||
|
// TODO: others
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The tags of RowIndex
|
||||||
|
pub const RowIndexType = std.meta.FieldEnum(RowIndex);
|
||||||
|
|
||||||
/// Each screen maintains its own cursor state.
|
/// Each screen maintains its own cursor state.
|
||||||
cursor: Cursor = .{},
|
cursor: Cursor = .{},
|
||||||
|
|
||||||
@ -171,9 +192,9 @@ pub fn getVisible(self: Screen) []Cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a single row in the active area by index (0-indexed).
|
/// Get a single row in the active area by index (0-indexed).
|
||||||
pub fn getRow(self: Screen, idx: usize) Row {
|
pub fn getRow(self: Screen, idx: RowIndex) Row {
|
||||||
// Get the index of the first byte of the the row at index.
|
// Get the index of the first byte of the the row at index.
|
||||||
const real_idx = self.viewportRowIndex(idx);
|
const real_idx = self.rowIndex(idx);
|
||||||
|
|
||||||
// The storage is sliced to return exactly the number of columns.
|
// The storage is sliced to return exactly the number of columns.
|
||||||
return self.storage[real_idx .. real_idx + self.cols];
|
return self.storage[real_idx .. real_idx + self.cols];
|
||||||
@ -183,21 +204,25 @@ pub fn getRow(self: Screen, idx: usize) Row {
|
|||||||
pub fn getCell(self: Screen, row: usize, col: usize) *Cell {
|
pub fn getCell(self: Screen, row: usize, col: usize) *Cell {
|
||||||
assert(row < self.rows);
|
assert(row < self.rows);
|
||||||
assert(col < self.cols);
|
assert(col < self.cols);
|
||||||
const row_idx = self.viewportRowIndex(row);
|
const row_idx = self.rowIndex(.{ .viewport = row });
|
||||||
return &self.storage[row_idx + col];
|
return &self.storage[row_idx + col];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the index for the given row (0-indexed) into the underlying
|
|
||||||
/// storage array. The row is 0-indexed from the top of the viewport.
|
|
||||||
fn viewportRowIndex(self: Screen, idx: usize) usize {
|
|
||||||
assert(idx < self.rows);
|
|
||||||
return self.rowIndex(self.visible_offset + idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the index for the given row (0-indexed) into the underlying
|
/// Returns the index for the given row (0-indexed) into the underlying
|
||||||
/// storage array. The row is 0-indexed from the top of the screen.
|
/// storage array. The row is 0-indexed from the top of the screen.
|
||||||
fn rowIndex(self: Screen, y: usize) usize {
|
fn rowIndex(self: Screen, idx: RowIndex) usize {
|
||||||
assert(y < self.totalRows());
|
const y = switch (idx) {
|
||||||
|
.screen => |y| y: {
|
||||||
|
assert(y < self.totalRows());
|
||||||
|
break :y y;
|
||||||
|
},
|
||||||
|
|
||||||
|
.viewport => |y| y: {
|
||||||
|
assert(y < self.rows);
|
||||||
|
break :y y + self.visible_offset;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const val = (self.top + y) * self.cols;
|
const val = (self.top + y) * self.cols;
|
||||||
if (val < self.storage.len) return val;
|
if (val < self.storage.len) return val;
|
||||||
return val - self.storage.len;
|
return val - self.storage.len;
|
||||||
@ -344,18 +369,28 @@ fn scrollDelta(self: *Screen, delta: isize, grow: bool) void {
|
|||||||
|
|
||||||
/// Copy row at src to dst.
|
/// Copy row at src to dst.
|
||||||
pub fn copyRow(self: *Screen, dst: usize, src: usize) void {
|
pub fn copyRow(self: *Screen, dst: usize, src: usize) void {
|
||||||
const src_row = self.getRow(src);
|
const src_row = self.getRow(.{ .viewport = src });
|
||||||
const dst_row = self.getRow(dst);
|
const dst_row = self.getRow(.{ .viewport = dst });
|
||||||
std.mem.copy(Cell, dst_row, src_row);
|
std.mem.copy(Cell, dst_row, src_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the screen. The rows or cols can be bigger or smaller. Due to
|
/// Resize the screen. The rows or cols can be bigger or smaller. This
|
||||||
/// the internal representation of a screen, this usually involves a significant
|
/// function can only be used to resize the viewport. The scrollback size
|
||||||
/// amount of copying compared to any other operations.
|
/// (in lines) can't be changed. But due to the resize, more or less scrollback
|
||||||
|
/// "space" becomes available due to the width of lines.
|
||||||
///
|
///
|
||||||
/// This will trim data if the size is getting smaller. It is expected that
|
/// Due to the internal representation of a screen, this usually involves a
|
||||||
/// callers will reflow the text prior to calling this.
|
/// significant amount of copying compared to any other operations.
|
||||||
|
///
|
||||||
|
/// This will trim data if the size is getting smaller. This will reflow the
|
||||||
|
/// soft wrapped text.
|
||||||
pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
||||||
|
// We do this in a pretty inefficient way because this implementation
|
||||||
|
// is easier and resizing is relatively rare. I welcome anyone to improve
|
||||||
|
// on this! Our naive approach is to just iterate over the entire screen
|
||||||
|
// (including scrollback) and reflow the entire thing by rewriting it.
|
||||||
|
// TODO: above not implemented yet
|
||||||
|
|
||||||
// Make a copy so we can access the old indexes.
|
// Make a copy so we can access the old indexes.
|
||||||
const old = self.*;
|
const old = self.*;
|
||||||
|
|
||||||
@ -381,8 +416,8 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
while (y < old.rows) : (y += 1) {
|
while (y < old.rows) : (y += 1) {
|
||||||
// Copy the old row into the new row, just losing the columsn
|
// Copy the old row into the new row, just losing the columsn
|
||||||
// if we got thinner.
|
// if we got thinner.
|
||||||
const old_row = old.getRow(y);
|
const old_row = old.getRow(.{ .viewport = y });
|
||||||
const new_row = self.getRow(y - start);
|
const new_row = self.getRow(.{ .viewport = y - start });
|
||||||
std.mem.copy(Cell, new_row, old_row[0..col_end]);
|
std.mem.copy(Cell, new_row, old_row[0..col_end]);
|
||||||
|
|
||||||
// If our new row is wider, then we copy zeroes into the rest.
|
// If our new row is wider, then we copy zeroes into the rest.
|
||||||
@ -393,7 +428,7 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
|
|
||||||
// If we grew rows, then set the remaining data to zero.
|
// If we grew rows, then set the remaining data to zero.
|
||||||
if (rows > old.rows) {
|
if (rows > old.rows) {
|
||||||
std.mem.set(Cell, self.storage[self.viewportRowIndex(old.rows)..], .{ .char = 0 });
|
std.mem.set(Cell, self.storage[self.rowIndex(.{ .viewport = old.rows })..], .{ .char = 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the old data
|
// Free the old data
|
||||||
@ -491,8 +526,8 @@ fn selectionSlices(self: Screen, sel: Selection) struct {
|
|||||||
// Get the true "top" and "bottom"
|
// Get the true "top" and "bottom"
|
||||||
const sel_top = sel.topLeft();
|
const sel_top = sel.topLeft();
|
||||||
const sel_bot = sel.bottomRight();
|
const sel_bot = sel.bottomRight();
|
||||||
const top = self.rowIndex(sel_top.y);
|
const top = self.rowIndex(.{ .screen = sel_top.y });
|
||||||
const bot = self.rowIndex(sel_bot.y);
|
const bot = self.rowIndex(.{ .screen = sel_bot.y });
|
||||||
|
|
||||||
// The bottom and top are available in one contiguous slice.
|
// The bottom and top are available in one contiguous slice.
|
||||||
if (bot >= top) {
|
if (bot >= top) {
|
||||||
@ -546,13 +581,13 @@ pub fn testString(self: Screen, alloc: Allocator) ![]const u8 {
|
|||||||
fn testWriteString(self: *Screen, text: []const u8) void {
|
fn testWriteString(self: *Screen, text: []const u8) void {
|
||||||
var y: usize = 0;
|
var y: usize = 0;
|
||||||
var x: usize = 0;
|
var x: usize = 0;
|
||||||
var row = self.getRow(y);
|
var row = self.getRow(.{ .viewport = y });
|
||||||
for (text) |c| {
|
for (text) |c| {
|
||||||
// Explicit newline forces a new row
|
// Explicit newline forces a new row
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
y += 1;
|
y += 1;
|
||||||
x = 0;
|
x = 0;
|
||||||
row = self.getRow(y);
|
row = self.getRow(.{ .viewport = y });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +596,7 @@ fn testWriteString(self: *Screen, text: []const u8) void {
|
|||||||
row[x - 1].attrs.wrap = 1;
|
row[x - 1].attrs.wrap = 1;
|
||||||
y += 1;
|
y += 1;
|
||||||
x = 0;
|
x = 0;
|
||||||
row = self.getRow(y);
|
row = self.getRow(.{ .viewport = y });
|
||||||
}
|
}
|
||||||
|
|
||||||
row[x].char = @intCast(u32, c);
|
row[x].char = @intCast(u32, c);
|
||||||
@ -588,7 +623,7 @@ test "Screen" {
|
|||||||
var iter = s.rowIterator();
|
var iter = s.rowIterator();
|
||||||
while (iter.next()) |row| {
|
while (iter.next()) |row| {
|
||||||
// Rows should be pointer equivalent to getRow
|
// Rows should be pointer equivalent to getRow
|
||||||
const row_other = s.getRow(count);
|
const row_other = s.getRow(.{ .viewport = count });
|
||||||
try testing.expectEqual(row.ptr, row_other.ptr);
|
try testing.expectEqual(row.ptr, row_other.ptr);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
@ -612,9 +647,9 @@ test "Screen: scrolling" {
|
|||||||
try testing.expect(s.viewportIsBottom());
|
try testing.expect(s.viewportIsBottom());
|
||||||
|
|
||||||
// Test our row index
|
// Test our row index
|
||||||
try testing.expectEqual(@as(usize, 5), s.viewportRowIndex(0));
|
try testing.expectEqual(@as(usize, 5), s.rowIndex(.{ .viewport = 0 }));
|
||||||
try testing.expectEqual(@as(usize, 10), s.viewportRowIndex(1));
|
try testing.expectEqual(@as(usize, 10), s.rowIndex(.{ .viewport = 1 }));
|
||||||
try testing.expectEqual(@as(usize, 0), s.viewportRowIndex(2));
|
try testing.expectEqual(@as(usize, 0), s.rowIndex(.{ .viewport = 2 }));
|
||||||
|
|
||||||
{
|
{
|
||||||
// Test our contents rotated
|
// Test our contents rotated
|
||||||
@ -650,9 +685,9 @@ test "Screen: scrolling" {
|
|||||||
// try testing.expect(s.viewportIsBottom());
|
// try testing.expect(s.viewportIsBottom());
|
||||||
//
|
//
|
||||||
// // Test our row index
|
// // Test our row index
|
||||||
// try testing.expectEqual(@as(usize, 5), s.viewportRowIndex(0));
|
// try testing.expectEqual(@as(usize, 5), s.rowIndex(0));
|
||||||
// try testing.expectEqual(@as(usize, 10), s.viewportRowIndex(1));
|
// try testing.expectEqual(@as(usize, 10), s.rowIndex(1));
|
||||||
// try testing.expectEqual(@as(usize, 15), s.viewportRowIndex(2));
|
// try testing.expectEqual(@as(usize, 15), s.rowIndex(2));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
test "Screen: scroll down from 0" {
|
test "Screen: scroll down from 0" {
|
||||||
@ -683,9 +718,9 @@ test "Screen: scrollback" {
|
|||||||
s.scroll(.{ .delta = 1 });
|
s.scroll(.{ .delta = 1 });
|
||||||
|
|
||||||
// Test our row index
|
// Test our row index
|
||||||
try testing.expectEqual(@as(usize, 5), s.viewportRowIndex(0));
|
try testing.expectEqual(@as(usize, 5), s.rowIndex(.{ .viewport = 0 }));
|
||||||
try testing.expectEqual(@as(usize, 10), s.viewportRowIndex(1));
|
try testing.expectEqual(@as(usize, 10), s.rowIndex(.{ .viewport = 1 }));
|
||||||
try testing.expectEqual(@as(usize, 15), s.viewportRowIndex(2));
|
try testing.expectEqual(@as(usize, 15), s.rowIndex(.{ .viewport = 2 }));
|
||||||
|
|
||||||
{
|
{
|
||||||
// Test our contents rotated
|
// Test our contents rotated
|
||||||
@ -914,7 +949,7 @@ test "Screen: selectionString wrap around" {
|
|||||||
// we're out of space.
|
// we're out of space.
|
||||||
s.scroll(.{ .delta = 1 });
|
s.scroll(.{ .delta = 1 });
|
||||||
try testing.expect(s.viewportIsBottom());
|
try testing.expect(s.viewportIsBottom());
|
||||||
try testing.expectEqual(@as(usize, 0), s.viewportRowIndex(2));
|
try testing.expectEqual(@as(usize, 0), s.rowIndex(.{ .viewport = 2 }));
|
||||||
s.testWriteString("1ABCD\n2EFGH\n3IJKL");
|
s.testWriteString("1ABCD\n2EFGH\n3IJKL");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -383,7 +383,7 @@ pub fn decaln(self: *Terminal) void {
|
|||||||
|
|
||||||
// Fill with Es, does not move cursor. We reset fg/bg so we can just
|
// Fill with Es, does not move cursor. We reset fg/bg so we can just
|
||||||
// optimize here by doing row copies.
|
// optimize here by doing row copies.
|
||||||
const filled = self.screen.getRow(0);
|
const filled = self.screen.getRow(.{ .viewport = 0 });
|
||||||
var col: usize = 0;
|
var col: usize = 0;
|
||||||
while (col < self.cols) : (col += 1) {
|
while (col < self.cols) : (col += 1) {
|
||||||
filled[col] = .{ .char = 'E' };
|
filled[col] = .{ .char = 'E' };
|
||||||
@ -391,7 +391,7 @@ pub fn decaln(self: *Terminal) void {
|
|||||||
|
|
||||||
var row: usize = 1;
|
var row: usize = 1;
|
||||||
while (row < self.rows) : (row += 1) {
|
while (row < self.rows) : (row += 1) {
|
||||||
std.mem.copy(Screen.Cell, self.screen.getRow(row), filled);
|
std.mem.copy(Screen.Cell, self.screen.getRow(.{ .viewport = row }), filled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,12 +615,12 @@ pub fn eraseLine(
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
.right => {
|
.right => {
|
||||||
const row = self.screen.getRow(self.screen.cursor.y);
|
const row = self.screen.getRow(.{ .viewport = self.screen.cursor.y });
|
||||||
std.mem.set(Screen.Cell, row[self.screen.cursor.x..], self.screen.cursor.pen);
|
std.mem.set(Screen.Cell, row[self.screen.cursor.x..], self.screen.cursor.pen);
|
||||||
},
|
},
|
||||||
|
|
||||||
.left => {
|
.left => {
|
||||||
const row = self.screen.getRow(self.screen.cursor.y);
|
const row = self.screen.getRow(.{ .viewport = self.screen.cursor.y });
|
||||||
std.mem.set(Screen.Cell, row[0 .. self.screen.cursor.x + 1], self.screen.cursor.pen);
|
std.mem.set(Screen.Cell, row[0 .. self.screen.cursor.x + 1], self.screen.cursor.pen);
|
||||||
|
|
||||||
// Unsets pending wrap state
|
// Unsets pending wrap state
|
||||||
@ -628,7 +628,7 @@ pub fn eraseLine(
|
|||||||
},
|
},
|
||||||
|
|
||||||
.complete => {
|
.complete => {
|
||||||
const row = self.screen.getRow(self.screen.cursor.y);
|
const row = self.screen.getRow(.{ .viewport = self.screen.cursor.y });
|
||||||
std.mem.set(Screen.Cell, row, self.screen.cursor.pen);
|
std.mem.set(Screen.Cell, row, self.screen.cursor.pen);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -821,7 +821,7 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the current row
|
// Get the current row
|
||||||
const row = self.screen.getRow(self.screen.cursor.y);
|
const row = self.screen.getRow(.{ .viewport = self.screen.cursor.y });
|
||||||
|
|
||||||
// Determine our indexes.
|
// Determine our indexes.
|
||||||
const start = self.screen.cursor.x;
|
const start = self.screen.cursor.x;
|
||||||
@ -943,7 +943,7 @@ pub fn deleteLines(self: *Terminal, count: usize) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (y <= self.scrolling_region.bottom) : (y += 1) {
|
while (y <= self.scrolling_region.bottom) : (y += 1) {
|
||||||
const row = self.screen.getRow(y);
|
const row = self.screen.getRow(.{ .viewport = y });
|
||||||
std.mem.set(Screen.Cell, row, self.screen.cursor.pen);
|
std.mem.set(Screen.Cell, row, self.screen.cursor.pen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user