1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-26 23:04:49 +03:00

term: fixup cursor position during rewrap

refs: https://github.com/wez/wezterm/issues/2162
This commit is contained in:
Wez Furlong 2022-07-24 22:32:31 -07:00
parent 8eb44b43b3
commit 4bf89e0c10
4 changed files with 184 additions and 5 deletions

View File

@ -105,7 +105,7 @@ impl Screen {
let mut rewrapped = VecDeque::new();
let mut logical_line: Option<Line> = None;
let mut logical_cursor_x: Option<usize> = None;
let mut adjusted_cursor = (cursor_y, cursor_y);
let mut adjusted_cursor = (cursor_x, cursor_y);
for (phys_idx, mut line) in self.lines.drain(..).enumerate() {
line.update_last_change_seqno(seqno);
@ -140,6 +140,25 @@ impl Screen {
let num_lines = x / physical_cols;
let last_x = x - (num_lines * physical_cols);
adjusted_cursor = (last_x, rewrapped.len() + num_lines);
// Special case: if the cursor lands in column zero, we'll
// lose track of its logical association with the wrapped
// line and it won't resize with the line correctly.
// Put it back on the prior line. The cursor is now
// technically outside of the viewport width.
if adjusted_cursor.0 == 0 && adjusted_cursor.1 > 0 {
if physical_cols < self.physical_cols {
// getting smaller: preserve its original position
// on the prior line
adjusted_cursor.0 = cursor_x;
} else {
// getting larger; we were most likely in column 1
// or somewhere close. Jump to the end of the
// prior line.
adjusted_cursor.0 = physical_cols;
}
adjusted_cursor.1 -= 1;
}
}
if line.len() <= physical_cols {

View File

@ -914,7 +914,11 @@ impl TerminalState {
if self.dec_origin_mode {
self.left_and_right_margins.end
} else {
self.screen().physical_cols
// We allow 1 extra for the cursor x position
// to account for some resize/rewrap scenarios
// where we don't want to forget that the
// cursor belongs to a wrapped line
self.screen().physical_cols + 1
} as i64
- 1,
)

View File

@ -298,7 +298,7 @@ fn test_cup() {
term.cup(-1, -1);
term.assert_cursor_pos(0, 0, None, None);
term.cup(500, 500);
term.assert_cursor_pos(3, 2, None, None);
term.assert_cursor_pos(4, 2, None, None);
}
#[test]
@ -313,7 +313,7 @@ fn test_hvp() {
term.hvp(-1, -1);
term.assert_cursor_pos(0, 0, None, None);
term.hvp(500, 500);
term.assert_cursor_pos(3, 2, None, None);
term.assert_cursor_pos(4, 2, None, None);
}
#[test]
@ -354,7 +354,7 @@ fn test_cha() {
term.assert_cursor_pos(0, 1, None, Some(seqno));
term.print("\x1b[100G");
term.assert_cursor_pos(3, 1, None, None);
term.assert_cursor_pos(4, 1, None, None);
}
#[test]

View File

@ -756,6 +756,162 @@ fn test_dec_double_width() {
assert!(lines[3].is_single_width());
}
/// This test skips over an edge case with cursor positioning,
/// while sizing down, but tries to trip over the same edge
/// case while sizing back up again
#[test]
fn test_resize_2162_by_2_then_up_1() {
let num_lines = 4;
let num_cols = 20;
let mut term = TestTerm::new(num_lines, num_cols, 0);
term.print("some long long text");
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
term.assert_cursor_pos(19, 0, None, Some(0));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols - 2,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long tex", "t", "", ""],
);
eprintln!("check cursor pos 2");
term.assert_cursor_pos(1, 1, None, Some(5));
term.resize(TerminalSize {
rows: num_lines - 1,
cols: num_cols,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(&term, file!(), line!(), &["some long long text", "", ""]);
eprintln!("check cursor pos 3");
term.assert_cursor_pos(19, 0, None, Some(5));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
eprintln!("check cursor pos 3");
term.assert_cursor_pos(19, 0, None, Some(5));
}
/// This test skips over an edge case with cursor positioning,
/// so it passes even ahead of a fix for issue 2162.
#[test]
fn test_resize_2162_by_2() {
let num_lines = 4;
let num_cols = 20;
let mut term = TestTerm::new(num_lines, num_cols, 0);
term.print("some long long text");
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
term.assert_cursor_pos(19, 0, None, Some(0));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols - 2,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long tex", "t", "", ""],
);
eprintln!("check cursor pos 2");
term.assert_cursor_pos(1, 1, None, Some(5));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
eprintln!("check cursor pos 3");
term.assert_cursor_pos(19, 0, None, Some(5));
}
/// This case tickles an edge case where the cursor ends
/// up drifting away from where the line wraps and ends up
/// in the wrong place
#[test]
fn test_resize_2162() {
let num_lines = 4;
let num_cols = 20;
let mut term = TestTerm::new(num_lines, num_cols, 0);
term.print("some long long text");
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
term.assert_cursor_pos(19, 0, None, Some(0));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols - 1,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
eprintln!("check cursor pos 2");
term.assert_cursor_pos(19, 0, None, Some(5));
term.resize(TerminalSize {
rows: num_lines,
cols: num_cols,
pixel_width: 0,
pixel_height: 0,
dpi: 0,
});
assert_visible_contents(
&term,
file!(),
line!(),
&["some long long text", "", "", ""],
);
eprintln!("check cursor pos 3");
term.assert_cursor_pos(19, 0, None, Some(5));
}
/// Test the behavior of wrapped lines when we resize the terminal
/// wider and then narrower.
#[test]