mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
terminal/new: left/right margins insertLines
This commit is contained in:
@ -3443,6 +3443,7 @@ test "Terminal: setLeftAndRightMargin simple" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: setLeftAndRightMargin left only" {
|
test "Terminal: setLeftAndRightMargin left only" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -3469,6 +3470,7 @@ test "Terminal: setLeftAndRightMargin left only" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: setLeftAndRightMargin left and right" {
|
test "Terminal: setLeftAndRightMargin left and right" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -3493,6 +3495,7 @@ test "Terminal: setLeftAndRightMargin left and right" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: setLeftAndRightMargin left equal right" {
|
test "Terminal: setLeftAndRightMargin left equal right" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -3517,6 +3520,7 @@ test "Terminal: setLeftAndRightMargin left equal right" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: setLeftAndRightMargin mode 69 unset" {
|
test "Terminal: setLeftAndRightMargin mode 69 unset" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
|
@ -1045,11 +1045,6 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
self.screen.cursor.x < self.scrolling_region.left or
|
self.screen.cursor.x < self.scrolling_region.left or
|
||||||
self.screen.cursor.x > self.scrolling_region.right) return;
|
self.screen.cursor.x > self.scrolling_region.right) return;
|
||||||
|
|
||||||
// TODO
|
|
||||||
if (self.scrolling_region.left > 0 or self.scrolling_region.right < self.cols - 1) {
|
|
||||||
@panic("TODO: left and right margin mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remaining rows from our cursor to the bottom of the scroll region.
|
// Remaining rows from our cursor to the bottom of the scroll region.
|
||||||
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
||||||
|
|
||||||
@ -1071,16 +1066,33 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
|
|
||||||
// TODO: detect active area split across multiple pages
|
// TODO: detect active area split across multiple pages
|
||||||
|
|
||||||
|
// If we have left/right scroll margins we have a slower path.
|
||||||
|
const left_right = self.scrolling_region.left > 0 or
|
||||||
|
self.scrolling_region.right < self.cols - 1;
|
||||||
|
|
||||||
// We work backwards so we don't overwrite data.
|
// We work backwards so we don't overwrite data.
|
||||||
while (@intFromPtr(y) >= @intFromPtr(top)) : (y -= 1) {
|
while (@intFromPtr(y) >= @intFromPtr(top)) : (y -= 1) {
|
||||||
const src: *Row = @ptrCast(y);
|
const src: *Row = @ptrCast(y);
|
||||||
const dst: *Row = @ptrCast(y + adjusted_count);
|
const dst: *Row = @ptrCast(y + adjusted_count);
|
||||||
|
|
||||||
// Swap the src/dst cells. This ensures that our dst gets the proper
|
if (!left_right) {
|
||||||
// shifted rows and src gets non-garbage cell data that we can clear.
|
// Swap the src/dst cells. This ensures that our dst gets the proper
|
||||||
const dst_row = dst.*;
|
// shifted rows and src gets non-garbage cell data that we can clear.
|
||||||
dst.* = src.*;
|
const dst_row = dst.*;
|
||||||
src.* = dst_row;
|
dst.* = src.*;
|
||||||
|
src.* = dst_row;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left/right scroll margins we have to copy cells, which is much slower...
|
||||||
|
var page = self.screen.cursor.page_offset.page.data;
|
||||||
|
page.moveCells(
|
||||||
|
src,
|
||||||
|
self.scrolling_region.left,
|
||||||
|
dst,
|
||||||
|
self.scrolling_region.left,
|
||||||
|
(self.scrolling_region.right - self.scrolling_region.left) + 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,7 +1114,10 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
assert(!row.grapheme);
|
assert(!row.grapheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@memset(cells, blank_cell);
|
@memset(
|
||||||
|
cells[self.scrolling_region.left .. self.scrolling_region.right + 1],
|
||||||
|
blank_cell,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the cursor to the left margin. But importantly this also
|
// Move the cursor to the left margin. But importantly this also
|
||||||
@ -2460,6 +2475,104 @@ test "Terminal: setLeftAndRightMargin simple" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left only" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(2, 0);
|
||||||
|
try testing.expectEqual(@as(usize, 1), t.scrolling_region.left);
|
||||||
|
try testing.expectEqual(@as(usize, t.cols - 1), t.scrolling_region.right);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("A\nDBC\nGEF\n HI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left and right" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(1, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" C\nABF\nDEI\nGH", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin left equal right" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, true);
|
||||||
|
t.setLeftAndRightMargin(2, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Terminal: setLeftAndRightMargin mode 69 unset" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.modes.set(.enable_left_and_right_margin, false);
|
||||||
|
t.setLeftAndRightMargin(1, 2);
|
||||||
|
t.setCursorPos(1, 2);
|
||||||
|
t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\nABC\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: insertLines simple" {
|
test "Terminal: insertLines simple" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
|
@ -202,6 +202,33 @@ pub const Page = struct {
|
|||||||
return .{ .row = row, .cell = cell };
|
return .{ .row = row, .cell = cell };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move a cell from one location to another. This will replace the
|
||||||
|
/// previous contents with a blank cell. Because this is a move, this
|
||||||
|
/// doesn't allocate and can't fail.
|
||||||
|
pub fn moveCells(
|
||||||
|
self: *Page,
|
||||||
|
src_row: *Row,
|
||||||
|
src_left: usize,
|
||||||
|
dst_row: *Row,
|
||||||
|
dst_left: usize,
|
||||||
|
len: usize,
|
||||||
|
) void {
|
||||||
|
const src_cells = src_row.cells.ptr(self.memory)[src_left .. src_left + len];
|
||||||
|
const dst_cells = dst_row.cells.ptr(self.memory)[dst_left .. dst_left + len];
|
||||||
|
|
||||||
|
// If src has no graphemes, this is very fast.
|
||||||
|
const src_grapheme = src_row.grapheme or grapheme: {
|
||||||
|
for (src_cells) |c| if (c.hasGrapheme()) break :grapheme true;
|
||||||
|
break :grapheme false;
|
||||||
|
};
|
||||||
|
if (!src_grapheme) {
|
||||||
|
@memcpy(dst_cells, src_cells);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@panic("TODO: grapheme move");
|
||||||
|
}
|
||||||
|
|
||||||
/// Append a codepoint to the given cell as a grapheme.
|
/// 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) !void {
|
||||||
if (comptime std.debug.runtime_safety) assert(cell.hasText());
|
if (comptime std.debug.runtime_safety) assert(cell.hasText());
|
||||||
|
Reference in New Issue
Block a user