@@ -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() {