mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
when growing cols, adjust viewport and cursor if we shorten
This commit is contained in:
@ -517,6 +517,9 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
self.storage = storage;
|
self.storage = storage;
|
||||||
self.cols = cols;
|
self.cols = cols;
|
||||||
|
|
||||||
|
// Whether we need to move the cursor or not
|
||||||
|
var new_cursor: ?point.ScreenPoint = null;
|
||||||
|
|
||||||
// Iterate over the screen since we need to check for reflow.
|
// Iterate over the screen since we need to check for reflow.
|
||||||
var iter = old.rowIterator(.screen);
|
var iter = old.rowIterator(.screen);
|
||||||
var y: usize = 0;
|
var y: usize = 0;
|
||||||
@ -525,6 +528,13 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
var new_row = self.getRow(.{ .screen = y });
|
var new_row = self.getRow(.{ .screen = y });
|
||||||
std.mem.copy(Cell, new_row, row);
|
std.mem.copy(Cell, new_row, row);
|
||||||
|
|
||||||
|
// We need to check if our cursor was on this line
|
||||||
|
// and in the part that WAS copied. If so, we need to move it.
|
||||||
|
if (cursor_pos.y == iter.value - 1) {
|
||||||
|
assert(new_cursor == null); // should only happen once
|
||||||
|
new_cursor = .{ .y = y, .x = cursor_pos.x };
|
||||||
|
}
|
||||||
|
|
||||||
// If no reflow, just keep going
|
// If no reflow, just keep going
|
||||||
if (row[row.len - 1].attrs.wrap == 0) {
|
if (row[row.len - 1].attrs.wrap == 0) {
|
||||||
y += 1;
|
y += 1;
|
||||||
@ -535,9 +545,6 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
// The goal is to keep the messiness of reflow down here and
|
// The goal is to keep the messiness of reflow down here and
|
||||||
// only reloop when we're back to clean non-wrapped lines.
|
// only reloop when we're back to clean non-wrapped lines.
|
||||||
|
|
||||||
// Whether we need to move the cursor or not
|
|
||||||
var new_cursor: ?point.ScreenPoint = null;
|
|
||||||
|
|
||||||
// Mark the last element as not wrapped
|
// Mark the last element as not wrapped
|
||||||
new_row[row.len - 1].attrs.wrap = 0;
|
new_row[row.len - 1].attrs.wrap = 0;
|
||||||
|
|
||||||
@ -545,7 +552,15 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
var x: usize = row.len;
|
var x: usize = row.len;
|
||||||
new_row = new_row[x..];
|
new_row = new_row[x..];
|
||||||
wrapping: while (iter.next()) |wrapped_row| {
|
wrapping: while (iter.next()) |wrapped_row| {
|
||||||
var wrapped_rem = wrapped_row;
|
// Trim the row from the right so that we ignore all trailing
|
||||||
|
// empty chars and don't wrap them.
|
||||||
|
const trimmed_row = trim: {
|
||||||
|
var i: usize = wrapped_row.len;
|
||||||
|
while (i > 0) : (i -= 1) if (!wrapped_row[i - 1].empty()) break;
|
||||||
|
break :trim wrapped_row[0..i];
|
||||||
|
};
|
||||||
|
|
||||||
|
var wrapped_rem = trimmed_row;
|
||||||
while (wrapped_rem.len > 0) {
|
while (wrapped_rem.len > 0) {
|
||||||
// If the wrapped row fits nicely...
|
// If the wrapped row fits nicely...
|
||||||
if (wrapped_rem.len <= new_row.len) {
|
if (wrapped_rem.len <= new_row.len) {
|
||||||
@ -561,6 +576,17 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
// If this row isn't also wrapped, we're done!
|
// If this row isn't also wrapped, we're done!
|
||||||
if (wrapped_rem[wrapped_rem.len - 1].attrs.wrap == 0) {
|
if (wrapped_rem[wrapped_rem.len - 1].attrs.wrap == 0) {
|
||||||
y += 1;
|
y += 1;
|
||||||
|
|
||||||
|
// If we were able to copy the entire row then
|
||||||
|
// we shortened the screen by one. We need to reflect
|
||||||
|
// this in our viewport.
|
||||||
|
if (wrapped_rem.len == trimmed_row.len and
|
||||||
|
self.visible_offset > 0)
|
||||||
|
{
|
||||||
|
self.visible_offset -= 1;
|
||||||
|
self.bottom -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
break :wrapping;
|
break :wrapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,14 +620,14 @@ pub fn resize(self: *Screen, alloc: Allocator, rows: usize, cols: usize) !void {
|
|||||||
new_row = self.getRow(.{ .screen = y });
|
new_row = self.getRow(.{ .screen = y });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a new cursor, we need to convert that to a viewport
|
// If we have a new cursor, we need to convert that to a viewport
|
||||||
// point and set it up.
|
// point and set it up.
|
||||||
if (new_cursor) |pos| {
|
if (new_cursor) |pos| {
|
||||||
const viewport_pos = pos.toViewport(self);
|
const viewport_pos = pos.toViewport(self);
|
||||||
self.cursor.x = viewport_pos.x;
|
self.cursor.x = viewport_pos.x;
|
||||||
self.cursor.y = viewport_pos.y;
|
self.cursor.y = viewport_pos.y;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,6 +996,10 @@ 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;
|
||||||
|
if (y >= self.rows) {
|
||||||
|
y -= 1;
|
||||||
|
self.scroll(.{ .delta = 1 });
|
||||||
|
}
|
||||||
row = self.getRow(.{ .active = y });
|
row = self.getRow(.{ .active = y });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1451,6 +1481,40 @@ test "Screen: resize more cols with reflow that fits full width" {
|
|||||||
try testing.expectEqual(@as(usize, 0), s.cursor.y);
|
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, 3, 6, 0);
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
const str = "1ABCD2EFGH\n3IJKL";
|
||||||
|
s.testWriteString(str);
|
||||||
|
|
||||||
|
// Verify we soft wrapped
|
||||||
|
{
|
||||||
|
var contents = try s.testString(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.cursor.x = 0;
|
||||||
|
s.cursor.y = 2;
|
||||||
|
try testing.expectEqual(@as(u32, '3'), s.getCell(s.cursor.y, s.cursor.x).char);
|
||||||
|
|
||||||
|
// Resize and verify we undid the soft wrap because we have space now
|
||||||
|
try s.resize(alloc, 3, 10);
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings(str, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our cursor should still be on the 3
|
||||||
|
try testing.expectEqual(@as(u32, '3'), s.getCell(s.cursor.y, s.cursor.x).char);
|
||||||
|
}
|
||||||
|
|
||||||
test "Screen: resize more cols with reflow that forces more wrapping" {
|
test "Screen: resize more cols with reflow that forces more wrapping" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
@ -1523,6 +1587,41 @@ test "Screen: resize more cols with reflow that unwraps multiple times" {
|
|||||||
try testing.expectEqual(@as(usize, 0), s.cursor.y);
|
try testing.expectEqual(@as(usize, 0), s.cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Screen: resize more cols with populated scrollback" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var s = try init(alloc, 3, 5, 5);
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
const str = "1ABCD\n2EFGH\n3IJKL\n4ABCD5EFGH";
|
||||||
|
s.testWriteString(str);
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
const expected = "3IJKL\n4ABCD\n5EFGH";
|
||||||
|
try testing.expectEqualStrings(expected, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Set our cursor to be on the "5"
|
||||||
|
s.cursor.x = 0;
|
||||||
|
s.cursor.y = 2;
|
||||||
|
try testing.expectEqual(@as(u32, '5'), s.getCell(s.cursor.y, s.cursor.x).char);
|
||||||
|
|
||||||
|
// Resize
|
||||||
|
try s.resize(alloc, 3, 10);
|
||||||
|
|
||||||
|
// Cursor should still be on the "5"
|
||||||
|
log.warn("cursor={}", .{s.cursor});
|
||||||
|
try testing.expectEqual(@as(u32, '5'), s.getCell(s.cursor.y, s.cursor.x).char);
|
||||||
|
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .viewport);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
const expected = "2EFGH\n3IJKL\n4ABCD5EFGH";
|
||||||
|
try testing.expectEqualStrings(expected, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Screen: resize less rows no scrollback" {
|
test "Screen: resize less rows no scrollback" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user