vim: Apply strict paragraph motion only in vim mode (#48024)
Reverts the editor's paragraph navigation behavior that was changed in #47734. Whitespace-only lines are now treated as paragraph boundaries again for non-vim mode users. Vim mode retains its own implementation where only truly empty lines are paragraph boundaries. Release Notes: - Fixed editor paragraph navigation to treat whitespace-only lines as paragraph boundaries again --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: dino <dinojoaocosta@gmail.com>
This commit is contained in:
1 parent
060715704d
commit
4eb6eebf3a
3 files changed
+86
-93
No files matched your search
@@ -2243,107 +2243,41 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext)
|
||||
});
|
||||
cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
|
||||
|
||||
cx.set_state(
|
||||
&r#"ˇone
|
||||
two
|
||||
|
||||
three
|
||||
fourˇ
|
||||
five
|
||||
|
||||
six"#
|
||||
.unindent(),
|
||||
);
|
||||
// The third line only contains a single space so we can later assert that the
|
||||
// editor's paragraph movement considers a non-blank line as a paragraph
|
||||
// boundary.
|
||||
cx.set_state(&"ˇone\ntwo\n \nthree\nfourˇ\nfive\n\nsix");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"one
|
||||
two
|
||||
ˇ
|
||||
three
|
||||
four
|
||||
five
|
||||
ˇ
|
||||
six"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"one\ntwo\nˇ \nthree\nfour\nfive\nˇ\nsix");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"one
|
||||
two
|
||||
|
||||
three
|
||||
four
|
||||
five
|
||||
ˇ
|
||||
sixˇ"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"one\ntwo\n \nthree\nfour\nfive\nˇ\nsixˇ");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"one
|
||||
two
|
||||
|
||||
three
|
||||
four
|
||||
five
|
||||
|
||||
sixˇ"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"one\ntwo\n \nthree\nfour\nfive\n\nsixˇ");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"one
|
||||
two
|
||||
|
||||
three
|
||||
four
|
||||
five
|
||||
ˇ
|
||||
six"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"one\ntwo\n \nthree\nfour\nfive\nˇ\nsix");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"one
|
||||
two
|
||||
ˇ
|
||||
three
|
||||
four
|
||||
five
|
||||
|
||||
six"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"one\ntwo\nˇ \nthree\nfour\nfive\n\nsix");
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&r#"ˇone
|
||||
two
|
||||
|
||||
three
|
||||
four
|
||||
five
|
||||
|
||||
six"#
|
||||
.unindent(),
|
||||
);
|
||||
cx.assert_editor_state(&"ˇone\ntwo\n \nthree\nfour\nfive\n\nsix");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
@@ -523,7 +523,7 @@ fn is_subword_boundary_end(left: char, right: char, classifier: &CharClassifier)
|
||||
}
|
||||
|
||||
/// Returns a position of the start of the current paragraph, where a paragraph
|
||||
/// is defined as a run of non-empty lines.
|
||||
/// is defined as a run of non-blank lines.
|
||||
pub fn start_of_paragraph(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
@@ -534,25 +534,25 @@ pub fn start_of_paragraph(
|
||||
return DisplayPoint::zero();
|
||||
}
|
||||
|
||||
let mut found_non_empty_line = false;
|
||||
let mut found_non_blank_line = false;
|
||||
for row in (0..point.row + 1).rev() {
|
||||
let empty = map.buffer_snapshot().line_len(MultiBufferRow(row)) == 0;
|
||||
if found_non_empty_line && empty {
|
||||
let blank = map.buffer_snapshot().is_line_blank(MultiBufferRow(row));
|
||||
if found_non_blank_line && blank {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
}
|
||||
count -= 1;
|
||||
found_non_empty_line = false;
|
||||
found_non_blank_line = false;
|
||||
}
|
||||
|
||||
found_non_empty_line |= !empty;
|
||||
found_non_blank_line |= !blank;
|
||||
}
|
||||
|
||||
DisplayPoint::zero()
|
||||
}
|
||||
|
||||
/// Returns a position of the end of the current paragraph, where a paragraph
|
||||
/// is defined as a run of non-empty lines.
|
||||
/// is defined as a run of non-blank lines.
|
||||
pub fn end_of_paragraph(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
@@ -563,18 +563,18 @@ pub fn end_of_paragraph(
|
||||
return map.max_point();
|
||||
}
|
||||
|
||||
let mut found_non_empty_line = false;
|
||||
let mut found_non_blank_line = false;
|
||||
for row in point.row..=map.buffer_snapshot().max_row().0 {
|
||||
let empty = map.buffer_snapshot().line_len(MultiBufferRow(row)) == 0;
|
||||
if found_non_empty_line && empty {
|
||||
let blank = map.buffer_snapshot().is_line_blank(MultiBufferRow(row));
|
||||
if found_non_blank_line && blank {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
}
|
||||
count -= 1;
|
||||
found_non_empty_line = false;
|
||||
found_non_blank_line = false;
|
||||
}
|
||||
|
||||
found_non_empty_line |= !empty;
|
||||
found_non_blank_line |= !blank;
|
||||
}
|
||||
|
||||
map.max_point()
|
||||
|
||||
@@ -1038,12 +1038,9 @@ impl Motion {
|
||||
),
|
||||
SentenceBackward => (sentence_backwards(map, point, times), SelectionGoal::None),
|
||||
SentenceForward => (sentence_forwards(map, point, times), SelectionGoal::None),
|
||||
StartOfParagraph => (
|
||||
movement::start_of_paragraph(map, point, times),
|
||||
SelectionGoal::None,
|
||||
),
|
||||
StartOfParagraph => (start_of_paragraph(map, point, times), SelectionGoal::None),
|
||||
EndOfParagraph => (
|
||||
map.clip_at_line_end(movement::end_of_paragraph(map, point, times)),
|
||||
map.clip_at_line_end(end_of_paragraph(map, point, times)),
|
||||
SelectionGoal::None,
|
||||
),
|
||||
CurrentLine => (next_line_end(map, point, times), SelectionGoal::None),
|
||||
@@ -2238,6 +2235,68 @@ pub(crate) fn sentence_forwards(
|
||||
map.max_point()
|
||||
}
|
||||
|
||||
/// Returns a position of the start of the current paragraph for vim motions,
|
||||
/// where a paragraph is defined as a run of non-empty lines. Lines containing
|
||||
/// only whitespace are not considered empty and do not act as paragraph
|
||||
/// boundaries.
|
||||
pub(crate) fn start_of_paragraph(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
mut count: usize,
|
||||
) -> DisplayPoint {
|
||||
let point = display_point.to_point(map);
|
||||
if point.row == 0 {
|
||||
return DisplayPoint::zero();
|
||||
}
|
||||
|
||||
let mut found_non_empty_line = false;
|
||||
for row in (0..point.row + 1).rev() {
|
||||
let empty = map.buffer_snapshot().line_len(MultiBufferRow(row)) == 0;
|
||||
if found_non_empty_line && empty {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
}
|
||||
count -= 1;
|
||||
found_non_empty_line = false;
|
||||
}
|
||||
|
||||
found_non_empty_line |= !empty;
|
||||
}
|
||||
|
||||
DisplayPoint::zero()
|
||||
}
|
||||
|
||||
/// Returns a position of the end of the current paragraph for vim motions,
|
||||
/// where a paragraph is defined as a run of non-empty lines. Lines containing
|
||||
/// only whitespace are not considered empty and do not act as paragraph
|
||||
/// boundaries.
|
||||
pub(crate) fn end_of_paragraph(
|
||||
map: &DisplaySnapshot,
|
||||
display_point: DisplayPoint,
|
||||
mut count: usize,
|
||||
) -> DisplayPoint {
|
||||
let point = display_point.to_point(map);
|
||||
if point.row == map.buffer_snapshot().max_row().0 {
|
||||
return map.max_point();
|
||||
}
|
||||
|
||||
let mut found_non_empty_line = false;
|
||||
for row in point.row..=map.buffer_snapshot().max_row().0 {
|
||||
let empty = map.buffer_snapshot().line_len(MultiBufferRow(row)) == 0;
|
||||
if found_non_empty_line && empty {
|
||||
if count <= 1 {
|
||||
return Point::new(row, 0).to_display_point(map);
|
||||
}
|
||||
count -= 1;
|
||||
found_non_empty_line = false;
|
||||
}
|
||||
|
||||
found_non_empty_line |= !empty;
|
||||
}
|
||||
|
||||
map.max_point()
|
||||
}
|
||||
|
||||
fn next_non_blank(map: &DisplaySnapshot, start: MultiBufferOffset) -> MultiBufferOffset {
|
||||
for (c, o) in map.buffer_chars_at(start) {
|
||||
if c == '\n' || !c.is_whitespace() {
|
||||
|
||||
Reference in new issue
Block a user