Fix g_ to go to last non-whitespace character

Use the new helpers for yss to do less buffer parsing.
This commit is contained in:
Conrad Irwin 2024-04-30 21:39:59 -06:00
parent 1e86d012a3
commit e2ae0125bd
4 changed files with 77 additions and 38 deletions

View File

@ -447,6 +447,8 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
vim.clear_operator(cx);
if let Some(operator) = waiting_operator {
vim.push_operator(operator, cx);
dbg!(count);
vim.update_state(|state| state.pre_count = count)
}
});
}
@ -755,7 +757,7 @@ impl Motion {
},
NextLineStart => (next_line_start(map, point, times), SelectionGoal::None),
StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None),
EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None),
EndOfLineDownward => (last_non_whitespace(map, point, times), SelectionGoal::None),
GoToColumn => (go_to_column(map, point, times), SelectionGoal::None),
WindowTop => window_top(map, point, &text_layout_details, times - 1),
WindowMiddle => window_middle(map, point, &text_layout_details),
@ -1422,6 +1424,28 @@ pub(crate) fn first_non_whitespace(
start_offset.to_display_point(map)
}
pub(crate) fn last_non_whitespace(
map: &DisplaySnapshot,
from: DisplayPoint,
count: usize,
) -> DisplayPoint {
let mut end_of_line = end_of_line(map, false, from, count).to_offset(map, Bias::Left);
let scope = map.buffer_snapshot.language_scope_at(from.to_point(map));
dbg!(end_of_line);
for (ch, offset) in map.reverse_buffer_chars_at(end_of_line) {
if ch == '\n' {
break;
}
end_of_line = offset;
dbg!(ch, offset);
if char_kind(&scope, ch) != CharKind::Whitespace || ch == '\n' {
break;
}
}
end_of_line.to_display_point(map)
}
pub(crate) fn start_of_line(
map: &DisplaySnapshot,
display_lines: bool,
@ -1899,6 +1923,16 @@ mod test {
cx.assert_shared_state("one\n ˇtwo\nthree").await;
}
#[gpui::test]
async fn test_end_of_line_downward(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state("ˇ one \n two \nthree").await;
cx.simulate_shared_keystrokes(["g", "_"]).await;
cx.assert_shared_state(" onˇe \n two \nthree").await;
cx.simulate_shared_keystrokes(["2", "g", "_"]).await;
cx.assert_shared_state(" one \n twˇo \nthree").await;
}
#[gpui::test]
async fn test_window_top(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;

View File

@ -1,7 +1,12 @@
use crate::{motion::Motion, object::Object, state::Mode, Vim};
use crate::{
motion::{self, Motion},
object::Object,
state::Mode,
Vim,
};
use editor::{movement, scroll::Autoscroll, Bias};
use gpui::WindowContext;
use language::{char_kind, BracketPair, CharKind};
use language::BracketPair;
use serde::Deserialize;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, Eq)]
@ -23,6 +28,7 @@ impl<'de> Deserialize<'de> for SurroundsType {
pub fn add_surrounds(text: Arc<str>, target: SurroundsType, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| {
vim.stop_recording();
let count = vim.take_count(cx);
vim.update_active_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
@ -52,43 +58,26 @@ pub fn add_surrounds(text: Arc<str>, target: SurroundsType, cx: &mut WindowConte
.range(
&display_map,
selection.clone(),
Some(1),
count,
true,
&text_layout_details,
)
.map(|mut range| {
// The Motion::CurrentLine operation will contain the newline of the current line,
// so we need to deal with this edge case
// The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace
if let Motion::CurrentLine = motion {
let start_offset =
range.start.to_offset(&display_map, Bias::Left);
let scope = display_map
.buffer_snapshot
.language_scope_at(range.start.to_point(&display_map));
for (ch, _) in display_map.buffer_chars_at(start_offset) {
if char_kind(&scope, ch) != CharKind::Whitespace {
break;
}
range.start =
movement::right(&display_map, range.start);
}
let end_offset =
range.end.to_offset(&display_map, Bias::Left);
let scope = display_map
.buffer_snapshot
.language_scope_at(range.end.to_point(&display_map));
for (last_ch, _) in
display_map.reverse_buffer_chars_at(end_offset)
{
if last_ch != '\n'
&& char_kind(&scope, last_ch)
!= CharKind::Whitespace
{
break;
}
range.end = movement::left(&display_map, range.end);
}
range.start = motion::first_non_whitespace(
&display_map,
false,
range.start,
);
range.end = movement::saturating_right(
&display_map,
motion::last_non_whitespace(
&display_map,
movement::left(&display_map, range.end),
1,
),
);
}
range
});
@ -651,7 +640,7 @@ mod test {
cx.set_state(
indoc! {"
The quˇick brown
The quˇick brown
fox jumps over
the lazy dog."},
Mode::Normal,
@ -659,11 +648,19 @@ mod test {
cx.simulate_keystrokes(["y", "s", "s", "{"]);
cx.assert_state(
indoc! {"
ˇ{ The quick brown }
ˇ{ The quick brown }
fox jumps over
the lazy dog."},
Mode::Normal,
);
cx.simulate_keystrokes(["2", "y", "s", "s", ")"]);
cx.assert_state(
indoc! {"
ˇ({ The quick brown }
fox jumps over)
the lazy dog."},
Mode::Normal,
);
}
#[gpui::test]

View File

@ -526,7 +526,7 @@ impl Vim {
| Operator::ChangeSurrounds { .. }
| Operator::DeleteSurrounds
) {
self.clear_operator(cx);
self.update_state(|state| state.operator_stack.clear());
};
self.update_state(|state| state.operator_stack.push(operator));
self.sync_vim_settings(cx);

View File

@ -0,0 +1,8 @@
{"Put":{"state":"ˇ one \n two \nthree"}}
{"Key":"g"}
{"Key":"_"}
{"Get":{"state":" onˇe \n two \nthree","mode":"Normal"}}
{"Key":"2"}
{"Key":"g"}
{"Key":"_"}
{"Get":{"state":" one \n twˇo \nthree","mode":"Normal"}}