vim: Fix hang in visual block motion (#40723)

Dino and Piotr Osiewicz created

The `vim::visual::Vim.visual_block_motion` method was recently updated
(https://github.com/zed-industries/zed/pull/39355) in order to jump
between buffer rows instead of display rows. However, with this now
being the case, the `break` condition was never met when the motion was
horizontal rather than vertical and soft wrapped lines were used. As
such, this commit udpates the condition to ensure it's always reached,
preventing the hanging from happening.

Release Notes:

- Fixed hang in Vim's visual block motions when updating selections

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>

Change summary

crates/vim/src/visual.rs | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)

Detailed changes

crates/vim/src/visual.rs 🔗

@@ -366,6 +366,8 @@ impl Vim {
 
             let mut selections = Vec::new();
             let mut row = tail.row();
+            let going_up = tail.row() > head.row();
+            let direction = if going_up { -1 } else { 1 };
 
             loop {
                 let laid_out_line = map.layout_row(row, &text_layout_details);
@@ -396,13 +398,18 @@ impl Vim {
 
                     selections.push(selection);
                 }
-                if row == head.row() {
+
+                // When dealing with soft wrapped lines, it's possible that
+                // `row` ends up being set to a value other than `head.row()` as
+                // `head.row()` might be a `DisplayPoint` mapped to a soft
+                // wrapped line, hence the need for `<=` and `>=` instead of
+                // `==`.
+                if going_up && row <= head.row() || !going_up && row >= head.row() {
                     break;
                 }
 
-                // Move to the next or previous buffer row, ensuring that
-                // wrapped lines are handled correctly.
-                let direction = if tail.row() > head.row() { -1 } else { 1 };
+                // Find the next or previous buffer row where the `row` should
+                // be moved to, so that wrapped lines are skipped.
                 row = map
                     .start_of_relative_buffer_row(DisplayPoint::new(row, 0), direction)
                     .row();