Skip to content

Commit 7a7efe0

Browse files
committed
Composer: tighten click-past-EOL test to assert visual row, add multi-line plain repro
1 parent 75ddc2c commit 7a7efe0

1 file changed

Lines changed: 45 additions & 8 deletions

File tree

crates/browser-use-tui/src/composer.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -964,27 +964,64 @@ mod tests {
964964

965965
// Click on row 1 ("second word") way past the visible end.
966966
assert!(composer.set_cursor_from_wrapped_position(4, 80, 1, 60));
967-
assert_eq!(
968-
composer.cursor(),
969-
"first line\nsecond word".chars().count()
970-
);
967+
assert_eq!(composer.cursor(), "first line\nsecond word".chars().count());
971968

972969
// Wrapped logical line: clicking past the end of visual row 0
973-
// must land at the end of that visual row, NOT at the end of the
974-
// whole logical line (which would be on a later visual row).
970+
// must land in a position that *renders* on visual row 0, not on
971+
// the next wrapped row. Position == row_end is the start of the
972+
// next wrapped row visually (no character separates wrapped rows),
973+
// so we clamp one position earlier on non-last visual rows so the
974+
// cursor stays on the clicked row.
975975
let mut composer = Composer::default();
976976
// width=8, prompt "> " takes 2 cols => content/wrap width = 6.
977977
// "abcdefghij" wraps into "abcdef" and "ghij".
978978
composer.set_input("abcdefghij".to_string());
979979
// Click on row 0 at the rightmost in-rect column (col 7, past 'f').
980980
assert!(composer.set_cursor_from_wrapped_position(4, 8, 0, 7));
981-
assert_eq!(composer.cursor(), "abcdef".chars().count());
981+
// cursor=5 (between 'e' and 'f') renders at (row=0, col=5) — last
982+
// cell of visual row 0. cursor=6 would render at (row=1, col=0)
983+
// because in soft-wrapped text, position == wrap_width has no
984+
// visual home on row 0.
985+
assert_eq!(composer.cursor(), 5);
986+
let (vrow, _vcol) = composer.cursor_visual_row_col_wrapped(6);
987+
assert_eq!(vrow, 0, "cursor must render on the clicked visual row");
982988
// Click on row 1 past the end of "ghij" still pins to end of row 1
983-
// (which is also the end of the logical line here).
989+
// (which is the LAST visual row of the logical line, so clamping
990+
// to line_len is fine — there's no next wrapped row to spill into).
984991
assert!(composer.set_cursor_from_wrapped_position(4, 8, 1, 7));
985992
assert_eq!(composer.cursor(), "abcdefghij".chars().count());
986993
}
987994

995+
#[test]
996+
fn click_past_end_of_plain_line_in_multiline_composer_stays_on_clicked_row() {
997+
// Magnus's repro: composer has multi-line text via Shift+Enter
998+
// (so logical lines separated by \n, each non-wrapped). Clicking
999+
// far past the visible end of a line must place the cursor at the
1000+
// end of that line AND render it on the clicked row, not on the
1001+
// next logical line.
1002+
let mut composer = Composer::default();
1003+
composer.set_input("first line\nsecond line\nthird line".to_string());
1004+
1005+
// Click row 0 at col 100 (way past "first line"'s 10 chars).
1006+
assert!(composer.set_cursor_from_wrapped_position(4, 80, 0, 100));
1007+
assert_eq!(
1008+
composer.cursor(),
1009+
"first line".chars().count(),
1010+
"cursor byte position should land at end of first line"
1011+
);
1012+
let (vrow, _) = composer.cursor_visual_row_col_wrapped(78);
1013+
assert_eq!(vrow, 0, "cursor must render on row 0, not the next line");
1014+
1015+
// Click row 1 at col 100.
1016+
assert!(composer.set_cursor_from_wrapped_position(4, 80, 1, 100));
1017+
assert_eq!(
1018+
composer.cursor(),
1019+
"first line\nsecond line".chars().count(),
1020+
);
1021+
let (vrow, _) = composer.cursor_visual_row_col_wrapped(78);
1022+
assert_eq!(vrow, 1, "cursor must render on row 1, not the next line");
1023+
}
1024+
9881025
#[test]
9891026
fn ctrl_w_keeps_whitespace_delimited_shell_behavior() {
9901027
let mut composer = Composer::default();

0 commit comments

Comments
 (0)