terminal: adjust page capacity for graphemes if necessary

pull/1609/head
Mitchell Hashimoto 2024-03-13 20:54:20 -07:00
parent c0ed1fa370
commit a59d4286c7
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
5 changed files with 57 additions and 16 deletions

View File

@ -20,8 +20,3 @@ Major Features:
- Bell
- Sixels: https://saitoha.github.io/libsixel/
paged-terminal branch:
- tests and logic for overflowing page capacities:
- graphemes

View File

@ -1033,6 +1033,48 @@ pub fn manualStyleUpdate(self: *Screen) !void {
self.cursor.style_ref = &md.ref;
}
/// Append a grapheme to the given cell within the current cursor row.
pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void {
self.cursor.page_pin.page.data.appendGrapheme(
self.cursor.page_row,
cell,
cp,
) catch |err| switch (err) {
error.OutOfMemory => {
// We need to determine the actual cell index of the cell so
// that after we adjust the capacity we can reload the cell.
const cell_idx: usize = cell_idx: {
const cells: [*]Cell = @ptrCast(self.cursor.page_cell);
const zero: [*]Cell = cells - self.cursor.x;
const target: [*]Cell = @ptrCast(cell);
const cell_idx = (@intFromPtr(target) - @intFromPtr(zero)) / @sizeOf(Cell);
break :cell_idx cell_idx;
};
// Adjust our capacity. This will update our cursor page pin and
// force us to reload.
const original_node = self.cursor.page_pin.page;
const new_bytes = original_node.data.capacity.grapheme_bytes * 2;
_ = try self.pages.adjustCapacity(original_node, .{ .grapheme_bytes = new_bytes });
self.cursorReload();
// The cell pointer is now invalid, so we need to get it from
// the reloaded cursor pointers.
const reloaded_cell: *Cell = switch (std.math.order(cell_idx, self.cursor.x)) {
.eq => self.cursor.page_cell,
.lt => self.cursorCellLeft(@intCast(self.cursor.x - cell_idx)),
.gt => self.cursorCellRight(@intCast(cell_idx - self.cursor.x)),
};
try self.cursor.page_pin.page.data.appendGrapheme(
self.cursor.page_row,
reloaded_cell,
cp,
);
},
};
}
/// Set the selection to the given selection. If this is a tracked selection
/// then the screen will take overnship of the selection. If this is untracked
/// then the screen will convert it to tracked internally. This will automatically

View File

@ -351,11 +351,7 @@ pub fn print(self: *Terminal, c: u21) !void {
}
log.debug("c={x} grapheme attach to left={}", .{ c, prev.left });
try self.screen.cursor.page_pin.page.data.appendGrapheme(
self.screen.cursor.page_row,
prev.cell,
c,
);
try self.screen.appendGrapheme(prev.cell, c);
return;
}
}
@ -408,11 +404,7 @@ pub fn print(self: *Terminal, c: u21) !void {
if (!emoji) return;
}
try self.screen.cursor.page_pin.page.data.appendGrapheme(
self.screen.cursor.page_row,
prev,
c,
);
try self.screen.appendGrapheme(prev, c);
return;
}
@ -2299,6 +2291,17 @@ test "Terminal: input unique style per cell" {
}
}
test "Terminal: input glitch text" {
const glitch = @embedFile("res/glitch.txt");
const alloc = testing.allocator;
var t = try init(alloc, .{ .cols = 30, .rows = 30 });
defer t.deinit(alloc);
for (0..100) |_| {
try t.printString(glitch);
}
}
test "Terminal: zero-width character at start" {
var t = try init(testing.allocator, .{ .cols = 80, .rows = 80 });
defer t.deinit(testing.allocator);

View File

@ -373,7 +373,7 @@ pub const Page = struct {
}
/// Append a codepoint to the given cell as a grapheme.
pub fn appendGrapheme(self: *Page, row: *Row, cell: *Cell, cp: u21) !void {
pub fn appendGrapheme(self: *Page, row: *Row, cell: *Cell, cp: u21) Allocator.Error!void {
if (comptime std.debug.runtime_safety) assert(cell.hasText());
const cell_offset = getOffset(Cell, self.memory, cell);

1
src/terminal/res/glitch.txt vendored Normal file
View File

@ -0,0 +1 @@
Ḡ̴͎͍̜͎͔͕̩̗͕͖̟̜͑̊̌̇̑͒͋͑̄̈́͐̈́́̽͌͂̎̀̔͋̓́̅̌̇͘̕͜͝͝h̶̡̞̫͉̳̬̜̱̥͕͑̾͛̒̆̒̉̒̑͂̄͘ͅǫ̷̨̥͔͔͖̭͚͙̯̟̭̘͇̫̰͚̺̳̙̳̟͚̫̱̹̱͒̂̑͒͜͠ͅş̴̖̰̜̱̹͙̅͒̀̏͆̐̋͂̓͋̃̈̔̂̈͛̐̿̔́̔̄͑̇͑̋̈́͌͋̾̃̽̈́̕͘̚͘͘͘͠͠t̵̢̜̱̦͇͉̬̮̼͖̳̗̥̝̬͇͕̥̜͕̳̱̥̮͉̮̩̘̰̪̤͉͎̲͈͍̳̟̠͈̝̫͋̊̀͐̍̅̀̄̃̈́̔̇̈́̄̃̽̂̌̅̄̋͒̃̈́̍̀̍̇̽̐͊̾̆̅̈̿̓͒̄̾͌̚͝͝͝͝͝t̴̥̼̳̗̬̬͔͎̯͉͇̮̰͖͇̝͔̳̳̗̰͇͎͉̬͇̝̺̯͎͖͔̍͆͒̊̒̔̊̈́̿̊̅͂̐͋̿͂̈̒̄͜͠͠ÿ̴̢̗̜̥͇͖̰͎̝̹̗̪̙̞̣̳͎̯̹͚̲̝̗̳̳̗̖͎̗̬͈͙̝̟͍̥̤͖͇̰͈̺͛̒̂͌̌̏̈̾̓̈́̿͐̂̓̔̓̂̈́͑͛͊͋̔̿̊͑͌̊̏͘͘̕͘͠͝