mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal: index from bottom row of scroll region always makes scrollback
Ghostty previously incorrectly only created scrollback if the top/bot margins were the full height of the viewport. The actual xterm behavior is to create scrollback as long as the top margin is the top row and the cursor is on the bottom margin (wherever that may be).
This commit is contained in:
@ -1109,15 +1109,51 @@ pub fn index(self: *Terminal) !void {
|
||||
// Scrolling dirties the images because it updates their placements pins.
|
||||
self.screen.kitty_images.dirty = true;
|
||||
|
||||
// If our scrolling region is the full screen, we create scrollback.
|
||||
// Otherwise, we simply scroll the region.
|
||||
// If our scrolling region is at the top, we create scrollback.
|
||||
if (self.scrolling_region.top == 0 and
|
||||
self.scrolling_region.bottom == self.rows - 1 and
|
||||
self.scrolling_region.left == 0 and
|
||||
self.scrolling_region.right == self.cols - 1)
|
||||
{
|
||||
// TODO: check if left/right need to be margin in xterm
|
||||
|
||||
// If our scrolling region is the full screen, this is an
|
||||
// easy and fast operation since we can just call grow.
|
||||
if (self.scrolling_region.bottom == self.rows - 1) {
|
||||
try self.screen.cursorDownScroll();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Our scrolling region is partially down the screen. In this
|
||||
// case we need to move the top of the scroll region into
|
||||
// scrollback while keeping the bottom of the scroll region
|
||||
// at the bottom of the screen.
|
||||
|
||||
// To do this today we break it down into a few operations:
|
||||
// 1. Pretend we're at the bottom of the screen and scroll
|
||||
// everything up.
|
||||
// 2. Create a new scroll region from the bottom of the old
|
||||
// scroll region to the bottom of the screen.
|
||||
// 3. Use `insertLines` to push the scroll region down.
|
||||
// 4. Reset the scroll region to the old scroll region.
|
||||
|
||||
// Step 1 (from above)
|
||||
const prev_x = self.screen.cursor.x;
|
||||
self.screen.cursorAbsolute(prev_x, self.rows - 1);
|
||||
try self.screen.cursorDownScroll();
|
||||
|
||||
// Steps 2-4 (from above)
|
||||
const old_top = self.scrolling_region.top;
|
||||
self.scrolling_region.top = self.scrolling_region.bottom;
|
||||
self.scrolling_region.bottom = self.rows - 1;
|
||||
self.screen.cursorAbsolute(prev_x, self.scrolling_region.top);
|
||||
self.insertLines(1);
|
||||
self.scrolling_region.bottom = self.scrolling_region.top;
|
||||
self.scrolling_region.top = old_top;
|
||||
self.screen.cursorAbsolute(prev_x, self.scrolling_region.bottom);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path for left and right scrolling region margins.
|
||||
if (self.scrolling_region.left != 0 or
|
||||
self.scrolling_region.right != self.cols - 1 or
|
||||
@ -1164,7 +1200,6 @@ pub fn index(self: *Terminal) !void {
|
||||
self.screen.cursor.style = .{};
|
||||
self.screen.manualStyleUpdate() catch unreachable;
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -6438,7 +6473,7 @@ test "Terminal: index bottom of scroll region with hyperlinks" {
|
||||
|
||||
test "Terminal: index bottom of scroll region clear hyperlinks" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try init(alloc, .{ .rows = 5, .cols = 5 });
|
||||
var t = try init(alloc, .{ .rows = 5, .cols = 5, .max_scrollback = 0 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
t.setTopAndBottomMargin(1, 2);
|
||||
@ -6597,11 +6632,31 @@ test "Terminal: index inside left/right margin" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: index bottom of scroll region" {
|
||||
test "Terminal: index bottom of scroll region creates scrollback" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try init(alloc, .{ .rows = 5, .cols = 5 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
t.setTopAndBottomMargin(1, 3);
|
||||
try t.printString("1\n2\n3");
|
||||
t.setCursorPos(4, 1);
|
||||
try t.print('X');
|
||||
t.setCursorPos(3, 1);
|
||||
try t.index();
|
||||
try t.print('Y');
|
||||
|
||||
{
|
||||
const str = try t.screen.dumpStringAlloc(alloc, .{ .screen = .{} });
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("1\n2\n3\nY\nX", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: index bottom of scroll region no scrollback" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try init(alloc, .{ .rows = 5, .cols = 5, .max_scrollback = 0 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
t.setTopAndBottomMargin(1, 3);
|
||||
t.setCursorPos(4, 1);
|
||||
try t.print('B');
|
||||
@ -6614,7 +6669,6 @@ test "Terminal: index bottom of scroll region" {
|
||||
try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 0 } }));
|
||||
try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 1 } }));
|
||||
try testing.expect(t.isDirty(.{ .active = .{ .x = 0, .y = 2 } }));
|
||||
try testing.expect(!t.isDirty(.{ .active = .{ .x = 0, .y = 3 } }));
|
||||
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
|
Reference in New Issue
Block a user