mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 05:11:44 +03:00
vim: Add }/{ for start/end of paragraph
Fixes: zed-industries/community#470
This commit is contained in:
parent
f6b64dc67a
commit
9ee2707d43
@ -37,6 +37,8 @@
|
|||||||
"$": "vim::EndOfLine",
|
"$": "vim::EndOfLine",
|
||||||
"shift-g": "vim::EndOfDocument",
|
"shift-g": "vim::EndOfDocument",
|
||||||
"w": "vim::NextWordStart",
|
"w": "vim::NextWordStart",
|
||||||
|
"{": "vim::StartOfParagraph",
|
||||||
|
"}": "vim::EndOfParagraph",
|
||||||
"shift-w": [
|
"shift-w": [
|
||||||
"vim::NextWordStart",
|
"vim::NextWordStart",
|
||||||
{
|
{
|
||||||
|
@ -5120,7 +5120,7 @@ impl Editor {
|
|||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_with(|map, selection| {
|
s.move_with(|map, selection| {
|
||||||
selection.collapse_to(
|
selection.collapse_to(
|
||||||
movement::start_of_paragraph(map, selection.head()),
|
movement::start_of_paragraph(map, selection.head(), 1),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -5140,7 +5140,7 @@ impl Editor {
|
|||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_with(|map, selection| {
|
s.move_with(|map, selection| {
|
||||||
selection.collapse_to(
|
selection.collapse_to(
|
||||||
movement::end_of_paragraph(map, selection.head()),
|
movement::end_of_paragraph(map, selection.head(), 1),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -5159,7 +5159,10 @@ impl Editor {
|
|||||||
|
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_heads_with(|map, head, _| {
|
s.move_heads_with(|map, head, _| {
|
||||||
(movement::start_of_paragraph(map, head), SelectionGoal::None)
|
(
|
||||||
|
movement::start_of_paragraph(map, head, 1),
|
||||||
|
SelectionGoal::None,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5176,7 +5179,10 @@ impl Editor {
|
|||||||
|
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.move_heads_with(|map, head, _| {
|
s.move_heads_with(|map, head, _| {
|
||||||
(movement::end_of_paragraph(map, head), SelectionGoal::None)
|
(
|
||||||
|
movement::end_of_paragraph(map, head, 1),
|
||||||
|
SelectionGoal::None,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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);
|
let point = display_point.to_point(map);
|
||||||
if point.row == 0 {
|
if point.row == 0 {
|
||||||
return map.max_point();
|
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() {
|
for row in (0..point.row + 1).rev() {
|
||||||
let blank = map.buffer_snapshot.is_line_blank(row);
|
let blank = map.buffer_snapshot.is_line_blank(row);
|
||||||
if found_non_blank_line && blank {
|
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;
|
found_non_blank_line |= !blank;
|
||||||
@ -212,7 +220,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
|
|||||||
DisplayPoint::zero()
|
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);
|
let point = display_point.to_point(map);
|
||||||
if point.row == map.max_buffer_row() {
|
if point.row == map.max_buffer_row() {
|
||||||
return DisplayPoint::zero();
|
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 {
|
for row in point.row..map.max_buffer_row() + 1 {
|
||||||
let blank = map.buffer_snapshot.is_line_blank(row);
|
let blank = map.buffer_snapshot.is_line_blank(row);
|
||||||
if found_non_blank_line && blank {
|
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;
|
found_non_blank_line |= !blank;
|
||||||
|
@ -31,6 +31,8 @@ pub enum Motion {
|
|||||||
CurrentLine,
|
CurrentLine,
|
||||||
StartOfLine,
|
StartOfLine,
|
||||||
EndOfLine,
|
EndOfLine,
|
||||||
|
StartOfParagraph,
|
||||||
|
EndOfParagraph,
|
||||||
StartOfDocument,
|
StartOfDocument,
|
||||||
EndOfDocument,
|
EndOfDocument,
|
||||||
Matching,
|
Matching,
|
||||||
@ -72,6 +74,8 @@ actions!(
|
|||||||
StartOfLine,
|
StartOfLine,
|
||||||
EndOfLine,
|
EndOfLine,
|
||||||
CurrentLine,
|
CurrentLine,
|
||||||
|
StartOfParagraph,
|
||||||
|
EndOfParagraph,
|
||||||
StartOfDocument,
|
StartOfDocument,
|
||||||
EndOfDocument,
|
EndOfDocument,
|
||||||
Matching,
|
Matching,
|
||||||
@ -92,6 +96,12 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
cx.add_action(|_: &mut Workspace, _: &StartOfLine, cx: _| motion(Motion::StartOfLine, cx));
|
cx.add_action(|_: &mut Workspace, _: &StartOfLine, cx: _| motion(Motion::StartOfLine, cx));
|
||||||
cx.add_action(|_: &mut Workspace, _: &EndOfLine, cx: _| motion(Motion::EndOfLine, cx));
|
cx.add_action(|_: &mut Workspace, _: &EndOfLine, cx: _| motion(Motion::EndOfLine, cx));
|
||||||
cx.add_action(|_: &mut Workspace, _: &CurrentLine, cx: _| motion(Motion::CurrentLine, cx));
|
cx.add_action(|_: &mut Workspace, _: &CurrentLine, cx: _| motion(Motion::CurrentLine, cx));
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| {
|
||||||
|
motion(Motion::StartOfParagraph, cx)
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| {
|
||||||
|
motion(Motion::EndOfParagraph, cx)
|
||||||
|
});
|
||||||
cx.add_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
|
cx.add_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
|
||||||
motion(Motion::StartOfDocument, cx)
|
motion(Motion::StartOfDocument, cx)
|
||||||
});
|
});
|
||||||
@ -142,7 +152,8 @@ impl Motion {
|
|||||||
pub fn linewise(&self) -> bool {
|
pub fn linewise(&self) -> bool {
|
||||||
use Motion::*;
|
use Motion::*;
|
||||||
match self {
|
match self {
|
||||||
Down | Up | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart => true,
|
Down | Up | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart
|
||||||
|
| StartOfParagraph | EndOfParagraph => true,
|
||||||
EndOfLine
|
EndOfLine
|
||||||
| NextWordEnd { .. }
|
| NextWordEnd { .. }
|
||||||
| Matching
|
| Matching
|
||||||
@ -172,6 +183,8 @@ impl Motion {
|
|||||||
| Backspace
|
| Backspace
|
||||||
| Right
|
| Right
|
||||||
| StartOfLine
|
| StartOfLine
|
||||||
|
| StartOfParagraph
|
||||||
|
| EndOfParagraph
|
||||||
| NextWordStart { .. }
|
| NextWordStart { .. }
|
||||||
| PreviousWordStart { .. }
|
| PreviousWordStart { .. }
|
||||||
| FirstNonWhitespace
|
| FirstNonWhitespace
|
||||||
@ -197,6 +210,8 @@ impl Motion {
|
|||||||
| Backspace
|
| Backspace
|
||||||
| Right
|
| Right
|
||||||
| StartOfLine
|
| StartOfLine
|
||||||
|
| StartOfParagraph
|
||||||
|
| EndOfParagraph
|
||||||
| NextWordStart { .. }
|
| NextWordStart { .. }
|
||||||
| PreviousWordStart { .. }
|
| PreviousWordStart { .. }
|
||||||
| FirstNonWhitespace
|
| FirstNonWhitespace
|
||||||
@ -235,6 +250,14 @@ impl Motion {
|
|||||||
FirstNonWhitespace => (first_non_whitespace(map, point), SelectionGoal::None),
|
FirstNonWhitespace => (first_non_whitespace(map, point), SelectionGoal::None),
|
||||||
StartOfLine => (start_of_line(map, point), SelectionGoal::None),
|
StartOfLine => (start_of_line(map, point), SelectionGoal::None),
|
||||||
EndOfLine => (end_of_line(map, point), SelectionGoal::None),
|
EndOfLine => (end_of_line(map, point), SelectionGoal::None),
|
||||||
|
StartOfParagraph => (
|
||||||
|
movement::start_of_paragraph(map, point, times),
|
||||||
|
SelectionGoal::None,
|
||||||
|
),
|
||||||
|
EndOfParagraph => (
|
||||||
|
movement::end_of_paragraph(map, point, times),
|
||||||
|
SelectionGoal::None,
|
||||||
|
),
|
||||||
CurrentLine => (end_of_line(map, point), SelectionGoal::None),
|
CurrentLine => (end_of_line(map, point), SelectionGoal::None),
|
||||||
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
||||||
EndOfDocument => (
|
EndOfDocument => (
|
||||||
@ -590,3 +613,96 @@ fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) ->
|
|||||||
let new_row = (point.row() + times as u32).min(map.max_buffer_row());
|
let new_row = (point.row() + times as u32).min(map.max_buffer_row());
|
||||||
map.clip_point(DisplayPoint::new(new_row, 0), Bias::Left)
|
map.clip_point(DisplayPoint::new(new_row, 0), Bias::Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use crate::{state::Mode, test::VimTestContext};
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
|
||||||
|
let initial_state = indoc! {r"ˇabc
|
||||||
|
def
|
||||||
|
|
||||||
|
paragraph
|
||||||
|
the second
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
third and
|
||||||
|
final"};
|
||||||
|
|
||||||
|
// goes down once
|
||||||
|
cx.set_state(initial_state, Mode::Normal);
|
||||||
|
cx.simulate_keystrokes(["}"]);
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {r"abc
|
||||||
|
def
|
||||||
|
ˇ
|
||||||
|
paragraph
|
||||||
|
the second
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
third and
|
||||||
|
final"},
|
||||||
|
Mode::Normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// goes up once
|
||||||
|
cx.simulate_keystrokes(["{"]);
|
||||||
|
cx.assert_state(initial_state, Mode::Normal);
|
||||||
|
|
||||||
|
// goes down twice
|
||||||
|
cx.simulate_keystrokes(["2", "}"]);
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {r"abc
|
||||||
|
def
|
||||||
|
|
||||||
|
paragraph
|
||||||
|
the second
|
||||||
|
ˇ
|
||||||
|
|
||||||
|
|
||||||
|
third and
|
||||||
|
final"},
|
||||||
|
Mode::Normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// goes down over multiple blanks
|
||||||
|
cx.simulate_keystrokes(["}"]);
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {r"abc
|
||||||
|
def
|
||||||
|
|
||||||
|
paragraph
|
||||||
|
the second
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
third and
|
||||||
|
finalˇ"},
|
||||||
|
Mode::Normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// goes up twice
|
||||||
|
cx.simulate_keystrokes(["2", "{"]);
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {r"abc
|
||||||
|
def
|
||||||
|
ˇ
|
||||||
|
paragraph
|
||||||
|
the second
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
third and
|
||||||
|
final"},
|
||||||
|
Mode::Normal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user