vim: Fix Helix mode subword motions to select traversed text (#45760)
Update Helix's handling of the following actions in order to ensure that selections are created when using subword motions: * `vim::NextSubwordStart` * `vim::NextSubwordEnd` * `vim::PreviousSubwordStart` * `vim::PreviousSubwordEnd` The handling of these motions was done by `vim::helix::Vim::helix_move_and_collapse`, which simply moved the cursor without doing any selection. This commit updates it to correctly use both `vim::helix::Vim::helix_find_range_forward` and `vim::helix::Vim::helix_find_range_backward`. The added tests have been confirmed against Helix's behavior of the following commands: * `move_next_sub_word_start` * `move_prev_sub_word_start` * `move_next_sub_word_end` * `move_prev_sub_word_end` Closes #41767 Release Notes: - Fix subword motions in Helix mode to select traversed text --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: dino <dinojoaocosta@gmail.com>
This commit is contained in:
1 parent
b986e15791
commit
293ae8d897
2 files changed
+380
-2
No files matched your search
+378
-1
@@ -367,6 +367,56 @@ impl Vim {
|
||||
}
|
||||
}
|
||||
|
||||
/// When `reversed` is true (used with `helix_find_range_backward`), the
|
||||
/// `left` and `right` characters are yielded in reverse text order, so the
|
||||
/// camelCase transition check must be flipped accordingly.
|
||||
fn subword_boundary_start(
|
||||
ignore_punctuation: bool,
|
||||
reversed: bool,
|
||||
) -> impl FnMut(char, char, &CharClassifier) -> bool {
|
||||
move |left, right, classifier| {
|
||||
let left_kind = classifier.kind_with(left, ignore_punctuation);
|
||||
let right_kind = classifier.kind_with(right, ignore_punctuation);
|
||||
let at_newline = (left == '\n') ^ (right == '\n');
|
||||
let is_separator = |c: char| "_$=".contains(c);
|
||||
|
||||
let is_word = left_kind != right_kind && right_kind != CharKind::Whitespace;
|
||||
let is_subword = (is_separator(left) && !is_separator(right))
|
||||
|| if reversed {
|
||||
right.is_lowercase() && left.is_uppercase()
|
||||
} else {
|
||||
left.is_lowercase() && right.is_uppercase()
|
||||
};
|
||||
|
||||
is_word || (is_subword && !right.is_whitespace()) || at_newline
|
||||
}
|
||||
}
|
||||
|
||||
/// When `reversed` is true (used with `helix_find_range_backward`), the
|
||||
/// `left` and `right` characters are yielded in reverse text order, so the
|
||||
/// camelCase transition check must be flipped accordingly.
|
||||
fn subword_boundary_end(
|
||||
ignore_punctuation: bool,
|
||||
reversed: bool,
|
||||
) -> impl FnMut(char, char, &CharClassifier) -> bool {
|
||||
move |left, right, classifier| {
|
||||
let left_kind = classifier.kind_with(left, ignore_punctuation);
|
||||
let right_kind = classifier.kind_with(right, ignore_punctuation);
|
||||
let at_newline = (left == '\n') ^ (right == '\n');
|
||||
let is_separator = |c: char| "_$=".contains(c);
|
||||
|
||||
let is_word = left_kind != right_kind && left_kind != CharKind::Whitespace;
|
||||
let is_subword = (!is_separator(left) && is_separator(right))
|
||||
|| if reversed {
|
||||
right.is_lowercase() && left.is_uppercase()
|
||||
} else {
|
||||
left.is_lowercase() && right.is_uppercase()
|
||||
};
|
||||
|
||||
is_word || (is_subword && !left.is_whitespace()) || at_newline
|
||||
}
|
||||
}
|
||||
|
||||
pub fn helix_move_cursor(
|
||||
&mut self,
|
||||
motion: Motion,
|
||||
@@ -391,6 +441,29 @@ impl Vim {
|
||||
let mut is_boundary = Self::is_boundary_right(ignore_punctuation);
|
||||
self.helix_find_range_backward(times, window, cx, &mut is_boundary)
|
||||
}
|
||||
// The subword motions implementation is based off of the same
|
||||
// commands present in Helix itself, namely:
|
||||
//
|
||||
// * `move_next_sub_word_start`
|
||||
// * `move_next_sub_word_end`
|
||||
// * `move_prev_sub_word_start`
|
||||
// * `move_prev_sub_word_end`
|
||||
Motion::NextSubwordStart { ignore_punctuation } => {
|
||||
let mut is_boundary = Self::subword_boundary_start(ignore_punctuation, false);
|
||||
self.helix_find_range_forward(times, window, cx, &mut is_boundary)
|
||||
}
|
||||
Motion::NextSubwordEnd { ignore_punctuation } => {
|
||||
let mut is_boundary = Self::subword_boundary_end(ignore_punctuation, false);
|
||||
self.helix_find_range_forward(times, window, cx, &mut is_boundary)
|
||||
}
|
||||
Motion::PreviousSubwordStart { ignore_punctuation } => {
|
||||
let mut is_boundary = Self::subword_boundary_end(ignore_punctuation, true);
|
||||
self.helix_find_range_backward(times, window, cx, &mut is_boundary)
|
||||
}
|
||||
Motion::PreviousSubwordEnd { ignore_punctuation } => {
|
||||
let mut is_boundary = Self::subword_boundary_start(ignore_punctuation, true);
|
||||
self.helix_find_range_backward(times, window, cx, &mut is_boundary)
|
||||
}
|
||||
Motion::EndOfLine { .. } => {
|
||||
// In Helix mode, EndOfLine should position cursor ON the last character,
|
||||
// not after it. We therefore need special handling for it.
|
||||
@@ -902,7 +975,7 @@ impl Vim {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use gpui::{UpdateGlobal, VisualTestContext};
|
||||
use gpui::{KeyBinding, UpdateGlobal, VisualTestContext};
|
||||
use indoc::indoc;
|
||||
use project::FakeFs;
|
||||
use search::{ProjectSearchView, project_search};
|
||||
@@ -975,6 +1048,310 @@ mod test {
|
||||
cx.assert_state("aa\n«ˇ »bb", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_next_subword_start(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
cx.enable_helix();
|
||||
|
||||
// Setup custom keybindings for subword motions so we can use the bindings
|
||||
// in `simulate_keystroke`.
|
||||
cx.update(|_window, cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
"w",
|
||||
crate::motion::NextSubwordStart {
|
||||
ignore_punctuation: false,
|
||||
},
|
||||
None,
|
||||
)]);
|
||||
});
|
||||
|
||||
cx.set_state("ˇfoo.bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«fooˇ».bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo«.ˇ»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo.«barˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo(bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«fooˇ»(bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo«(ˇ»bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo(«barˇ»)", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo_bar_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«foo_ˇ»bar_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo_«bar_ˇ»baz", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfooBarBaz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«fooˇ»BarBaz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo«Barˇ»Baz", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo;bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«fooˇ»;bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo«;ˇ»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("foo;«barˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇ<?php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("«<?ˇ»php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?«phpˇ»\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n«$ˇ»someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n$«someˇ»Variable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n$some«Variable ˇ»= 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n$someVariable «= ˇ»2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n$someVariable = «2ˇ»;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("w");
|
||||
cx.assert_state("<?php\n\n$someVariable = 2«;ˇ»", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_next_subword_end(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
cx.enable_helix();
|
||||
|
||||
// Setup custom keybindings for subword motions so we can use the bindings
|
||||
// in `simulate_keystroke`.
|
||||
cx.update(|_window, cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
"e",
|
||||
crate::motion::NextSubwordEnd {
|
||||
ignore_punctuation: false,
|
||||
},
|
||||
None,
|
||||
)]);
|
||||
});
|
||||
|
||||
cx.set_state("ˇfoo.bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«fooˇ».bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo«.ˇ»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo.«barˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo(bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«fooˇ»(bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo«(ˇ»bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo(«barˇ»)", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo_bar_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«fooˇ»_bar_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo«_barˇ»_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo_bar«_bazˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfooBarBaz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«fooˇ»BarBaz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo«Barˇ»Baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("fooBar«Bazˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇfoo;bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«fooˇ»;bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo«;ˇ»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("foo;«barˇ»", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("ˇ<?php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("«<?ˇ»php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?«phpˇ»\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n«$ˇ»someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n$«someˇ»Variable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n$some«Variableˇ» = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n$someVariable« =ˇ» 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n$someVariable =« 2ˇ»;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("e");
|
||||
cx.assert_state("<?php\n\n$someVariable = 2«;ˇ»", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_previous_subword_start(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
cx.enable_helix();
|
||||
|
||||
// Setup custom keybindings for subword motions so we can use the bindings
|
||||
// in `simulate_keystroke`.
|
||||
cx.update(|_window, cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
"b",
|
||||
crate::motion::PreviousSubwordStart {
|
||||
ignore_punctuation: false,
|
||||
},
|
||||
None,
|
||||
)]);
|
||||
});
|
||||
|
||||
cx.set_state("foo.barˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo.«ˇbar»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo«ˇ.»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇfoo».bar", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo(bar)ˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo(bar«ˇ)»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo(«ˇbar»)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo«ˇ(»bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇfoo»(bar)", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo_bar_bazˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo_bar_«ˇbaz»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo_«ˇbar_»baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇfoo_»bar_baz", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo;barˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo;«ˇbar»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo«ˇ;»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇfoo»;bar", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("<?php\n\n$someVariable = 2;ˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n$someVariable = 2«ˇ;»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n$someVariable = «ˇ2»;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n$someVariable «ˇ= »2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n$some«ˇVariable »= 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n$«ˇsome»Variable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?php\n\n«ˇ$»someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("<?«ˇphp»\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇ<?»php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("fooBarBazˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("fooBar«ˇBaz»", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("foo«ˇBar»Baz", Mode::HelixNormal);
|
||||
cx.simulate_keystroke("b");
|
||||
cx.assert_state("«ˇfoo»BarBaz", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_previous_subword_end(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
cx.enable_helix();
|
||||
|
||||
// Setup custom keybindings for subword motions so we can use the bindings
|
||||
// in `simulate_keystrokes`.
|
||||
cx.update(|_window, cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
"g e",
|
||||
crate::motion::PreviousSubwordEnd {
|
||||
ignore_punctuation: false,
|
||||
},
|
||||
None,
|
||||
)]);
|
||||
});
|
||||
|
||||
cx.set_state("foo.barˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo.«ˇbar»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo«ˇ.»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇfoo».bar", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo(bar)ˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo(bar«ˇ)»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo(«ˇbar»)", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo«ˇ(»bar)", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇfoo»(bar)", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo_bar_bazˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo_bar«ˇ_baz»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo«ˇ_bar»_baz", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇfoo»_bar_baz", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("foo;barˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo;«ˇbar»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo«ˇ;»bar", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇfoo»;bar", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("<?php\n\n$someVariable = 2;ˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n$someVariable = 2«ˇ;»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n$someVariable =«ˇ 2»;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n$someVariable«ˇ =» 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n$some«ˇVariable» = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n$«ˇsome»Variable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?php\n\n«ˇ$»someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("<?«ˇphp»\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇ<?»php\n\n$someVariable = 2;", Mode::HelixNormal);
|
||||
|
||||
cx.set_state("fooBarBazˇ", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("fooBar«ˇBaz»", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("foo«ˇBar»Baz", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("g e");
|
||||
cx.assert_state("«ˇfoo»BarBaz", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
@@ -1924,9 +1924,10 @@ fn next_subword_start(
|
||||
let found_subword_start = is_subword_start(left, right, ".$_-");
|
||||
let is_word_start = (left_kind != right_kind)
|
||||
&& (!right.is_ascii_punctuation() || is_stopping_punct(right));
|
||||
|
||||
let found = (!right.is_whitespace() && (is_word_start || found_subword_start))
|
||||
|| at_newline && crossed_newline
|
||||
|| at_newline && left == '\n'; // Prevents skipping repeated empty lines
|
||||
|| right == '\n' && left == '\n'; // Prevents skipping repeated empty lines
|
||||
|
||||
crossed_newline |= at_newline;
|
||||
found
|
||||
|
||||
Reference in new issue
Block a user