From 3d4abde55afa6f01bc9f2451e49af3aeb40a89c9 Mon Sep 17 00:00:00 2001 From: Dino Date: Tue, 21 Oct 2025 12:23:00 +0100 Subject: [PATCH] vim: Fix hang in visual block motion (#40723) 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> --- crates/vim/src/visual.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index bce49eb7d4a3c21b00a8076f0474d9da591cc993..59555205d9862e51c2778eec1f321338fd5e7569 100644 --- a/crates/vim/src/visual.rs +++ b/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();