terminal: SU, fix DL bug

This commit is contained in:
Mitchell Hashimoto
2023-10-10 13:00:00 -07:00
parent 7a8f2bfed6
commit 3cc0cbcc9d
3 changed files with 229 additions and 13 deletions

View File

@ -2887,10 +2887,6 @@ pub fn dumpString(self: *Screen, writer: anytype, opts: Dump) !void {
// Handle blank rows
if (row.isEmpty()) {
// Blank rows should never have wrap set. A blank row doesn't
// include explicit spaces so there should never be a scenario
// it's wrapped.
assert(!row.header().flags.wrap);
blank_rows += 1;
continue;
}

View File

@ -1707,8 +1707,8 @@ pub fn deleteLines(self: *Terminal, count: usize) !void {
// The amount of lines we need to scroll up.
const scroll_amount = rem - count;
const scroll_top = self.scrolling_region.bottom - scroll_amount;
for (self.screen.cursor.y..scroll_top + 1) |y| {
const scroll_end_y = self.screen.cursor.y + scroll_amount;
for (self.screen.cursor.y..scroll_end_y) |y| {
const src = self.screen.getRow(.{ .active = y + count });
const dst = self.screen.getRow(.{ .active = y });
for (self.scrolling_region.left..self.scrolling_region.right + 1) |x| {
@ -1717,7 +1717,7 @@ pub fn deleteLines(self: *Terminal, count: usize) !void {
}
// Insert blank lines
for (scroll_top + 1..self.scrolling_region.bottom + 1) |y| {
for (scroll_end_y..self.scrolling_region.bottom + 1) |y| {
const row = self.screen.getRow(.{ .active = y });
row.fillSlice(.{
.bg = self.screen.cursor.pen.bg,
@ -1748,13 +1748,18 @@ pub fn scrollDown(self: *Terminal, count: usize) !void {
/// The new lines are created according to the current SGR state.
///
/// Does not change the (absolute) cursor position.
// TODO: test
pub fn scrollUp(self: *Terminal, count: usize) !void {
self.screen.scrollRegionUp(
.{ .active = self.scrolling_region.top },
.{ .active = self.scrolling_region.bottom },
count,
);
const tracy = trace(@src());
defer tracy.end();
// Preserve the cursor
const cursor = self.screen.cursor;
defer self.screen.cursor = cursor;
// Move to the top of the scroll region
self.screen.cursor.y = self.scrolling_region.top;
self.screen.cursor.x = self.scrolling_region.left;
try self.deleteLines(count);
}
/// Options for scrolling the viewport of the terminal grid.
@ -2978,6 +2983,30 @@ test "Terminal: deleteLines left/right scroll region" {
}
}
test "Terminal: deleteLines left/right scroll region from top" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
defer t.deinit(alloc);
try t.printString("ABC123");
t.carriageReturn();
try t.linefeed();
try t.printString("DEF456");
t.carriageReturn();
try t.linefeed();
try t.printString("GHI789");
t.scrolling_region.left = 1;
t.scrolling_region.right = 3;
t.setCursorPos(1, 2);
try t.deleteLines(1);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("AEF423\nDHI756\nG 89", str);
}
}
test "Terminal: deleteLines left/right scroll region high count" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
@ -5728,6 +5757,100 @@ test "Terminal: scrollDown outside of left/right scroll region" {
}
}
test "Terminal: scrollUp simple" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
try t.printString("ABC");
t.carriageReturn();
try t.linefeed();
try t.printString("DEF");
t.carriageReturn();
try t.linefeed();
try t.printString("GHI");
t.setCursorPos(2, 2);
const cursor = t.screen.cursor;
try t.scrollUp(1);
try testing.expectEqual(cursor, t.screen.cursor);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("DEF\nGHI", str);
}
}
test "Terminal: scrollUp top/bottom scroll region" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
try t.printString("ABC");
t.carriageReturn();
try t.linefeed();
try t.printString("DEF");
t.carriageReturn();
try t.linefeed();
try t.printString("GHI");
t.setTopAndBottomMargin(2, 3);
t.setCursorPos(1, 1);
try t.scrollUp(1);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("ABC\nGHI", str);
}
}
test "Terminal: scrollUp left/right scroll region" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
defer t.deinit(alloc);
try t.printString("ABC123");
t.carriageReturn();
try t.linefeed();
try t.printString("DEF456");
t.carriageReturn();
try t.linefeed();
try t.printString("GHI789");
t.scrolling_region.left = 1;
t.scrolling_region.right = 3;
t.setCursorPos(2, 2);
const cursor = t.screen.cursor;
try t.scrollUp(1);
try testing.expectEqual(cursor, t.screen.cursor);
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("AEF423\nDHI756\nG 89", str);
}
}
test "Terminal: scrollUp preserves pending wrap" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
t.setCursorPos(1, 5);
try t.print('A');
t.setCursorPos(2, 5);
try t.print('B');
t.setCursorPos(3, 5);
try t.print('C');
try t.scrollUp(1);
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings(" B\n C\n\nX", str);
}
}
test "Terminal: tabClear single" {
const alloc = testing.allocator;
var t = try init(alloc, 30, 5);

View File

@ -0,0 +1,97 @@
import VTSequence from "@/components/VTSequence";
# Scroll Up (SU)
<VTSequence sequence={["CSI", "Pn", "S"]} />
Remove `n` lines from the top of the scroll region and shift existing
lines up.
The parameter `n` must be an integer greater than or equal to 1. If `n` is less than
or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1.
This sequence executes [Delete Line (DL)](/vt/dl) with the cursor position
set to the top of the scroll region. There are some differences from DL
which are explained below.
The cursor position after the operation must be unchanged from when SU was
invoked. The pending wrap state is _not_ reset.
## Validation
### SU V-1: Simple Usage
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "ABC\n"
printf "DEF\n"
printf "GHI\n"
printf "\033[2;2H"
printf "\033[S"
```
```
|DEF_____|
|GHI_____|
```
### SU V-2: Top/Bottom Scroll Region
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "ABC\n"
printf "DEF\n"
printf "GHI\n"
printf "\033[2;3r" # scroll region top/bottom
printf "\033[1;1H"
printf "\033[S"
```
```
|ABC_____|
|GHI_____|
```
### SU V-3: Left/Right Scroll Regions
```bash
printf "\033[1;1H" # move to top-left
printf "\033[0J" # clear screen
printf "ABC123\n"
printf "DEF456\n"
printf "GHI789\n"
printf "\033[?69h" # enable left/right margins
printf "\033[2;4s" # scroll region left/right
printf "\033[2;2H"
printf "\033[S"
```
```
|AEF423__|
|DHI756__|
|G___89__|
```
### SU V-4: Preserves Pending Wrap
```bash
cols=$(tput cols)
printf "\033[1;${cols}H" # move to top-right
printf "\033[2J" # clear screen
printf "A"
printf "\033[2;${cols}H"
printf "B"
printf "\033[3;${cols}H"
printf "C"
printf "\033[S"
printf "X"
```
```
|_______B|
|_______C|
|________|
|X_______|
```