Fix panic in vim motion when not listed as exclusive and add features enum to capture why tests are ignored

This commit is contained in:
K Simmons 2022-10-24 18:27:56 -07:00
parent c295f943ba
commit 21ad375b42
34 changed files with 678 additions and 688 deletions

View file

@ -15,7 +15,7 @@ use crate::{
Vim, Vim,
}; };
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Motion { pub enum Motion {
Left, Left,
Backspace, Backspace,
@ -139,14 +139,22 @@ impl Motion {
pub fn inclusive(self) -> bool { pub fn inclusive(self) -> bool {
use Motion::*; use Motion::*;
if self.linewise() {
return true;
}
match self { match self {
EndOfLine | NextWordEnd { .. } | Matching => true, Down
Left | Right | StartOfLine | NextWordStart { .. } | PreviousWordStart { .. } => false, | Up
_ => panic!("Exclusivity not defined for {self:?}"), | StartOfDocument
| EndOfDocument
| CurrentLine
| EndOfLine
| NextWordEnd { .. }
| Matching => true,
Left
| Backspace
| Right
| StartOfLine
| NextWordStart { .. }
| PreviousWordStart { .. }
| FirstNonWhitespace => false,
} }
} }
@ -194,10 +202,11 @@ impl Motion {
times: usize, times: usize,
expand_to_surrounding_newline: bool, expand_to_surrounding_newline: bool,
) { ) {
let (head, goal) = self.move_point(map, selection.head(), selection.goal, times); let (new_head, goal) = self.move_point(map, selection.head(), selection.goal, times);
selection.set_head(head, goal); selection.set_head(new_head, goal);
if self.linewise() { if self.linewise() {
if selection.start != selection.end {
selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
if expand_to_surrounding_newline { if expand_to_surrounding_newline {
@ -215,6 +224,7 @@ impl Motion {
} }
(_, selection.end) = map.next_line_boundary(selection.end.to_point(map)); (_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
}
} else { } else {
// If the motion is exclusive and the end of the motion is in column 1, the // If the motion is exclusive and the end of the motion is in column 1, the
// end of the motion is moved to the end of the previous line and the motion // end of the motion is moved to the end of the previous line and the motion
@ -222,6 +232,7 @@ impl Motion {
// but "d}" will not include that line. // but "d}" will not include that line.
let mut inclusive = self.inclusive(); let mut inclusive = self.inclusive();
if !inclusive if !inclusive
&& self != Motion::Backspace
&& selection.end.row() > selection.start.row() && selection.end.row() > selection.start.row()
&& selection.end.column() == 0 && selection.end.column() == 0
&& selection.end.row() > 0 && selection.end.row() > 0

View file

@ -372,7 +372,7 @@ mod test {
Mode::{self, *}, Mode::{self, *},
Namespace, Operator, Namespace, Operator,
}, },
test::{NeovimBackedTestContext, VimTestContext}, test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext},
}; };
#[gpui::test] #[gpui::test]
@ -741,10 +741,13 @@ mod test {
brown ˇfox brown ˇfox
jumps ˇover"}) jumps ˇover"})
.await; .await;
cx.assert(indoc! {" cx.assert_exempted(
indoc! {"
The quick The quick
ˇ ˇ
brown fox"}) brown fox"},
ExemptionFeatures::DeletionOnEmptyLine,
)
.await; .await;
} }

View file

@ -85,372 +85,271 @@ fn expand_changed_word_selection(
mod test { mod test {
use indoc::indoc; use indoc::indoc;
use crate::{ use crate::test::{ExemptionFeatures, NeovimBackedTestContext};
state::Mode,
test::{NeovimBackedTestContext, VimTestContext},
};
#[gpui::test] #[gpui::test]
async fn test_change_h(cx: &mut gpui::TestAppContext) { async fn test_change_h(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "h"]);
let mut cx = cx.binding(["c", "h"]).mode_after(Mode::Insert); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Tˇst"); cx.assert("Tˇest").await;
cx.assert("Tˇest", "ˇest"); cx.assert("ˇTest").await;
cx.assert("ˇTest", "ˇTest"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test Test
ˇtest"}, ˇtest"})
indoc! {" .await;
}
#[gpui::test]
async fn test_change_backspace(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx)
.await
.binding(["c", "backspace"]);
cx.assert("Teˇst").await;
cx.assert("Tˇest").await;
cx.assert("ˇTest").await;
cx.assert(indoc! {"
Test Test
ˇtest"}, ˇtest"})
); .await;
} }
#[gpui::test] #[gpui::test]
async fn test_change_l(cx: &mut gpui::TestAppContext) { async fn test_change_l(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "l"]);
let mut cx = cx.binding(["c", "l"]).mode_after(Mode::Insert); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Teˇt"); cx.assert("Tesˇt").await;
cx.assert("Tesˇt", "Tesˇ");
} }
#[gpui::test] #[gpui::test]
async fn test_change_w(cx: &mut gpui::TestAppContext) { async fn test_change_w(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "w"]);
let mut cx = cx.binding(["c", "w"]).mode_after(Mode::Insert); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Teˇ"); cx.assert("Tˇest test").await;
cx.assert("Tˇest test", "Tˇ test"); cx.assert("Testˇ test").await;
cx.assert("Testˇ test", "Testˇtest"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test teˇst Test teˇst
test"}, test"})
indoc! {" .await;
Test teˇ cx.assert(indoc! {"
test"},
);
cx.assert(
indoc! {"
Test tesˇt Test tesˇt
test"}, test"})
indoc! {" .await;
Test tesˇ cx.assert(indoc! {"
test"},
);
cx.assert(
indoc! {"
Test test Test test
ˇ ˇ
test"}, test"})
indoc! {" .await;
Test test
ˇ
test"},
);
let mut cx = cx.binding(["c", "shift-w"]); let mut cx = cx.binding(["c", "shift-w"]);
cx.assert("Test teˇst-test test", "Test teˇ test"); cx.assert("Test teˇst-test test").await;
} }
#[gpui::test] #[gpui::test]
async fn test_change_e(cx: &mut gpui::TestAppContext) { async fn test_change_e(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "e"]);
let mut cx = cx.binding(["c", "e"]).mode_after(Mode::Insert); cx.assert("Teˇst Test").await;
cx.assert("Teˇst Test", "Teˇ Test"); cx.assert("Tˇest test").await;
cx.assert("Tˇest test", "Tˇ test"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test teˇst Test teˇst
test"}, test"})
indoc! {" .await;
Test teˇ cx.assert(indoc! {"
test"},
);
cx.assert(
indoc! {"
Test tesˇt Test tesˇt
test"}, test"})
"Test tesˇ", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test test Test test
ˇ ˇ
test"}, test"})
indoc! {" .await;
Test test
ˇ"},
);
let mut cx = cx.binding(["c", "shift-e"]); let mut cx = cx.binding(["c", "shift-e"]);
cx.assert("Test teˇst-test test", "Test teˇ test"); cx.assert("Test teˇst-test test").await;
} }
#[gpui::test] #[gpui::test]
async fn test_change_b(cx: &mut gpui::TestAppContext) { async fn test_change_b(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "b"]);
let mut cx = cx.binding(["c", "b"]).mode_after(Mode::Insert); cx.assert("Teˇst Test").await;
cx.assert("Teˇst Test", "ˇst Test"); cx.assert("Test ˇtest").await;
cx.assert("Test ˇtest", "ˇtest"); cx.assert("Test1 test2 ˇtest3").await;
cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test test Test test
ˇtest"}, ˇtest"})
indoc! {" .await;
Test ˇ
test"},
);
println!("Marker"); println!("Marker");
cx.assert( cx.assert(indoc! {"
indoc! {"
Test test Test test
ˇ ˇ
test"}, test"})
indoc! {" .await;
Test ˇ
test"},
);
let mut cx = cx.binding(["c", "shift-b"]); let mut cx = cx.binding(["c", "shift-b"]);
cx.assert("Test test-test ˇtest", "Test ˇtest"); cx.assert("Test test-test ˇtest").await;
} }
#[gpui::test] #[gpui::test]
async fn test_change_end_of_line(cx: &mut gpui::TestAppContext) { async fn test_change_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "$"]);
let mut cx = cx.binding(["c", "$"]).mode_after(Mode::Insert); cx.assert(indoc! {"
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox"}, brown fox"})
indoc! {" .await;
The cx.assert(indoc! {"
brown fox"},
);
cx.assert(
indoc! {"
The quick The quick
ˇ ˇ
brown fox"}, brown fox"})
indoc! {" .await;
The quick
ˇ
brown fox"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_change_0(cx: &mut gpui::TestAppContext) { async fn test_change_0(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "0"]);
let mut cx = cx.binding(["c", "0"]).mode_after(Mode::Insert); cx.assert(indoc! {"
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox"}, brown fox"})
indoc! {" .await;
ˇuick cx.assert(indoc! {"
brown fox"},
);
cx.assert(
indoc! {"
The quick The quick
ˇ ˇ
brown fox"}, brown fox"})
indoc! {" .await;
The quick
ˇ
brown fox"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_change_k(cx: &mut gpui::TestAppContext) { async fn test_change_k(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "k"]);
let mut cx = cx.binding(["c", "k"]).mode_after(Mode::Insert); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown ˇfox brown ˇfox
jumps over"}, jumps over"})
indoc! {" .await;
ˇ cx.assert(indoc! {"
jumps over"},
);
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
jumps ˇover"}, jumps ˇover"})
indoc! {" .await;
The quick cx.assert_exempted(
ˇ"},
);
cx.assert(
indoc! {" indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
ExemptionFeatures::OperatorAbortsOnFailedMotion,
)
.await;
cx.assert_exempted(
indoc! {" indoc! {"
ˇ ˇ
brown fox brown fox
jumps over"}, jumps over"},
); ExemptionFeatures::OperatorAbortsOnFailedMotion,
cx.assert( )
indoc! {" .await;
ˇ
brown fox
jumps over"},
indoc! {"
ˇ
brown fox
jumps over"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_change_j(cx: &mut gpui::TestAppContext) { async fn test_change_j(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "j"]);
let mut cx = cx.binding(["c", "j"]).mode_after(Mode::Insert); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown ˇfox brown ˇfox
jumps over"}, jumps over"})
indoc! {" .await;
The quick cx.assert_exempted(
ˇ"},
);
cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps ˇover"}, jumps ˇover"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
The quick )
brown fox .await;
ˇ"}, cx.assert(indoc! {"
);
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over"}, jumps over"})
indoc! {" .await;
ˇ cx.assert_exempted(
jumps over"},
);
cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
ˇ"}, ˇ"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
The quick )
brown fox .await;
ˇ"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_change_end_of_document(cx: &mut gpui::TestAppContext) { async fn test_change_end_of_document(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx)
let mut cx = cx.binding(["c", "shift-g"]).mode_after(Mode::Insert); .await
cx.assert( .binding(["c", "shift-g"]);
indoc! {" cx.assert(indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
indoc! {" .await;
The quick cx.assert(indoc! {"
ˇ"},
);
cx.assert(
indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
indoc! {" .await;
The quick cx.assert_exempted(
ˇ"},
);
cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the lˇazy"}, the lˇazy"},
ExemptionFeatures::OperatorAbortsOnFailedMotion,
)
.await;
cx.assert_exempted(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
ˇ"}, ˇ"},
); ExemptionFeatures::OperatorAbortsOnFailedMotion,
cx.assert( )
indoc! {" .await;
The quick
brown fox
jumps over
ˇ"},
indoc! {"
The quick
brown fox
jumps over
ˇ"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_change_gg(cx: &mut gpui::TestAppContext) { async fn test_change_gg(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx)
let mut cx = cx.binding(["c", "g", "g"]).mode_after(Mode::Insert); .await
cx.assert( .binding(["c", "g", "g"]);
indoc! {" cx.assert(indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
indoc! {" .await;
ˇ cx.assert(indoc! {"
jumps over
the lazy"},
);
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the lˇazy"}, the lˇazy"})
"ˇ", .await;
); cx.assert_exempted(
cx.assert(
indoc! {" indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
ExemptionFeatures::OperatorAbortsOnFailedMotion,
)
.await;
cx.assert_exempted(
indoc! {" indoc! {"
ˇ ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
); ExemptionFeatures::OperatorAbortsOnFailedMotion,
cx.assert( )
indoc! {" .await;
ˇ
brown fox
jumps over
the lazy"},
indoc! {"
ˇ
brown fox
jumps over
the lazy"},
);
} }
#[gpui::test] #[gpui::test]
@ -493,14 +392,15 @@ mod test {
async fn test_repeated_cb(cx: &mut gpui::TestAppContext) { async fn test_repeated_cb(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await; let mut cx = NeovimBackedTestContext::new(cx).await;
// Changing back any number of times from the start of the file doesn't cx.add_initial_state_exemptions(
// switch to insert mode in vim. This is weird and painful to implement indoc! {"
cx.add_initial_state_exemption(indoc! {"
ˇThe quick brown ˇThe quick brown
fox jumps-over fox jumps-over
the lazy dog the lazy dog
"}); "},
ExemptionFeatures::OperatorAbortsOnFailedMotion,
);
for count in 1..=5 { for count in 1..=5 {
cx.assert_binding_matches_all( cx.assert_binding_matches_all(

View file

@ -96,354 +96,256 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Mutab
mod test { mod test {
use indoc::indoc; use indoc::indoc;
use crate::{state::Mode, test::VimTestContext}; use crate::{
state::Mode,
test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext},
};
#[gpui::test] #[gpui::test]
async fn test_delete_h(cx: &mut gpui::TestAppContext) { async fn test_delete_h(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "h"]);
let mut cx = cx.binding(["d", "h"]); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Tˇst"); cx.assert("Tˇest").await;
cx.assert("Tˇest", "ˇest"); cx.assert("ˇTest").await;
cx.assert("ˇTest", "ˇTest"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test Test
ˇtest"}, ˇtest"})
indoc! {" .await;
Test
ˇtest"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_l(cx: &mut gpui::TestAppContext) { async fn test_delete_l(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "l"]);
let mut cx = cx.binding(["d", "l"]); cx.assert("ˇTest").await;
cx.assert("ˇTest", "ˇest"); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Teˇt"); cx.assert("Tesˇt").await;
cx.assert("Tesˇt", "Teˇs"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Tesˇt Tesˇt
test"}, test"})
indoc! {" .await;
Teˇs
test"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_w(cx: &mut gpui::TestAppContext) { async fn test_delete_w(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "w"]);
let mut cx = cx.binding(["d", "w"]); cx.assert("Teˇst").await;
cx.assert("Teˇst", "Tˇe"); cx.assert("Tˇest test").await;
cx.assert("Tˇest test", "Tˇtest"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test teˇst Test teˇst
test"}, test"})
indoc! {" .await;
Test tˇe cx.assert(indoc! {"
test"},
);
cx.assert(
indoc! {"
Test tesˇt Test tesˇt
test"}, test"})
indoc! {" .await;
Test teˇs cx.assert_exempted(
test"},
);
cx.assert(
indoc! {" indoc! {"
Test test Test test
ˇ ˇ
test"}, test"},
indoc! {" ExemptionFeatures::DeletionOnEmptyLine,
Test test )
ˇ .await;
test"},
);
let mut cx = cx.binding(["d", "shift-w"]); let mut cx = cx.binding(["d", "shift-w"]);
cx.assert("Test teˇst-test test", "Test teˇtest"); cx.assert("Test teˇst-test test").await;
} }
#[gpui::test] #[gpui::test]
async fn test_delete_e(cx: &mut gpui::TestAppContext) { async fn test_delete_e(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "e"]);
let mut cx = cx.binding(["d", "e"]); cx.assert("Teˇst Test").await;
cx.assert("Teˇst Test", "Teˇ Test"); cx.assert("Tˇest test").await;
cx.assert("Tˇest test", "Tˇ test"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test teˇst Test teˇst
test"}, test"})
indoc! {" .await;
Test tˇe cx.assert(indoc! {"
test"},
);
cx.assert(
indoc! {"
Test tesˇt Test tesˇt
test"}, test"})
"Test teˇs", .await;
); cx.assert_exempted(
cx.assert(
indoc! {" indoc! {"
Test test Test test
ˇ ˇ
test"}, test"},
indoc! {" ExemptionFeatures::DeletionOnEmptyLine,
Test test )
ˇ"}, .await;
);
let mut cx = cx.binding(["d", "shift-e"]); let mut cx = cx.binding(["d", "shift-e"]);
cx.assert("Test teˇst-test test", "Test teˇ test"); cx.assert("Test teˇst-test test").await;
} }
#[gpui::test] #[gpui::test]
async fn test_delete_b(cx: &mut gpui::TestAppContext) { async fn test_delete_b(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "b"]);
let mut cx = cx.binding(["d", "b"]); cx.assert("Teˇst Test").await;
cx.assert("Teˇst Test", "ˇst Test"); cx.assert("Test ˇtest").await;
cx.assert("Test ˇtest", "ˇtest"); cx.assert("Test1 test2 ˇtest3").await;
cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3"); cx.assert(indoc! {"
cx.assert(
indoc! {"
Test test Test test
ˇtest"}, ˇtest"})
// Trailing whitespace after cursor .await;
indoc! {" cx.assert(indoc! {"
Testˇ
test"},
);
cx.assert(
indoc! {"
Test test Test test
ˇ ˇ
test"}, test"})
// Trailing whitespace after cursor .await;
indoc! {"
Testˇ
test"},
);
let mut cx = cx.binding(["d", "shift-b"]); let mut cx = cx.binding(["d", "shift-b"]);
cx.assert("Test test-test ˇtest", "Test ˇtest"); cx.assert("Test test-test ˇtest").await;
} }
#[gpui::test] #[gpui::test]
async fn test_delete_end_of_line(cx: &mut gpui::TestAppContext) { async fn test_delete_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "$"]);
let mut cx = cx.binding(["d", "$"]); cx.assert(indoc! {"
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox"}, brown fox"})
indoc! {" .await;
The ˇq cx.assert(indoc! {"
brown fox"},
);
cx.assert(
indoc! {"
The quick The quick
ˇ ˇ
brown fox"}, brown fox"})
indoc! {" .await;
The quick
ˇ
brown fox"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_0(cx: &mut gpui::TestAppContext) { async fn test_delete_0(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "0"]);
let mut cx = cx.binding(["d", "0"]); cx.assert(indoc! {"
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox"}, brown fox"})
indoc! {" .await;
ˇuick cx.assert(indoc! {"
brown fox"},
);
cx.assert(
indoc! {"
The quick The quick
ˇ ˇ
brown fox"}, brown fox"})
indoc! {" .await;
The quick
ˇ
brown fox"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_k(cx: &mut gpui::TestAppContext) { async fn test_delete_k(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "k"]);
let mut cx = cx.binding(["d", "k"]); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown ˇfox brown ˇfox
jumps over"}, jumps over"})
"jumps ˇover", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
jumps ˇover"}, jumps ˇover"})
"The quˇick", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over"}, jumps over"})
indoc! {" .await;
brownˇ fox cx.assert(indoc! {"
jumps over"},
);
cx.assert(
indoc! {"
ˇbrown fox ˇbrown fox
jumps over"}, jumps over"})
"ˇjumps over", .await;
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_j(cx: &mut gpui::TestAppContext) { async fn test_delete_j(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "j"]);
let mut cx = cx.binding(["d", "j"]); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown ˇfox brown ˇfox
jumps over"}, jumps over"})
"The quˇick", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
jumps ˇover"}, jumps ˇover"})
indoc! {" .await;
The quick cx.assert(indoc! {"
brown ˇfox"},
);
cx.assert(
indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over"}, jumps over"})
"jumpsˇ over", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
ˇ"}, ˇ"})
indoc! {" .await;
The quick
ˇbrown fox"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) { async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx)
let mut cx = cx.binding(["d", "shift-g"]); .await
cx.assert( .binding(["d", "shift-g"]);
indoc! {" cx.assert(indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
"The qˇuick", .await;
); cx.assert(indoc! {"
cx.assert(
indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
"The qˇuick", .await;
); cx.assert_exempted(
cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the lˇazy"}, the lˇazy"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
The quick )
brown fox .await;
jumpsˇ over"}, cx.assert_exempted(
);
cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
ˇ"}, ˇ"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
The quick )
brown fox .await;
ˇjumps over"},
);
} }
#[gpui::test] #[gpui::test]
async fn test_delete_gg(cx: &mut gpui::TestAppContext) { async fn test_delete_gg(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let mut cx = NeovimBackedTestContext::new(cx)
let mut cx = cx.binding(["d", "g", "g"]); .await
cx.assert( .binding(["d", "g", "g"]);
indoc! {" cx.assert(indoc! {"
The quick The quick
brownˇ fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"})
indoc! {" .await;
jumpsˇ over cx.assert(indoc! {"
the lazy"},
);
cx.assert(
indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the lˇazy"}, the lˇazy"})
"ˇ", .await;
); cx.assert_exempted(
cx.assert(
indoc! {" indoc! {"
The qˇuick The qˇuick
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
brownˇ fox )
jumps over .await;
the lazy"}, cx.assert_exempted(
);
cx.assert(
indoc! {" indoc! {"
ˇ ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" ExemptionFeatures::OperatorAbortsOnFailedMotion,
ˇbrown fox )
jumps over .await;
the lazy"},
);
} }
#[gpui::test] #[gpui::test]

View file

@ -431,7 +431,7 @@ fn surrounding_markers(
mod test { mod test {
use indoc::indoc; use indoc::indoc;
use crate::test::NeovimBackedTestContext; use crate::test::{ExemptionFeatures, NeovimBackedTestContext};
const WORD_LOCATIONS: &'static str = indoc! {" const WORD_LOCATIONS: &'static str = indoc! {"
The quick ˇbrowˇnˇ The quick ˇbrowˇnˇ
@ -482,25 +482,46 @@ mod test {
cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS) cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS)
.await; .await;
// Visual text objects are slightly broken when used with non empty selections cx.assert_binding_matches_all_exempted(
// cx.assert_binding_matches_all(["v", "h", "i", "w"], WORD_LOCATIONS) ["v", "h", "i", "w"],
// .await; WORD_LOCATIONS,
// cx.assert_binding_matches_all(["v", "l", "i", "w"], WORD_LOCATIONS) ExemptionFeatures::NonEmptyVisualTextObjects,
// .await; )
.await;
cx.assert_binding_matches_all_exempted(
["v", "l", "i", "w"],
WORD_LOCATIONS,
ExemptionFeatures::NonEmptyVisualTextObjects,
)
.await;
cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS) cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS)
.await; .await;
// Visual text objects are slightly broken when used with non empty selections cx.assert_binding_matches_all_exempted(
// cx.assert_binding_matches_all(["v", "i", "h", "shift-w"], WORD_LOCATIONS) ["v", "i", "h", "shift-w"],
// .await; WORD_LOCATIONS,
// cx.assert_binding_matches_all(["v", "i", "l", "shift-w"], WORD_LOCATIONS) ExemptionFeatures::NonEmptyVisualTextObjects,
// .await; )
.await;
cx.assert_binding_matches_all_exempted(
["v", "i", "l", "shift-w"],
WORD_LOCATIONS,
ExemptionFeatures::NonEmptyVisualTextObjects,
)
.await;
// Visual around words is somewhat broken right now when it comes to newlines cx.assert_binding_matches_all_exempted(
// cx.assert_binding_matches_all(["v", "a", "w"], WORD_LOCATIONS) ["v", "a", "w"],
// .await; WORD_LOCATIONS,
// cx.assert_binding_matches_all(["v", "a", "shift-w"], WORD_LOCATIONS) ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
// .await; )
.await;
cx.assert_binding_matches_all_exempted(
["v", "a", "shift-w"],
WORD_LOCATIONS,
ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
)
.await;
} }
const SENTENCE_EXAMPLES: &[&'static str] = &[ const SENTENCE_EXAMPLES: &[&'static str] = &[
@ -511,17 +532,15 @@ mod test {
the lazy doˇgˇ.ˇ ˇThe quick ˇ the lazy doˇgˇ.ˇ ˇThe quick ˇ
brown fox jumps over brown fox jumps over
"}, "},
// Position of the cursor after deletion between lines isn't quite right. indoc! {"
// Deletion in a sentence at the start of a line with whitespace is incorrect. The quick brown fox jumps.
// indoc! {" Over the lazy dog
// The quick brown fox jumps. ˇ
// Over the lazy dog ˇ
// ˇ ˇ fox-jumpˇs over
// ˇ the lazy dog.ˇ
// ˇ fox-jumpˇs over ˇ
// the lazy dog.ˇ "},
// ˇ
// "},
r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#, r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#,
]; ];
@ -530,15 +549,28 @@ mod test {
let mut cx = NeovimBackedTestContext::new(cx) let mut cx = NeovimBackedTestContext::new(cx)
.await .await
.binding(["c", "i", "s"]); .binding(["c", "i", "s"]);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n",
ExemptionFeatures::SentenceOnEmptyLines);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n",
ExemptionFeatures::SentenceAtStartOfLineWithWhitespace);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n",
ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile);
for sentence_example in SENTENCE_EXAMPLES { for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await; cx.assert_all(sentence_example).await;
} }
let mut cx = cx.binding(["c", "a", "s"]); let mut cx = cx.binding(["c", "a", "s"]);
// Resulting position is slightly incorrect for unintuitive reasons. cx.add_initial_state_exemptions(
cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); "The quick brown?ˇ Fox Jumps! Over the lazy.",
// Changing around the sentence at the end of the line doesn't remove whitespace.' ExemptionFeatures::IncorrectLandingPosition,
cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); );
cx.add_initial_state_exemptions(
"The quick brown.)]\'\" Brown fox jumps.ˇ ",
ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
);
for sentence_example in SENTENCE_EXAMPLES { for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await; cx.assert_all(sentence_example).await;
@ -550,15 +582,29 @@ mod test {
let mut cx = NeovimBackedTestContext::new(cx) let mut cx = NeovimBackedTestContext::new(cx)
.await .await
.binding(["d", "i", "s"]); .binding(["d", "i", "s"]);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n fox-jumps over\nthe lazy dog.\n\n",
ExemptionFeatures::SentenceOnEmptyLines);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n",
ExemptionFeatures::SentenceAtStartOfLineWithWhitespace);
cx.add_initial_state_exemptions(
"The quick brown fox jumps.\nOver the lazy dog\n\n\n fox-jumps over\nthe lazy dog.ˇ\nˇ\n",
ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile);
for sentence_example in SENTENCE_EXAMPLES { for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await; cx.assert_all(sentence_example).await;
} }
let mut cx = cx.binding(["d", "a", "s"]); let mut cx = cx.binding(["d", "a", "s"]);
// Resulting position is slightly incorrect for unintuitive reasons. cx.add_initial_state_exemptions(
cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); "The quick brown?ˇ Fox Jumps! Over the lazy.",
// Changing around the sentence at the end of the line doesn't remove whitespace.' ExemptionFeatures::IncorrectLandingPosition,
cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); );
cx.add_initial_state_exemptions(
"The quick brown.)]\'\" Brown fox jumps.ˇ ",
ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
);
for sentence_example in SENTENCE_EXAMPLES { for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await; cx.assert_all(sentence_example).await;
@ -571,14 +617,18 @@ mod test {
.await .await
.binding(["v", "i", "s"]); .binding(["v", "i", "s"]);
for sentence_example in SENTENCE_EXAMPLES { for sentence_example in SENTENCE_EXAMPLES {
cx.assert_all(sentence_example).await; cx.assert_all_exempted(sentence_example, ExemptionFeatures::SentenceOnEmptyLines)
.await;
} }
// Visual around sentences is somewhat broken right now when it comes to newlines let mut cx = cx.binding(["v", "a", "s"]);
// let mut cx = cx.binding(["d", "a", "s"]); for sentence_example in SENTENCE_EXAMPLES {
// for sentence_example in SENTENCE_EXAMPLES { cx.assert_all_exempted(
// cx.assert_all(sentence_example).await; sentence_example,
// } ExemptionFeatures::AroundSentenceStartingBetweenIncludesWrongWhitespace,
)
.await;
}
} }
// Test string with "`" for opening surrounders and "'" for closing surrounders // Test string with "`" for opening surrounders and "'" for closing surrounders
@ -588,10 +638,9 @@ mod test {
the ˇlazy 'ˇ`ˇg"}; the ˇlazy 'ˇ`ˇg"};
const SURROUNDING_OBJECTS: &[(char, char)] = &[ const SURROUNDING_OBJECTS: &[(char, char)] = &[
// ('\'', '\''), // Quote, ('\'', '\''), // Quote
// ('`', '`'), // Back Quote ('`', '`'), // Back Quote
// ('"', '"'), // Double Quote ('"', '"'), // Double Quote
// ('"', '"'), // Double Quote
('(', ')'), // Parentheses ('(', ')'), // Parentheses
('[', ']'), // SquareBrackets ('[', ']'), // SquareBrackets
('{', '}'), // CurlyBrackets ('{', '}'), // CurlyBrackets
@ -603,16 +652,23 @@ mod test {
let mut cx = NeovimBackedTestContext::new(cx).await; let mut cx = NeovimBackedTestContext::new(cx).await;
for (start, end) in SURROUNDING_OBJECTS { for (start, end) in SURROUNDING_OBJECTS {
if ((start == &'\'' || start == &'`' || start == &'"')
&& !ExemptionFeatures::QuotesSeekForward.supported())
|| (start == &'<' && !ExemptionFeatures::AngleBracketsFreezeNeovim.supported())
{
continue;
}
let marked_string = SURROUNDING_MARKER_STRING let marked_string = SURROUNDING_MARKER_STRING
.replace('`', &start.to_string()) .replace('`', &start.to_string())
.replace('\'', &end.to_string()); .replace('\'', &end.to_string());
// cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string) cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string)
// .await; .await;
cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string) cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string)
.await; .await;
// cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string) cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string)
// .await; .await;
cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string) cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string)
.await; .await;
} }
@ -623,16 +679,22 @@ mod test {
let mut cx = NeovimBackedTestContext::new(cx).await; let mut cx = NeovimBackedTestContext::new(cx).await;
for (start, end) in SURROUNDING_OBJECTS { for (start, end) in SURROUNDING_OBJECTS {
if ((start == &'\'' || start == &'`' || start == &'"')
&& !ExemptionFeatures::QuotesSeekForward.supported())
|| (start == &'<' && !ExemptionFeatures::AngleBracketsFreezeNeovim.supported())
{
continue;
}
let marked_string = SURROUNDING_MARKER_STRING let marked_string = SURROUNDING_MARKER_STRING
.replace('`', &start.to_string()) .replace('`', &start.to_string())
.replace('\'', &end.to_string()); .replace('\'', &end.to_string());
// cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string) cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string)
// .await; .await;
cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string) cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string)
.await; .await;
// cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string) cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string)
// .await; .await;
cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string) cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string)
.await; .await;
} }

View file

@ -4,7 +4,7 @@ use gpui::ContextHandle;
use crate::state::Mode; use crate::state::Mode;
use super::NeovimBackedTestContext; use super::{ExemptionFeatures, NeovimBackedTestContext, SUPPORTED_FEATURES};
pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> { pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> {
cx: NeovimBackedTestContext<'a>, cx: NeovimBackedTestContext<'a>,
@ -42,6 +42,20 @@ impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> {
.await .await
} }
pub async fn assert_exempted(
&mut self,
marked_positions: &str,
feature: ExemptionFeatures,
) -> Option<(ContextHandle, ContextHandle)> {
if SUPPORTED_FEATURES.contains(&feature) {
self.cx
.assert_binding_matches(self.keystrokes_under_test, marked_positions)
.await
} else {
None
}
}
pub fn assert_manual( pub fn assert_manual(
&mut self, &mut self,
initial_state: &str, initial_state: &str,
@ -63,6 +77,18 @@ impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> {
.assert_binding_matches_all(self.keystrokes_under_test, marked_positions) .assert_binding_matches_all(self.keystrokes_under_test, marked_positions)
.await .await
} }
pub async fn assert_all_exempted(
&mut self,
marked_positions: &str,
feature: ExemptionFeatures,
) {
if SUPPORTED_FEATURES.contains(&feature) {
self.cx
.assert_binding_matches_all(self.keystrokes_under_test, marked_positions)
.await
}
}
} }
impl<'a, const COUNT: usize> Deref for NeovimBackedBindingTestContext<'a, COUNT> { impl<'a, const COUNT: usize> Deref for NeovimBackedBindingTestContext<'a, COUNT> {

View file

@ -8,6 +8,45 @@ use util::test::marked_text_offsets;
use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
use crate::state::Mode; use crate::state::Mode;
pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[];
/// Enum representing features we have tests for but which don't work, yet. Used
/// to add exemptions and automatically
#[derive(PartialEq, Eq)]
pub enum ExemptionFeatures {
// MOTIONS
// Deletions on empty lines miss some newlines
DeletionOnEmptyLine,
// When a motion fails, it should should not apply linewise operations
OperatorAbortsOnFailedMotion,
// OBJECTS
// Resulting position after the operation is slightly incorrect for unintuitive reasons.
IncorrectLandingPosition,
// Operator around the text object at the end of the line doesn't remove whitespace.
AroundObjectLeavesWhitespaceAtEndOfLine,
// Sentence object on empty lines
SentenceOnEmptyLines,
// Whitespace isn't included with text objects at the start of the line
SentenceAtStartOfLineWithWhitespace,
// Whitespace around sentences is slightly incorrect when starting between sentences
AroundSentenceStartingBetweenIncludesWrongWhitespace,
// Non empty selection with text objects in visual mode
NonEmptyVisualTextObjects,
// Quote style surrounding text objects don't seek forward properly
QuotesSeekForward,
// Neovim freezes up for some reason with angle brackets
AngleBracketsFreezeNeovim,
// Sentence Doesn't backtrack when its at the end of the file
SentenceAfterPunctuationAtEndOfFile,
}
impl ExemptionFeatures {
pub fn supported(&self) -> bool {
SUPPORTED_FEATURES.contains(self)
}
}
pub struct NeovimBackedTestContext<'a> { pub struct NeovimBackedTestContext<'a> {
cx: VimTestContext<'a>, cx: VimTestContext<'a>,
// Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which
@ -27,10 +66,22 @@ impl<'a> NeovimBackedTestContext<'a> {
} }
} }
pub fn add_initial_state_exemption(&mut self, initial_state: &str) { pub fn add_initial_state_exemptions(
let initial_state = initial_state.to_string(); &mut self,
marked_positions: &str,
missing_feature: ExemptionFeatures, // Feature required to support this exempted test case
) {
if !missing_feature.supported() {
let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
for cursor_offset in cursor_offsets.iter() {
let mut marked_text = unmarked_text.clone();
marked_text.insert(*cursor_offset, 'ˇ');
// None represents all keybindings being exempted for that initial state // None represents all keybindings being exempted for that initial state
self.exemptions.insert(initial_state, None); self.exemptions.insert(marked_text, None);
}
}
} }
pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
@ -120,6 +171,18 @@ impl<'a> NeovimBackedTestContext<'a> {
} }
} }
pub async fn assert_binding_matches_all_exempted<const COUNT: usize>(
&mut self,
keystrokes: [&str; COUNT],
marked_positions: &str,
feature: ExemptionFeatures,
) {
if SUPPORTED_FEATURES.contains(&feature) {
self.assert_binding_matches_all(keystrokes, marked_positions)
.await
}
}
pub fn binding<const COUNT: usize>( pub fn binding<const COUNT: usize>(
self, self,
keystrokes: [&'static str; COUNT], keystrokes: [&'static str; COUNT],

View file

@ -0,0 +1 @@
[{"Text":"uick\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"st Test"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"test"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Test1 test3"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Test \ntest"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Test \n\ntest"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Test test"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"Tst"},{"Mode":"Insert"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Insert"},{"Text":"est"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Test"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Testtest"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"Te Test"},{"Mode":"Insert"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Insert"},{"Text":"T test"},{"Mode":"Insert"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Insert"},{"Text":"Test te\ntest"},{"Mode":"Insert"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Insert"},{"Text":"Test tes"},{"Mode":"Insert"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Insert"},{"Text":"Test test\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"Test te test"},{"Mode":"Insert"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"The q\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"Tst"},{"Mode":"Insert"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Insert"},{"Text":"est"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Test"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Test\ntest"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]

View file

@ -0,0 +1 @@
[{"Text":"Tet"},{"Mode":"Insert"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Insert"},{"Text":"Tes"},{"Mode":"Insert"},{"Selection":{"start":[0,3],"end":[0,3]}},{"Mode":"Insert"}]

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
[{"Text":"Te"},{"Mode":"Insert"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Insert"},{"Text":"T test"},{"Mode":"Insert"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Insert"},{"Text":"Testtest"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"Test te\ntest"},{"Mode":"Insert"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Insert"},{"Text":"Test tes\ntest"},{"Mode":"Insert"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Insert"},{"Text":"Test test\n\ntest"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"Test te test"},{"Mode":"Insert"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Insert"}]

View file

@ -1 +1 @@
[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] [{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"uick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"st Test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Test1 test3"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test \ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"Test \n\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"Test test"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"Te Test"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"T test"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test te test"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"The q\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"jumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"Tst"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"est"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Test\ntest"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,6],"end":[2,6]}},{"Mode":"Normal"},{"Text":"jumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"jumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]

View file

@ -0,0 +1 @@
[{"Text":"est"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Tet"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"Tes"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"Tes\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"}]

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
[{"Text":"Te"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Ttest"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Test te\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"Test tes\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"Test tetest"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"}]

File diff suppressed because one or more lines are too long