Merge pull request #1803 from zed-industries/fix-vim-motion-panic

Add more explicit neovim testcase exemptions
This commit is contained in:
Kay Simmons 2022-10-24 23:30:06 -07:00 committed by GitHub
commit 076d353e84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 678 additions and 693 deletions

View file

@ -15,7 +15,7 @@ use crate::{
Vim,
};
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Motion {
Left,
Backspace,
@ -139,14 +139,22 @@ impl Motion {
pub fn inclusive(self) -> bool {
use Motion::*;
if self.linewise() {
return true;
}
match self {
EndOfLine | NextWordEnd { .. } | Matching => true,
Left | Right | StartOfLine | NextWordStart { .. } | PreviousWordStart { .. } => false,
_ => panic!("Exclusivity not defined for {self:?}"),
Down
| Up
| StartOfDocument
| EndOfDocument
| CurrentLine
| EndOfLine
| NextWordEnd { .. }
| Matching => true,
Left
| Backspace
| Right
| StartOfLine
| NextWordStart { .. }
| PreviousWordStart { .. }
| FirstNonWhitespace => false,
}
}
@ -194,27 +202,29 @@ impl Motion {
times: usize,
expand_to_surrounding_newline: bool,
) {
let (head, goal) = self.move_point(map, selection.head(), selection.goal, times);
selection.set_head(head, goal);
let (new_head, goal) = self.move_point(map, selection.head(), selection.goal, times);
selection.set_head(new_head, goal);
if self.linewise() {
selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
if selection.start != selection.end {
selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
if expand_to_surrounding_newline {
if selection.end.row() < map.max_point().row() {
*selection.end.row_mut() += 1;
*selection.end.column_mut() = 0;
selection.end = map.clip_point(selection.end, Bias::Right);
// Don't reset the end here
return;
} else if selection.start.row() > 0 {
*selection.start.row_mut() -= 1;
*selection.start.column_mut() = map.line_len(selection.start.row());
selection.start = map.clip_point(selection.start, Bias::Left);
if expand_to_surrounding_newline {
if selection.end.row() < map.max_point().row() {
*selection.end.row_mut() += 1;
*selection.end.column_mut() = 0;
selection.end = map.clip_point(selection.end, Bias::Right);
// Don't reset the end here
return;
} else if selection.start.row() > 0 {
*selection.start.row_mut() -= 1;
*selection.start.column_mut() = map.line_len(selection.start.row());
selection.start = map.clip_point(selection.start, Bias::Left);
}
}
}
(_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
(_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
}
} else {
// 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
@ -222,6 +232,7 @@ impl Motion {
// but "d}" will not include that line.
let mut inclusive = self.inclusive();
if !inclusive
&& self != Motion::Backspace
&& selection.end.row() > selection.start.row()
&& selection.end.column() == 0
&& selection.end.row() > 0

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ use gpui::ContextHandle;
use crate::state::Mode;
use super::NeovimBackedTestContext;
use super::{ExemptionFeatures, NeovimBackedTestContext, SUPPORTED_FEATURES};
pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> {
cx: NeovimBackedTestContext<'a>,
@ -42,6 +42,20 @@ impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> {
.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(
&mut self,
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)
.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> {

View file

@ -8,6 +8,45 @@ use util::test::marked_text_offsets;
use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
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> {
cx: VimTestContext<'a>,
// 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) {
let initial_state = initial_state.to_string();
// None represents all keybindings being exempted for that initial state
self.exemptions.insert(initial_state, None);
pub fn add_initial_state_exemptions(
&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
self.exemptions.insert(marked_text, None);
}
}
}
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>(
self,
keystrokes: [&'static str; COUNT],

View file

@ -38,11 +38,6 @@ impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> {
}
}
pub fn mode_after(mut self, mode_after: Mode) -> Self {
self.mode_after = mode_after;
self
}
pub fn assert(&mut self, initial_state: &str, state_after: &str) {
self.cx.assert_binding(
self.keystrokes_under_test,

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