vim: Fix ap text object selection when there is line wrapping (#35485)
In Vim mode, `ap` text object (used in `vap`, `dap`, `cap`) was selecting multiple paragraphs when soft wrap was enabled. The bug was caused by using DisplayRow coordinates for arithmetic instead of buffer row coordinates in the paragraph boundary calculation. Fix by converting to buffer coordinates before arithmetic, then back to display coordinates for the final result. Closes #35085 --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
co-authored by
Conrad Irwin
parent
e1d31cfcc3
commit
768b2de368
@@ -1444,14 +1444,15 @@ fn paragraph(
|
||||
return None;
|
||||
}
|
||||
|
||||
let paragraph_start_row = paragraph_start.row();
|
||||
if paragraph_start_row.0 != 0 {
|
||||
let paragraph_start_buffer_point = paragraph_start.to_point(map);
|
||||
if paragraph_start_buffer_point.row != 0 {
|
||||
let previous_paragraph_last_line_start =
|
||||
Point::new(paragraph_start_row.0 - 1, 0).to_display_point(map);
|
||||
Point::new(paragraph_start_buffer_point.row - 1, 0).to_display_point(map);
|
||||
paragraph_start = start_of_paragraph(map, previous_paragraph_last_line_start);
|
||||
}
|
||||
} else {
|
||||
let mut start_row = paragraph_end_row.0 + 1;
|
||||
let paragraph_end_buffer_point = paragraph_end.to_point(map);
|
||||
let mut start_row = paragraph_end_buffer_point.row + 1;
|
||||
if i > 0 {
|
||||
start_row += 1;
|
||||
}
|
||||
@@ -1903,6 +1904,90 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_change_paragraph_object_with_soft_wrap(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
const WRAPPING_EXAMPLE: &str = indoc! {"
|
||||
ˇFirst paragraph with very long text that will wrap when soft wrap is enabled and line length is ˇlimited making it span multiple display lines.
|
||||
|
||||
ˇSecond paragraph that is also quite long and will definitely wrap under soft wrap conditions and ˇshould be handled correctly.
|
||||
|
||||
ˇThird paragraph with additional long text content that will also wrap when line length is constrained by the wrapping ˇsettings.ˇ
|
||||
"};
|
||||
|
||||
cx.set_shared_wrap(20).await;
|
||||
|
||||
cx.simulate_at_each_offset("c i p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
cx.simulate_at_each_offset("c a p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_paragraph_object_with_soft_wrap(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
const WRAPPING_EXAMPLE: &str = indoc! {"
|
||||
ˇFirst paragraph with very long text that will wrap when soft wrap is enabled and line length is ˇlimited making it span multiple display lines.
|
||||
|
||||
ˇSecond paragraph that is also quite long and will definitely wrap under soft wrap conditions and ˇshould be handled correctly.
|
||||
|
||||
ˇThird paragraph with additional long text content that will also wrap when line length is constrained by the wrapping ˇsettings.ˇ
|
||||
"};
|
||||
|
||||
cx.set_shared_wrap(20).await;
|
||||
|
||||
cx.simulate_at_each_offset("d i p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
cx.simulate_at_each_offset("d a p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_paragraph_whitespace(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
a
|
||||
ˇ•
|
||||
aaaaaaaaaaaaa
|
||||
"})
|
||||
.await;
|
||||
|
||||
cx.simulate_shared_keystrokes("d i p").await;
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
a
|
||||
aaaaaaaˇaaaaaa
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_visual_paragraph_object_with_soft_wrap(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
const WRAPPING_EXAMPLE: &str = indoc! {"
|
||||
ˇFirst paragraph with very long text that will wrap when soft wrap is enabled and line length is ˇlimited making it span multiple display lines.
|
||||
|
||||
ˇSecond paragraph that is also quite long and will definitely wrap under soft wrap conditions and ˇshould be handled correctly.
|
||||
|
||||
ˇThird paragraph with additional long text content that will also wrap when line length is constrained by the wrapping ˇsettings.ˇ
|
||||
"};
|
||||
|
||||
cx.set_shared_wrap(20).await;
|
||||
|
||||
cx.simulate_at_each_offset("v i p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
cx.simulate_at_each_offset("v a p", WRAPPING_EXAMPLE)
|
||||
.await
|
||||
.assert_matches();
|
||||
}
|
||||
|
||||
// Test string with "`" for opening surrounders and "'" for closing surrounders
|
||||
const SURROUNDING_MARKER_STRING: &str = indoc! {"
|
||||
ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn`
|
||||
|
||||
Reference in New Issue
Block a user