diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 1cbcf82aa..600ca99d1 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1449,16 +1449,21 @@ pub fn cursorRight(self: *Terminal, count: usize) void { /// Move the cursor down amount lines. If amount is greater than the maximum /// move distance then it is internally adjusted to the maximum. This sequence /// will not scroll the screen or scroll region. If amount is 0, adjust it to 1. -// TODO: test -pub fn cursorDown(self: *Terminal, count: usize) void { +pub fn cursorDown(self: *Terminal, count_req: usize) void { const tracy = trace(@src()); defer tracy.end(); + // Always resets pending wrap self.screen.cursor.pending_wrap = false; - self.screen.cursor.y += if (count == 0) 1 else count; - if (self.screen.cursor.y >= self.rows) { - self.screen.cursor.y = self.rows - 1; - } + + // The max the cursor can move to depends where the cursor currently is + const max = if (self.screen.cursor.y <= self.scrolling_region.bottom) + self.scrolling_region.bottom + else + self.rows - 1; + + const count = @max(count_req, 1); + self.screen.cursor.y = @min(max, self.screen.cursor.y +| count); } /// Move the cursor up amount lines. If amount is greater than the maximum @@ -3561,3 +3566,72 @@ test "Terminal: cursorLeft extended reverse wrap is priority if both set" { try testing.expectEqualStrings("ABCDE\n1\n X", str); } } + +test "Terminal: cursorDown basic" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + try t.print('A'); + t.cursorDown(10); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\n\n\n\n X", str); + } +} + +test "Terminal: cursorDown above bottom scroll margin" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setScrollingRegion(1, 3); + try t.print('A'); + t.cursorDown(10); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\n\n X", str); + } +} + +test "Terminal: cursorDown below bottom scroll margin" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + t.setScrollingRegion(1, 3); + try t.print('A'); + t.setCursorPos(4, 1); + t.cursorDown(10); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("A\n\n\n\nX", str); + } +} + +test "Terminal: cursorDown resets wrap" { + const alloc = testing.allocator; + var t = try init(alloc, 5, 5); + defer t.deinit(alloc); + + for ("ABCDE") |c| try t.print(c); + try testing.expect(t.screen.cursor.pending_wrap); + t.cursorDown(1); + try testing.expect(!t.screen.cursor.pending_wrap); + try t.print('X'); + + { + var str = try t.plainString(testing.allocator); + defer testing.allocator.free(str); + try testing.expectEqualStrings("ABCDE\n X", str); + } +}