diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 1aff79a0c..e892f9e25 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -6711,6 +6711,7 @@ test "Screen: resize more cols with reflow that fits full width" { try testing.expectEqual(@as(usize, 0), s.cursor.y); } +// X test "Screen: resize more cols with reflow that ends in newline" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/terminal/new/PageList.zig b/src/terminal/new/PageList.zig index a6a980de8..784a87ff7 100644 --- a/src/terminal/new/PageList.zig +++ b/src/terminal/new/PageList.zig @@ -533,6 +533,20 @@ const ReflowCursor = struct { self.x = x; self.y = y; } + + fn countTrailingEmptyCells(self: *const ReflowCursor) usize { + // If the row is wrapped, all empty cells are meaningful. + if (self.page_row.wrap) return 0; + + const cells: [*]pagepkg.Cell = @ptrCast(self.page_cell); + const len: usize = self.page.size.cols - self.x; + for (0..len) |i| { + const rev_i = len - i - 1; + if (!cells[rev_i].isEmpty()) return i; + } + + return len; + } }; /// Reflow the given page into the new capacity. The new capacity can have @@ -590,9 +604,24 @@ fn reflowPage( } src_cursor.cursorAbsolute(0, @intCast(src_y)); - for (src_cursor.x..src_cursor.page.size.cols) |src_x| { + + // Trim trailing empty cells if the row is not wrapped. If the + // row is wrapped then we don't trim trailing empty cells because + // the empty cells can be meaningful. + const trailing_empty = src_cursor.countTrailingEmptyCells(); + const cols_len = src_cursor.page.size.cols - trailing_empty; + + for (src_cursor.x..cols_len) |src_x| { assert(src_cursor.x == src_x); + // std.log.warn("src_y={} src_x={} dst_y={} dst_x={} cp={u}", .{ + // src_cursor.y, + // src_cursor.x, + // dst_cursor.y, + // dst_cursor.x, + // src_cursor.page_cell.content.codepoint, + // }); + if (dst_cursor.pending_wrap) { dst_cursor.page_row.wrap = true; dst_cursor.cursorScroll(); @@ -2940,7 +2969,7 @@ test "PageList resize reflow more cols cursor in wrapped row that isn't unwrappe rac.row.wrap_continuation = true; } for (0..s.cols) |x| { - const rac = page.getRowAndCell(x, 1); + const rac = page.getRowAndCell(x, 2); rac.cell.* = .{ .content_tag = .codepoint, .content = .{ .codepoint = @intCast(x) }, diff --git a/src/terminal/new/Screen.zig b/src/terminal/new/Screen.zig index 5877b076b..c19840522 100644 --- a/src/terminal/new/Screen.zig +++ b/src/terminal/new/Screen.zig @@ -2304,3 +2304,48 @@ test "Screen: resize more cols with reflow that fits full width" { try testing.expectEqual(@as(usize, 5), s.cursor.x); try testing.expectEqual(@as(usize, 0), s.cursor.y); } + +test "Screen: resize more cols with reflow that ends in newline" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 6, 3, 0); + defer s.deinit(); + const str = "1ABCD2EFGH\n3IJKL"; + try s.testWriteString(str); + + // Verify we soft wrapped + { + const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} }); + defer alloc.free(contents); + const expected = "1ABCD2\nEFGH\n3IJKL"; + try testing.expectEqualStrings(expected, contents); + } + + // Let's put our cursor on the last row + s.cursorAbsolute(0, 2); + { + const list_cell = s.pages.getCell(.{ .active = .{ + .x = s.cursor.x, + .y = s.cursor.y, + } }).?; + try testing.expectEqual(@as(u21, '3'), list_cell.cell.content.codepoint); + } + + // Resize and verify we undid the soft wrap because we have space now + try s.resize(10, 3); + { + const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} }); + defer alloc.free(contents); + try testing.expectEqualStrings(str, contents); + } + + // Our cursor should still be on the 3 + { + const list_cell = s.pages.getCell(.{ .active = .{ + .x = s.cursor.x, + .y = s.cursor.y, + } }).?; + try testing.expectEqual(@as(u21, '3'), list_cell.cell.content.codepoint); + } +}