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:
Lucas Vieira
2025-08-18 09:57:53 -06:00
committed by GitHub
co-authored by Conrad Irwin
parent e1d31cfcc3
commit 768b2de368
7 changed files with 365 additions and 4 deletions
+22
View File
@@ -2,6 +2,7 @@ use crate::{
Vim,
motion::{Motion, MotionKind},
object::Object,
state::Mode,
};
use collections::{HashMap, HashSet};
use editor::{
@@ -102,8 +103,20 @@ impl Vim {
// Emulates behavior in vim where if we expanded backwards to include a newline
// the cursor gets set back to the start of the line
let mut should_move_to_start: HashSet<_> = Default::default();
// Emulates behavior in vim where after deletion the cursor should try to move
// to the same column it was before deletion if the line is not empty or only
// contains whitespace
let mut column_before_move: HashMap<_, _> = Default::default();
let target_mode = object.target_visual_mode(vim.mode, around);
editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(|map, selection| {
let cursor_point = selection.head().to_point(map);
if target_mode == Mode::VisualLine {
column_before_move.insert(selection.id, cursor_point.column);
}
object.expand_selection(map, selection, around, times);
let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
let mut move_selection_start_to_previous_line =
@@ -164,6 +177,15 @@ impl Vim {
let mut cursor = selection.head();
if should_move_to_start.contains(&selection.id) {
*cursor.column_mut() = 0;
} else if let Some(column) = column_before_move.get(&selection.id)
&& *column > 0
{
let mut cursor_point = cursor.to_point(map);
cursor_point.column = *column;
cursor = map
.buffer_snapshot
.clip_point(cursor_point, Bias::Left)
.to_display_point(map);
}
cursor = map.clip_point(cursor, Bias::Left);
selection.collapse_to(cursor, selection.goal)