diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 4d3d11424..79672102e 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -6442,6 +6442,7 @@ test "Screen: resize more rows with populated scrollback" { } } +// X test "Screen: resize more rows and cols with wrapping" { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/terminal/new/PageList.zig b/src/terminal/new/PageList.zig index 72e472661..e7ebe8c05 100644 --- a/src/terminal/new/PageList.zig +++ b/src/terminal/new/PageList.zig @@ -457,6 +457,46 @@ fn resizeGrowCols( } else { for (total..self.rows) |_| _ = try self.grow(); } + + // If we have a cursor, we need to update the correct y value. I'm + // not at all happy about this, I wish we could do this in a more + // efficient way as we resize the pages. But at the time of typing this + // I can't think of a way and I'd rather get things working. Someone please + // help! + // + // The challenge is that as rows are unwrapped, we want to preserve the + // cursor. So for examle if you have "A\nB" where AB is soft-wrapped and + // the cursor is on 'B' (x=0, y=1) and you grow the columns, we want + // the cursor to remain on B (x=1, y=0) as it grows. + // + // The easy thing to do would be to count how many rows we unwrapped + // and then subtract that from the original y. That's how I started. The + // challenge is that if we unwrap with scrollback, our scrollback is + // "pulled down" so that the original (x=0,y=0) line is now pushed down. + // Detecting this while resizing seems non-obvious. This is a tested case + // so if you change this logic, you should see failures or passes if it + // works. + // + // The approach I take instead is if we have a cursor offset, I work + // backwards to find the offset we marked while reflowing and update + // the y from that. This is _not terrible_ because active areas are + // generally small and this is a more or less linear search. Its just + // kind of clunky. + if (cursor) |c| cursor: { + const offset = c.offset orelse break :cursor; + var active_it = self.rowIterator(.{ .active = .{} }, null); + var y: size.CellCountInt = 0; + while (active_it.next()) |it_offset| { + if (it_offset.page == offset.page and + it_offset.row_offset == offset.row_offset) + { + c.y = y; + break :cursor; + } + + y += 1; + } + } } // We use a cursor to track where we are in the src/dst. This is very @@ -672,6 +712,9 @@ fn reflowPage( // better calculate the CHANGE in coordinate by subtracting // our dst from src which will calculate how many rows // we unwrapped to get here. + // + // Note this doesn't handle when we pull down scrollback. + // See the cursor updates in resizeGrowCols for that. c.y -|= src_cursor.y - dst_cursor.y; c.offset = .{ diff --git a/src/terminal/new/Screen.zig b/src/terminal/new/Screen.zig index b49c4853e..226aeef5d 100644 --- a/src/terminal/new/Screen.zig +++ b/src/terminal/new/Screen.zig @@ -2476,14 +2476,13 @@ test "Screen: resize more cols with populated scrollback" { } // Cursor should still be on the "5" - // TODO - // { - // const list_cell = s.pages.getCell(.{ .active = .{ - // .x = s.cursor.x, - // .y = s.cursor.y, - // } }).?; - // try testing.expectEqual(@as(u21, '5'), list_cell.cell.content.codepoint); - // } + { + const list_cell = s.pages.getCell(.{ .active = .{ + .x = s.cursor.x, + .y = s.cursor.y, + } }).?; + try testing.expectEqual(@as(u21, '5'), list_cell.cell.content.codepoint); + } } test "Screen: resize more cols with reflow" { @@ -2523,9 +2522,41 @@ test "Screen: resize more cols with reflow" { } // Our cursor should've moved - // TODO - // try testing.expectEqual(@as(size.CellCountInt, 2), s.cursor.x); - // try testing.expectEqual(@as(size.CellCountInt, 2), s.cursor.y); + try testing.expectEqual(@as(size.CellCountInt, 2), s.cursor.x); + try testing.expectEqual(@as(size.CellCountInt, 2), s.cursor.y); +} + +test "Screen: resize more rows and cols with wrapping" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 2, 4, 0); + defer s.deinit(); + const str = "1A2B\n3C4D"; + try s.testWriteString(str); + { + const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} }); + defer alloc.free(contents); + const expected = "1A\n2B\n3C\n4D"; + try testing.expectEqualStrings(expected, contents); + } + + try s.resize(5, 10); + + // Cursor should move due to wrapping + try testing.expectEqual(@as(size.CellCountInt, 3), s.cursor.x); + try testing.expectEqual(@as(size.CellCountInt, 1), s.cursor.y); + + { + const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} }); + defer alloc.free(contents); + try testing.expectEqualStrings(str, contents); + } + { + const contents = try s.dumpStringAlloc(alloc, .{ .screen = .{} }); + defer alloc.free(contents); + try testing.expectEqualStrings(str, contents); + } } test "Screen: resize less rows no scrollback" {