vim: { } to navigate by paragraph (#2668)

As part of this I added `assert_shared_state()` to the
NeovimBackedTestContext so that it is more like a drop-in replacement
for the VimTestContext.

The remaining part of zed-industries/community#682 is adding bracket
matching to plain text. It looks like the current logic requires there
to be a tree sitter language for the language in order to support
bracket matching. I didn't fix this in this PR because I was unsure
whether to try and work around that, or to try and add a plain text tree
sitter language.

Release Notes:

- vim: support `{` and `}` for paragraph motion
([#470](https://github.com/zed-industries/community/issues/470)).
- vim: fix `%` at the end of the line
([#682](https://github.com/zed-industries/community/issues/682)).
This commit is contained in:
Conrad Irwin 2023-07-07 14:59:06 -06:00 committed by GitHub
commit 460bf93866
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 377 additions and 60 deletions

View file

@ -5123,7 +5123,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::start_of_paragraph(map, selection.head()),
movement::start_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@ -5143,7 +5143,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::end_of_paragraph(map, selection.head()),
movement::end_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@ -5162,7 +5162,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::start_of_paragraph(map, head), SelectionGoal::None)
(
movement::start_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}
@ -5179,7 +5182,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::end_of_paragraph(map, head), SelectionGoal::None)
(
movement::end_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}

View file

@ -193,7 +193,11 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
})
}
pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub 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 map.max_point();
@ -203,7 +207,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
for row in (0..point.row + 1).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;
@ -212,7 +220,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
DisplayPoint::zero()
}
pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub fn end_of_paragraph(
map: &DisplaySnapshot,
display_point: DisplayPoint,
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
return DisplayPoint::zero();
@ -222,7 +234,11 @@ pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> D
for row in point.row..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;

View file

@ -210,6 +210,10 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, marked_text.to_string())
}
pub fn editor_state(&mut self) -> String {
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
}
#[track_caller]
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
let expected_ranges = self.ranges(marked_text);
@ -248,14 +252,8 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, expected_marked_text)
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self
.editor
fn editor_selections(&self) -> Vec<Range<usize>> {
self.editor
.read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
.into_iter()
.map(|s| {
@ -265,12 +263,22 @@ impl<'a> EditorTestContext<'a> {
s.start..s.end
}
})
.collect::<Vec<_>>();
.collect::<Vec<_>>()
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self.editor_selections();
let actual_marked_text =
generate_marked_text(&self.buffer_text(), &actual_selections, true);
if expected_selections != actual_selections {
panic!(
indoc! {"
{}Editor has unexpected selections.
Expected selections: