Merge pull request #1469 from zed-industries/distinctive-marked-text

Tweak "marked ranges" test helper so that it can be used in documents with braces and brackets
This commit is contained in:
Max Brunsfeld 2022-08-04 11:39:04 -07:00 committed by GitHub
commit 134803745a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1362 additions and 1479 deletions

View file

@ -1170,7 +1170,7 @@ pub mod tests {
); );
language.set_theme(&theme); language.set_theme(&theme);
let (text, highlighted_ranges) = marked_text_ranges(r#"const[] [a]: B = "c [d]""#); let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
@ -1246,28 +1246,28 @@ pub mod tests {
} }
use Bias::{Left, Right}; use Bias::{Left, Right};
assert("||α", false, Left, cx); assert("ˇˇα", false, Left, cx);
assert("||α", true, Left, cx); assert("ˇˇα", true, Left, cx);
assert("||α", false, Right, cx); assert("ˇˇα", false, Right, cx);
assert("|α|", true, Right, cx); assert("ˇαˇ", true, Right, cx);
assert("||", false, Left, cx); assert("ˇˇ", false, Left, cx);
assert("||", true, Left, cx); assert("ˇˇ", true, Left, cx);
assert("||", false, Right, cx); assert("ˇˇ", false, Right, cx);
assert("|✋|", true, Right, cx); assert("ˇ✋ˇ", true, Right, cx);
assert("||🍐", false, Left, cx); assert("ˇˇ🍐", false, Left, cx);
assert("||🍐", true, Left, cx); assert("ˇˇ🍐", true, Left, cx);
assert("||🍐", false, Right, cx); assert("ˇˇ🍐", false, Right, cx);
assert("|🍐|", true, Right, cx); assert("ˇ🍐ˇ", true, Right, cx);
assert("||\t", false, Left, cx); assert("ˇˇ\t", false, Left, cx);
assert("||\t", true, Left, cx); assert("ˇˇ\t", true, Left, cx);
assert("||\t", false, Right, cx); assert("ˇˇ\t", false, Right, cx);
assert("|\t|", true, Right, cx); assert("ˇ\tˇ", true, Right, cx);
assert(" ||\t", false, Left, cx); assert(" ˇˇ\t", false, Left, cx);
assert(" ||\t", true, Left, cx); assert(" ˇˇ\t", true, Left, cx);
assert(" ||\t", false, Right, cx); assert(" ˇˇ\t", false, Right, cx);
assert(" |\t|", true, Right, cx); assert(" ˇ\tˇ", true, Right, cx);
assert(" ||\t", false, Left, cx); assert(" ˇˇ\t", false, Left, cx);
assert(" ||\t", false, Right, cx); assert(" ˇˇ\t", false, Right, cx);
} }
#[gpui::test] #[gpui::test]
@ -1283,10 +1283,10 @@ pub mod tests {
); );
} }
assert("||", cx); assert("ˇˇ", cx);
assert("|a|", cx); assert("ˇaˇ", cx);
assert("a|b|", cx); assert("aˇbˇ", cx);
assert("a|α|", cx); assert("aˇαˇ", cx);
} }
#[gpui::test] #[gpui::test]

File diff suppressed because it is too large Load diff

View file

@ -32,13 +32,10 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use indoc::indoc;
use language::{BracketPair, Language, LanguageConfig};
use crate::test::EditorLspTestContext;
use super::*; use super::*;
use crate::test::EditorLspTestContext;
use indoc::indoc;
use language::{BracketPair, Language, LanguageConfig};
#[gpui::test] #[gpui::test]
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) { async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
@ -76,67 +73,61 @@ mod tests {
.await; .await;
// positioning cursor inside bracket highlights both // positioning cursor inside bracket highlights both
cx.set_state_by( cx.set_state(indoc! {r#"
vec!['|'.into()], pub fn test("Test ˇargument") {
indoc! {r#" another_test(1, 2, 3);
pub fn test("Test |argument") { }
another_test(1, 2, 3); "#});
}"#},
);
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test[(]"Test argument"[)] { pub fn test«(»"Test argument"«)» {
another_test(1, 2, 3); another_test(1, 2, 3);
}"#}); }
"#});
cx.set_state_by( cx.set_state(indoc! {r#"
vec!['|'.into()], pub fn test("Test argument") {
indoc! {r#" another_test(1, ˇ2, 3);
pub fn test("Test argument") { }
another_test(1, |2, 3); "#});
}"#},
);
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { pub fn test("Test argument") {
another_test[(]1, 2, 3[)]; another_test«(»1, 2, 3«)»;
}"#}); }
"#});
cx.set_state_by( cx.set_state(indoc! {r#"
vec!['|'.into()], pub fn test("Test argument") {
indoc! {r#" anotherˇ_test(1, 2, 3);
pub fn test("Test argument") { }
another|_test(1, 2, 3); "#});
}"#},
);
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") [{] pub fn test("Test argument") «{»
another_test(1, 2, 3); another_test(1, 2, 3);
[}]"#}); «}»
"#});
// positioning outside of brackets removes highlight // positioning outside of brackets removes highlight
cx.set_state_by( cx.set_state(indoc! {r#"
vec!['|'.into()], pub fˇn test("Test argument") {
indoc! {r#" another_test(1, 2, 3);
pub f|n test("Test argument") { }
another_test(1, 2, 3); "#});
}"#},
);
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { pub fn test("Test argument") {
another_test(1, 2, 3); another_test(1, 2, 3);
}"#}); }
"#});
// non empty selection dismisses highlight // non empty selection dismisses highlight
// positioning outside of brackets removes highlight cx.set_state(indoc! {r#"
cx.set_state_by( pub fn test("Te«st argˇ»ument") {
vec![('<', '>').into()], another_test(1, 2, 3);
indoc! {r#" }
pub fn test("Te<st arg>ument") { "#});
another_test(1, 2, 3);
}"#},
);
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#" cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
pub fn test("Test argument") { pub fn test("Test argument") {
another_test(1, 2, 3); another_test(1, 2, 3);
}"#}); }
"#});
} }
} }

View file

@ -439,11 +439,11 @@ mod tests {
// Basic hover delays and then pops without moving the mouse // Basic hover delays and then pops without moving the mouse
cx.set_state(indoc! {" cx.set_state(indoc! {"
fn |test() fn ˇtest() { println!(); }
println!();"}); "});
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn test() fn test() { printˇln!(); }
print|ln!();"}); "});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
hover_at( hover_at(
@ -458,16 +458,16 @@ mod tests {
// After delay, hover should be visible. // After delay, hover should be visible.
let symbol_range = cx.lsp_range(indoc! {" let symbol_range = cx.lsp_range(indoc! {"
fn test() fn test() { «println!»(); }
[println!]();"}); "});
let mut requests = let mut requests =
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move { cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover { Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent { contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown, kind: lsp::MarkupKind::Markdown,
value: indoc! {" value: indoc! {"
# Some basic docs # Some basic docs
Some test documentation"} Some test documentation"}
.to_string(), .to_string(),
}), }),
range: Some(symbol_range), range: Some(symbol_range),
@ -496,8 +496,8 @@ mod tests {
// Mouse moved with no hover response dismisses // Mouse moved with no hover response dismisses
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn te|st() fn teˇst() { println!(); }
println!();"}); "});
let mut request = cx let mut request = cx
.lsp .lsp
.handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) }); .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
@ -531,12 +531,12 @@ mod tests {
// Hover with keyboard has no delay // Hover with keyboard has no delay
cx.set_state(indoc! {" cx.set_state(indoc! {"
f|n test() fˇn test() { println!(); }
println!();"}); "});
cx.update_editor(|editor, cx| hover(editor, &Hover, cx)); cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
let symbol_range = cx.lsp_range(indoc! {" let symbol_range = cx.lsp_range(indoc! {"
[fn] test() «fn» test() { println!(); }
println!();"}); "});
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move { cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover { Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent { contents: lsp::HoverContents::Markup(lsp::MarkupContent {
@ -584,13 +584,13 @@ mod tests {
// Hover with just diagnostic, pops DiagnosticPopover immediately and then // Hover with just diagnostic, pops DiagnosticPopover immediately and then
// info popover once request completes // info popover once request completes
cx.set_state(indoc! {" cx.set_state(indoc! {"
fn te|st() fn teˇst() { println!(); }
println!();"}); "});
// Send diagnostic to client // Send diagnostic to client
let range = cx.text_anchor_range(indoc! {" let range = cx.text_anchor_range(indoc! {"
fn [test]() fn «test»() { println!(); }
println!();"}); "});
cx.update_buffer(|buffer, cx| { cx.update_buffer(|buffer, cx| {
let snapshot = buffer.text_snapshot(); let snapshot = buffer.text_snapshot();
let set = DiagnosticSet::from_sorted_entries( let set = DiagnosticSet::from_sorted_entries(
@ -616,15 +616,15 @@ mod tests {
// Info Popover shows after request responded to // Info Popover shows after request responded to
let range = cx.lsp_range(indoc! {" let range = cx.lsp_range(indoc! {"
fn [test]() fn «test»() { println!(); }
println!();"}); "});
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move { cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover { Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent { contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown, kind: lsp::MarkupKind::Markdown,
value: indoc! {" value: indoc! {"
# Some other basic docs # Some other basic docs
Some other test documentation"} Some other test documentation"}
.to_string(), .to_string(),
}), }),
range: Some(range), range: Some(range),

View file

@ -405,20 +405,20 @@ mod tests {
cx.set_state(indoc! {" cx.set_state(indoc! {"
struct A; struct A;
let v|ariable = A; let vˇariable = A;
"}); "});
// Basic hold cmd+shift, expect highlight in region if response contains type definition // Basic hold cmd+shift, expect highlight in region if response contains type definition
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
struct A; struct A;
let v|ariable = A; let vˇariable = A;
"}); "});
let symbol_range = cx.lsp_range(indoc! {" let symbol_range = cx.lsp_range(indoc! {"
struct A; struct A;
let [variable] = A; let «variable» = A;
"}); "});
let target_range = cx.lsp_range(indoc! {" let target_range = cx.lsp_range(indoc! {"
struct [A]; struct «A»;
let variable = A; let variable = A;
"}); "});
@ -450,7 +450,7 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
struct A; struct A;
let [variable] = A; let «variable» = A;
"}); "});
// Unpress shift causes highlight to go away (normal goto-definition is not valid here) // Unpress shift causes highlight to go away (normal goto-definition is not valid here)
@ -473,10 +473,10 @@ mod tests {
// Cmd+shift click without existing definition requests and jumps // Cmd+shift click without existing definition requests and jumps
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
struct A; struct A;
let v|ariable = A; let vˇariable = A;
"}); "});
let target_range = cx.lsp_range(indoc! {" let target_range = cx.lsp_range(indoc! {"
struct [A]; struct «A»;
let variable = A; let variable = A;
"}); "});
@ -503,7 +503,7 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
struct [A}; struct «»;
let variable = A; let variable = A;
"}); "});
} }
@ -520,34 +520,22 @@ mod tests {
.await; .await;
cx.set_state(indoc! {" cx.set_state(indoc! {"
fn |test() fn ˇtest() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Basic hold cmd, expect highlight in region if response contains definition // Basic hold cmd, expect highlight in region if response contains definition
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn test() fn test() { do_wˇork(); }
do_w|ork(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let symbol_range = cx.lsp_range(indoc! {" let symbol_range = cx.lsp_range(indoc! {"
fn test() fn test() { «do_work»(); }
[do_work](); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let target_range = cx.lsp_range(indoc! {" let target_range = cx.lsp_range(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn «do_work»() { test(); }
fn [do_work]()
test();
"}); "});
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move { let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
@ -575,11 +563,8 @@ mod tests {
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { «do_work»(); }
[do_work](); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Unpress cmd causes highlight to go away // Unpress cmd causes highlight to go away
@ -593,13 +578,11 @@ mod tests {
cx, cx,
); );
}); });
// Assert no link highlights // Assert no link highlights
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Response without source range still highlights word // Response without source range still highlights word
@ -630,20 +613,14 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { «do_work»(); }
[do_work](); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Moving mouse to location with no response dismisses highlight // Moving mouse to location with no response dismisses highlight
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
f|n test() fˇn test() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let mut requests = cx let mut requests = cx
.lsp .lsp
@ -667,20 +644,14 @@ mod tests {
// Assert no link highlights // Assert no link highlights
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Move mouse without cmd and then pressing cmd triggers highlight // Move mouse without cmd and then pressing cmd triggers highlight
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { teˇst(); }
fn do_work()
te|st();
"}); "});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link( update_go_to_definition_link(
@ -697,26 +668,17 @@ mod tests {
// Assert no link highlights // Assert no link highlights
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let symbol_range = cx.lsp_range(indoc! {" let symbol_range = cx.lsp_range(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { «test»(); }
fn do_work()
[test]();
"}); "});
let target_range = cx.lsp_range(indoc! {" let target_range = cx.lsp_range(indoc! {"
fn [test]() fn «test»() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move { let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
@ -743,20 +705,14 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { «test»(); }
fn do_work()
[test]();
"}); "});
// Moving within symbol range doesn't re-request // Moving within symbol range doesn't re-request
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { tesˇt(); }
fn do_work()
tes|t();
"}); "});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link( update_go_to_definition_link(
@ -771,11 +727,8 @@ mod tests {
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { «test»(); }
fn do_work()
[test]();
"}); "});
// Cmd click with existing definition doesn't re-request and dismisses highlight // Cmd click with existing definition doesn't re-request and dismisses highlight
@ -790,35 +743,24 @@ mod tests {
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
}); });
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
fn [test}() fn «testˇ»() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Assert no link highlights after jump // Assert no link highlights after jump
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
// Cmd click without existing definition requests and jumps // Cmd click without existing definition requests and jumps
let hover_point = cx.display_point(indoc! {" let hover_point = cx.display_point(indoc! {"
fn test() fn test() { do_wˇork(); }
do_w|ork(); fn do_work() { test(); }
fn do_work()
test();
"}); "});
let target_range = cx.lsp_range(indoc! {" let target_range = cx.lsp_range(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn «do_work»() { test(); }
fn [do_work]()
test();
"}); "});
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move { let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
@ -836,13 +778,9 @@ mod tests {
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
fn test() fn test() { do_work(); }
do_work(); fn «do_workˇ»() { test(); }
fn [do_work}()
test();
"}); "});
} }
} }

View file

@ -67,11 +67,9 @@ pub fn deploy_context_menu(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use indoc::indoc;
use crate::test::EditorLspTestContext;
use super::*; use super::*;
use crate::test::EditorLspTestContext;
use indoc::indoc;
#[gpui::test] #[gpui::test]
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) { async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
@ -85,11 +83,15 @@ mod tests {
.await; .await;
cx.set_state(indoc! {" cx.set_state(indoc! {"
fn te|st() fn teˇst() {
do_work();"}); do_work();
}
"});
let point = cx.display_point(indoc! {" let point = cx.display_point(indoc! {"
fn test() fn test() {
do_w|ork();"}); do_wˇork();
}
"});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
deploy_context_menu( deploy_context_menu(
editor, editor,
@ -102,8 +104,10 @@ mod tests {
}); });
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
fn test() fn test() {
do_w|ork();"}); do_wˇork();
}
"});
cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible())); cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
} }
} }

View file

@ -287,20 +287,20 @@ mod tests {
); );
} }
assert("\n| |lorem", cx); assert("\nˇ ˇlorem", cx);
assert("|\n| lorem", cx); assert("ˇ\nˇ lorem", cx);
assert(" |lorem|", cx); assert(" ˇloremˇ", cx);
assert("| |lorem", cx); assert("ˇ ˇlorem", cx);
assert(" |lor|em", cx); assert(" ˇlorˇem", cx);
assert("\nlorem\n| |ipsum", cx); assert("\nlorem\nˇ ˇipsum", cx);
assert("\n\n|\n|", cx); assert("\n\nˇ\nˇ", cx);
assert(" |lorem |ipsum", cx); assert(" ˇlorem ˇipsum", cx);
assert("lorem|-|ipsum", cx); assert("loremˇ-ˇipsum", cx);
assert("lorem|-#$@|ipsum", cx); assert("loremˇ-#$@ˇipsum", cx);
assert("|lorem_|ipsum", cx); assert("ˇlorem_ˇipsum", cx);
assert(" |defγ|", cx); assert(" ˇdefγˇ", cx);
assert(" |bcΔ|", cx); assert(" ˇbcΔˇ", cx);
assert(" ab|——|cd", cx); assert(" abˇ——ˇcd", cx);
} }
#[gpui::test] #[gpui::test]
@ -315,26 +315,26 @@ mod tests {
} }
// Subword boundaries are respected // Subword boundaries are respected
assert("lorem_|ip|sum", cx); assert("lorem_ˇipˇsum", cx);
assert("lorem_|ipsum|", cx); assert("lorem_ˇipsumˇ", cx);
assert("|lorem_|ipsum", cx); assert("ˇlorem_ˇipsum", cx);
assert("lorem_|ipsum_|dolor", cx); assert("lorem_ˇipsum_ˇdolor", cx);
assert("lorem|Ip|sum", cx); assert("loremˇIpˇsum", cx);
assert("lorem|Ipsum|", cx); assert("loremˇIpsumˇ", cx);
// Word boundaries are still respected // Word boundaries are still respected
assert("\n| |lorem", cx); assert("\nˇ ˇlorem", cx);
assert(" |lorem|", cx); assert(" ˇloremˇ", cx);
assert(" |lor|em", cx); assert(" ˇlorˇem", cx);
assert("\nlorem\n| |ipsum", cx); assert("\nlorem\nˇ ˇipsum", cx);
assert("\n\n|\n|", cx); assert("\n\nˇ\nˇ", cx);
assert(" |lorem |ipsum", cx); assert(" ˇlorem ˇipsum", cx);
assert("lorem|-|ipsum", cx); assert("loremˇ-ˇipsum", cx);
assert("lorem|-#$@|ipsum", cx); assert("loremˇ-#$@ˇipsum", cx);
assert(" |defγ|", cx); assert(" ˇdefγˇ", cx);
assert(" bc|Δ|", cx); assert(" bcˇΔˇ", cx);
assert(" |bcδ|", cx); assert(" ˇbcδˇ", cx);
assert(" ab|——|cd", cx); assert(" abˇ——ˇcd", cx);
} }
#[gpui::test] #[gpui::test]
@ -352,14 +352,14 @@ mod tests {
); );
} }
assert("abc|def\ngh\nij|k", cx, |left, right| { assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
left == 'c' && right == 'd' left == 'c' && right == 'd'
}); });
assert("abcdef\n|gh\nij|k", cx, |left, right| { assert("abcdef\nˇgh\nijˇk", cx, |left, right| {
left == '\n' && right == 'g' left == '\n' && right == 'g'
}); });
let mut line_count = 0; let mut line_count = 0;
assert("abcdef\n|gh\nij|k", cx, |left, _| { assert("abcdef\nˇgh\nijˇk", cx, |left, _| {
if left == '\n' { if left == '\n' {
line_count += 1; line_count += 1;
line_count == 2 line_count == 2
@ -380,17 +380,17 @@ mod tests {
); );
} }
assert("\n| lorem|", cx); assert("\nˇ loremˇ", cx);
assert(" |lorem|", cx); assert(" ˇloremˇ", cx);
assert(" lor|em|", cx); assert(" lorˇemˇ", cx);
assert(" lorem| |\nipsum\n", cx); assert(" loremˇ ˇ\nipsum\n", cx);
assert("\n|\n|\n\n", cx); assert("\nˇ\nˇ\n\n", cx);
assert("lorem| ipsum| ", cx); assert("loremˇ ipsumˇ ", cx);
assert("lorem|-|ipsum", cx); assert("loremˇ-ˇipsum", cx);
assert("lorem|#$@-|ipsum", cx); assert("loremˇ#$@-ˇipsum", cx);
assert("lorem|_ipsum|", cx); assert("loremˇ_ipsumˇ", cx);
assert(" |bcΔ|", cx); assert(" ˇbcΔˇ", cx);
assert(" ab|——|cd", cx); assert(" abˇ——ˇcd", cx);
} }
#[gpui::test] #[gpui::test]
@ -405,25 +405,25 @@ mod tests {
} }
// Subword boundaries are respected // Subword boundaries are respected
assert("lo|rem|_ipsum", cx); assert("loˇremˇ_ipsum", cx);
assert("|lorem|_ipsum", cx); assert("ˇloremˇ_ipsum", cx);
assert("lorem|_ipsum|", cx); assert("loremˇ_ipsumˇ", cx);
assert("lorem|_ipsum|_dolor", cx); assert("loremˇ_ipsumˇ_dolor", cx);
assert("lo|rem|Ipsum", cx); assert("loˇremˇIpsum", cx);
assert("lorem|Ipsum|Dolor", cx); assert("loremˇIpsumˇDolor", cx);
// Word boundaries are still respected // Word boundaries are still respected
assert("\n| lorem|", cx); assert("\nˇ loremˇ", cx);
assert(" |lorem|", cx); assert(" ˇloremˇ", cx);
assert(" lor|em|", cx); assert(" lorˇemˇ", cx);
assert(" lorem| |\nipsum\n", cx); assert(" loremˇ ˇ\nipsum\n", cx);
assert("\n|\n|\n\n", cx); assert("\nˇ\nˇ\n\n", cx);
assert("lorem| ipsum| ", cx); assert("loremˇ ipsumˇ ", cx);
assert("lorem|-|ipsum", cx); assert("loremˇ-ˇipsum", cx);
assert("lorem|#$@-|ipsum", cx); assert("loremˇ#$@-ˇipsum", cx);
assert("lorem|_ipsum|", cx); assert("loremˇ_ipsumˇ", cx);
assert(" |bc|Δ", cx); assert(" ˇbcˇΔ", cx);
assert(" ab|——|cd", cx); assert(" abˇ——ˇcd", cx);
} }
#[gpui::test] #[gpui::test]
@ -441,14 +441,14 @@ mod tests {
); );
} }
assert("abc|def\ngh\nij|k", cx, |left, right| { assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
left == 'j' && right == 'k' left == 'j' && right == 'k'
}); });
assert("ab|cdef\ngh\n|ijk", cx, |left, right| { assert("abˇcdef\ngh\nˇijk", cx, |left, right| {
left == '\n' && right == 'i' left == '\n' && right == 'i'
}); });
let mut line_count = 0; let mut line_count = 0;
assert("abc|def\ngh\n|ijk", cx, |left, _| { assert("abcˇdef\ngh\nˇijk", cx, |left, _| {
if left == '\n' { if left == '\n' {
line_count += 1; line_count += 1;
line_count == 2 line_count == 2
@ -469,14 +469,14 @@ mod tests {
); );
} }
assert("||lorem| ipsum", cx); assert("ˇˇloremˇ ipsum", cx);
assert("|lo|rem| ipsum", cx); assert("ˇloˇremˇ ipsum", cx);
assert("|lorem|| ipsum", cx); assert("ˇloremˇˇ ipsum", cx);
assert("lorem| | |ipsum", cx); assert("loremˇ ˇ ˇipsum", cx);
assert("lorem\n|||\nipsum", cx); assert("lorem\nˇˇˇ\nipsum", cx);
assert("lorem\n||ipsum|", cx); assert("lorem\nˇˇipsumˇ", cx);
assert("lorem,|| |ipsum", cx); assert("lorem,ˇˇ ˇipsum", cx);
assert("|lorem||, ipsum", cx); assert("ˇloremˇˇ, ipsum", cx);
} }
#[gpui::test] #[gpui::test]

View file

@ -1,34 +1,28 @@
use std::{
any::TypeId,
ops::{Deref, DerefMut, Range},
sync::Arc,
};
use anyhow::Result;
use futures::{Future, StreamExt};
use indoc::indoc;
use collections::BTreeMap;
use gpui::{
json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle,
};
use language::{
point_to_lsp, Buffer, BufferSnapshot, FakeLspAdapter, Language, LanguageConfig, Selection,
};
use lsp::{notification, request};
use project::Project;
use settings::Settings;
use util::{
assert_set_eq, set_eq,
test::{marked_text, marked_text_ranges, marked_text_ranges_by, SetEqError, TextRangeMarker},
};
use workspace::{pane, AppState, Workspace, WorkspaceHandle};
use crate::{ use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
multi_buffer::ToPointUtf16, multi_buffer::ToPointUtf16,
AnchorRangeExt, Autoscroll, DisplayPoint, Editor, EditorMode, MultiBuffer, ToPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, EditorMode, MultiBuffer, ToPoint,
}; };
use anyhow::Result;
use futures::{Future, StreamExt};
use gpui::{
json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle,
};
use indoc::indoc;
use language::{point_to_lsp, Buffer, BufferSnapshot, FakeLspAdapter, Language, LanguageConfig};
use lsp::{notification, request};
use project::Project;
use settings::Settings;
use std::{
any::TypeId,
ops::{Deref, DerefMut, Range},
sync::Arc,
};
use util::{
assert_set_eq, set_eq,
test::{generate_marked_text, marked_text_offsets, marked_text_ranges},
};
use workspace::{pane, AppState, Workspace, WorkspaceHandle};
#[cfg(test)] #[cfg(test)]
#[ctor::ctor] #[ctor::ctor]
@ -43,7 +37,7 @@ pub fn marked_display_snapshot(
text: &str, text: &str,
cx: &mut gpui::MutableAppContext, cx: &mut gpui::MutableAppContext,
) -> (DisplaySnapshot, Vec<DisplayPoint>) { ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
let (unmarked_text, markers) = marked_text(text); let (unmarked_text, markers) = marked_text_offsets(text);
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
let font_id = cx let font_id = cx
@ -65,7 +59,7 @@ pub fn marked_display_snapshot(
} }
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) { pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
let (umarked_text, text_ranges) = marked_text_ranges(marked_text); let (umarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), umarked_text); assert_eq!(editor.text(cx), umarked_text);
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges)); editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
} }
@ -75,8 +69,7 @@ pub fn assert_text_with_selections(
marked_text: &str, marked_text: &str,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text); let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), unmarked_text); assert_eq!(editor.text(cx), unmarked_text);
assert_eq!(editor.selections.ranges(cx), text_ranges); assert_eq!(editor.selections.ranges(cx), text_ranges);
} }
@ -190,94 +183,58 @@ impl<'a> EditorTestContext<'a> {
} }
} }
pub fn display_point(&mut self, cursor_location: &str) -> DisplayPoint { fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
let (_, locations) = marked_text(cursor_location); let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text);
ranges
}
pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
let ranges = self.ranges(marked_text);
let snapshot = self let snapshot = self
.editor .editor
.update(self.cx, |editor, cx| editor.snapshot(cx)); .update(self.cx, |editor, cx| editor.snapshot(cx));
locations[0].to_display_point(&snapshot.display_snapshot) ranges[0].start.to_display_point(&snapshot)
} }
// Returns anchors for the current buffer using `[`..`]` // Returns anchors for the current buffer using `«` and `»`
pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> { pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
let range_marker: TextRangeMarker = ('[', ']').into(); let ranges = self.ranges(marked_text);
let (unmarked_text, mut ranges) =
marked_text_ranges_by(&marked_text, vec![range_marker.clone()]);
assert_eq!(self.buffer_text(), unmarked_text);
let offset_range = ranges.remove(&range_marker).unwrap()[0].clone();
let snapshot = self.buffer_snapshot(); let snapshot = self.buffer_snapshot();
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
snapshot.anchor_before(offset_range.start)..snapshot.anchor_after(offset_range.end)
} }
// Sets the editor state via a marked string. /// Change the editor's text and selections using a string containing
// `|` characters represent empty selections /// embedded range markers that represent the ranges and directions of
// `[` to `}` represents a non empty selection with the head at `}` /// each selection.
// `{` to `]` represents a non empty selection with the head at `{` ///
pub fn set_state(&mut self, text: &str) { /// See the `util::test::marked_text_ranges` function for more information.
self.set_state_by( pub fn set_state(&mut self, marked_text: &str) {
vec![ let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
'|'.into(),
('[', '}').into(),
TextRangeMarker::ReverseRange('{', ']'),
],
text,
);
}
pub fn set_state_by(&mut self, range_markers: Vec<TextRangeMarker>, text: &str) {
self.editor.update(self.cx, |editor, cx| { self.editor.update(self.cx, |editor, cx| {
let (unmarked_text, selection_ranges) = marked_text_ranges_by(&text, range_markers);
editor.set_text(unmarked_text, cx); editor.set_text(unmarked_text, cx);
let selection_ranges: Vec<Range<usize>> = selection_ranges
.values()
.into_iter()
.flatten()
.cloned()
.collect();
editor.change_selections(Some(Autoscroll::Fit), cx, |s| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
s.select_ranges(selection_ranges) s.select_ranges(selection_ranges)
}) })
}) })
} }
// Asserts the editor state via a marked string. /// Make an assertion about the editor's text and the ranges and directions
// `|` characters represent empty selections /// of its selections using a string containing embedded range markers.
// `[` to `}` represents a non empty selection with the head at `}` ///
// `{` to `]` represents a non empty selection with the head at `{` /// See the `util::test::marked_text_ranges` function for more information.
pub fn assert_editor_state(&mut self, text: &str) { pub fn assert_editor_state(&mut self, marked_text: &str) {
let (unmarked_text, mut selection_ranges) = marked_text_ranges_by( let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
&text,
vec!['|'.into(), ('[', '}').into(), ('{', ']').into()],
);
let buffer_text = self.buffer_text(); let buffer_text = self.buffer_text();
assert_eq!( assert_eq!(
buffer_text, unmarked_text, buffer_text, unmarked_text,
"Unmarked text doesn't match buffer text" "Unmarked text doesn't match buffer text"
); );
self.assert_selections(expected_selections, marked_text.to_string())
let expected_empty_selections = selection_ranges.remove(&'|'.into()).unwrap_or_default();
let expected_reverse_selections = selection_ranges
.remove(&('{', ']').into())
.unwrap_or_default();
let expected_forward_selections = selection_ranges
.remove(&('[', '}').into())
.unwrap_or_default();
self.assert_selections(
expected_empty_selections,
expected_reverse_selections,
expected_forward_selections,
Some(text.to_string()),
)
} }
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) { pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); let expected_ranges = self.ranges(marked_text);
assert_eq!(unmarked, self.buffer_text());
let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap();
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| { let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
editor editor
@ -289,176 +246,62 @@ impl<'a> EditorTestContext<'a> {
.map(|range| range.to_offset(&snapshot.buffer_snapshot)) .map(|range| range.to_offset(&snapshot.buffer_snapshot))
.collect() .collect()
}); });
assert_set_eq!(actual_ranges, expected_ranges);
assert_set_eq!(asserted_ranges, actual_ranges);
} }
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) { pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); let expected_ranges = self.ranges(marked_text);
assert_eq!(unmarked, self.buffer_text());
let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap();
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
let actual_ranges: Vec<Range<usize>> = snapshot let actual_ranges: Vec<Range<usize>> = snapshot
.display_snapshot
.highlight_ranges::<Tag>() .highlight_ranges::<Tag>()
.map(|ranges| ranges.as_ref().clone().1) .map(|ranges| ranges.as_ref().clone().1)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .into_iter()
.map(|range| range.to_offset(&snapshot.buffer_snapshot)) .map(|range| range.to_offset(&snapshot.buffer_snapshot))
.collect(); .collect();
assert_set_eq!(actual_ranges, expected_ranges);
assert_set_eq!(asserted_ranges, actual_ranges);
} }
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Selection<usize>>) { pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
let mut empty_selections = Vec::new(); let expected_marked_text =
let mut reverse_selections = Vec::new(); generate_marked_text(&self.buffer_text(), &expected_selections, true);
let mut forward_selections = Vec::new(); self.assert_selections(expected_selections, expected_marked_text)
for selection in expected_selections {
let range = selection.range();
if selection.is_empty() {
empty_selections.push(range);
} else if selection.reversed {
reverse_selections.push(range);
} else {
forward_selections.push(range)
}
}
self.assert_selections(
empty_selections,
reverse_selections,
forward_selections,
None,
)
} }
fn assert_selections( fn assert_selections(
&mut self, &mut self,
expected_empty_selections: Vec<Range<usize>>, expected_selections: Vec<Range<usize>>,
expected_reverse_selections: Vec<Range<usize>>, expected_marked_text: String,
expected_forward_selections: Vec<Range<usize>>,
asserted_text: Option<String>,
) { ) {
let (empty_selections, reverse_selections, forward_selections) = let actual_selections = self
self.editor.read_with(self.cx, |editor, cx| { .editor
let mut empty_selections = Vec::new(); .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
let mut reverse_selections = Vec::new(); .into_iter()
let mut forward_selections = Vec::new(); .map(|s| {
if s.reversed {
for selection in editor.selections.all::<usize>(cx) { s.end..s.start
let range = selection.range(); } else {
if selection.is_empty() { s.start..s.end
empty_selections.push(range);
} else if selection.reversed {
reverse_selections.push(range);
} else {
forward_selections.push(range)
}
} }
})
.collect::<Vec<_>>();
let actual_marked_text =
generate_marked_text(&self.buffer_text(), &actual_selections, true);
if expected_selections != actual_selections {
panic!(
indoc! {"
Editor has unexpected selections.
(empty_selections, reverse_selections, forward_selections) Expected selections:
}); {}
let asserted_selections = asserted_text.unwrap_or_else(|| { Actual selections:
self.insert_markers( {}
&expected_empty_selections, "},
&expected_reverse_selections, expected_marked_text, actual_marked_text,
&expected_forward_selections, );
)
});
let actual_selections =
self.insert_markers(&empty_selections, &reverse_selections, &forward_selections);
let unmarked_text = self.buffer_text();
let all_eq: Result<(), SetEqError<String>> =
set_eq!(expected_empty_selections, empty_selections)
.map_err(|err| {
err.map(|missing| {
let mut error_text = unmarked_text.clone();
error_text.insert(missing.start, '|');
error_text
})
})
.and_then(|_| {
set_eq!(expected_reverse_selections, reverse_selections).map_err(|err| {
err.map(|missing| {
let mut error_text = unmarked_text.clone();
error_text.insert(missing.start, '{');
error_text.insert(missing.end, ']');
error_text
})
})
})
.and_then(|_| {
set_eq!(expected_forward_selections, forward_selections).map_err(|err| {
err.map(|missing| {
let mut error_text = unmarked_text.clone();
error_text.insert(missing.start, '[');
error_text.insert(missing.end, '}');
error_text
})
})
});
match all_eq {
Err(SetEqError::LeftMissing(location_text)) => {
panic!(
indoc! {"
Editor has extra selection
Extra Selection Location:
{}
Asserted selections:
{}
Actual selections:
{}"},
location_text, asserted_selections, actual_selections,
);
}
Err(SetEqError::RightMissing(location_text)) => {
panic!(
indoc! {"
Editor is missing empty selection
Missing Selection Location:
{}
Asserted selections:
{}
Actual selections:
{}"},
location_text, asserted_selections, actual_selections,
);
}
_ => {}
} }
} }
fn insert_markers(
&mut self,
empty_selections: &Vec<Range<usize>>,
reverse_selections: &Vec<Range<usize>>,
forward_selections: &Vec<Range<usize>>,
) -> String {
let mut editor_text_with_selections = self.buffer_text();
let mut selection_marks = BTreeMap::new();
for range in empty_selections {
selection_marks.insert(&range.start, '|');
}
for range in reverse_selections {
selection_marks.insert(&range.start, '{');
selection_marks.insert(&range.end, ']');
}
for range in forward_selections {
selection_marks.insert(&range.start, '[');
selection_marks.insert(&range.end, '}');
}
for (offset, mark) in selection_marks.into_iter().rev() {
editor_text_with_selections.insert(*offset, mark);
}
editor_text_with_selections
}
} }
impl<'a> Deref for EditorTestContext<'a> { impl<'a> Deref for EditorTestContext<'a> {
@ -575,10 +418,8 @@ impl<'a> EditorLspTestContext<'a> {
// Constructs lsp range using a marked string with '[', ']' range delimiters // Constructs lsp range using a marked string with '[', ']' range delimiters
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); let ranges = self.ranges(marked_text);
assert_eq!(unmarked, self.buffer_text()); self.to_lsp_range(ranges[0].clone())
let offset_range = ranges.remove(&('[', ']').into()).unwrap()[0].clone();
self.to_lsp_range(offset_range)
} }
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range { pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {

View file

@ -15,6 +15,9 @@ futures = "0.3"
log = { version = "0.4.16", features = ["kv_unstable_serde"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] }
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
tempdir = { version = "0.3.7", optional = true } tempdir = { version = "0.3.7", optional = true }
serde_json = { version = "1.0", features = [ serde_json = { version = "1.0", features = ["preserve_order"], optional = true }
"preserve_order",
], optional = true } [dev-dependencies]
rand = { version = "0.8" }
tempdir = { version = "0.3.7" }
serde_json = { version = "1.0", features = ["preserve_order"] }

View file

@ -1,4 +1,4 @@
#[cfg(feature = "test-support")] #[cfg(any(test, feature = "test-support"))]
pub mod test; pub mod test;
use futures::Future; use futures::Future;

View file

@ -1,6 +1,8 @@
use std::{collections::HashMap, ops::Range}; use std::{cmp::Ordering, collections::HashMap, ops::Range};
pub fn marked_text_by( /// Construct a string and a list of offsets within that string using a single
/// string containing embedded position markers.
pub fn marked_text_offsets_by(
marked_text: &str, marked_text: &str,
markers: Vec<char>, markers: Vec<char>,
) -> (String, HashMap<char, Vec<usize>>) { ) -> (String, HashMap<char, Vec<usize>>) {
@ -19,9 +21,196 @@ pub fn marked_text_by(
(unmarked_text, extracted_markers) (unmarked_text, extracted_markers)
} }
pub fn marked_text(marked_text: &str) -> (String, Vec<usize>) { /// Construct a string and a list of ranges within that string using a single
let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['|']); /// string containing embedded range markers, using arbitrary characters as
(unmarked_text, markers.remove(&'|').unwrap_or_default()) /// range markers. By using multiple different range markers, you can construct
/// ranges that overlap each other.
///
/// The returned ranges will be grouped by their range marking characters.
pub fn marked_text_ranges_by(
marked_text: &str,
markers: Vec<TextRangeMarker>,
) -> (String, HashMap<TextRangeMarker, Vec<Range<usize>>>) {
let all_markers = markers.iter().flat_map(|m| m.markers()).collect();
let (unmarked_text, mut marker_offsets) = marked_text_offsets_by(marked_text, all_markers);
let range_lookup = markers
.into_iter()
.map(|marker| {
(
marker.clone(),
match marker {
TextRangeMarker::Empty(empty_marker_char) => marker_offsets
.remove(&empty_marker_char)
.unwrap_or_default()
.into_iter()
.map(|empty_index| empty_index..empty_index)
.collect::<Vec<Range<usize>>>(),
TextRangeMarker::Range(start_marker, end_marker) => {
let starts = marker_offsets.remove(&start_marker).unwrap_or_default();
let ends = marker_offsets.remove(&end_marker).unwrap_or_default();
assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced");
starts
.into_iter()
.zip(ends)
.map(|(start, end)| {
assert!(end >= start, "marked ranges must be disjoint");
start..end
})
.collect::<Vec<Range<usize>>>()
}
TextRangeMarker::ReverseRange(start_marker, end_marker) => {
let starts = marker_offsets.remove(&start_marker).unwrap_or_default();
let ends = marker_offsets.remove(&end_marker).unwrap_or_default();
assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced");
starts
.into_iter()
.zip(ends)
.map(|(start, end)| {
assert!(end >= start, "marked ranges must be disjoint");
end..start
})
.collect::<Vec<Range<usize>>>()
}
},
)
})
.collect();
(unmarked_text, range_lookup)
}
/// Construct a string and a list of ranges within that string using a single
/// string containing embedded range markers. The characters used to mark the
/// ranges are as follows:
///
/// 1. To mark a range of text, surround it with the `«` and `»` angle brackets,
/// which can be typed on a US keyboard with the `alt-|` and `alt-shift-|` keys.
///
/// ```
/// foo «selected text» bar
/// ```
///
/// 2. To mark a single position in the text, use the `ˇ` caron,
/// which can be typed on a US keyboard with the `alt-shift-t` key.
///
/// ```
/// the cursors are hereˇ and hereˇ.
/// ```
///
/// 3. To mark a range whose direction is meaningful (like a selection),
/// put a caron character beside one of its bounds, on the inside:
///
/// ```
/// one «ˇreversed» selection and one «forwardˇ» selection
/// ```
pub fn marked_text_ranges(
marked_text: &str,
ranges_are_directed: bool,
) -> (String, Vec<Range<usize>>) {
let mut unmarked_text = String::with_capacity(marked_text.len());
let mut ranges = Vec::new();
let mut prev_marked_ix = 0;
let mut current_range_start = None;
let mut current_range_cursor = None;
for (marked_ix, marker) in marked_text.match_indices(&['«', '»', 'ˇ']) {
unmarked_text.push_str(&marked_text[prev_marked_ix..marked_ix]);
let unmarked_len = unmarked_text.len();
let len = marker.len();
prev_marked_ix = marked_ix + len;
match marker {
"ˇ" => {
if current_range_start.is_some() {
if current_range_cursor.is_some() {
panic!("duplicate point marker 'ˇ' at index {marked_ix}");
} else {
current_range_cursor = Some(unmarked_len);
}
} else {
ranges.push(unmarked_len..unmarked_len);
}
}
"«" => {
if current_range_start.is_some() {
panic!("unexpected range start marker '«' at index {marked_ix}");
}
current_range_start = Some(unmarked_len);
}
"»" => {
let current_range_start = if let Some(start) = current_range_start.take() {
start
} else {
panic!("unexpected range end marker '»' at index {marked_ix}");
};
let mut reversed = false;
if let Some(current_range_cursor) = current_range_cursor.take() {
if current_range_cursor == current_range_start {
reversed = true;
} else if current_range_cursor != unmarked_len {
panic!("unexpected 'ˇ' marker in the middle of a range");
}
} else if ranges_are_directed {
panic!("missing 'ˇ' marker to indicate range direction");
}
ranges.push(if reversed {
unmarked_len..current_range_start
} else {
current_range_start..unmarked_len
});
}
_ => unreachable!(),
}
}
unmarked_text.push_str(&marked_text[prev_marked_ix..]);
(unmarked_text, ranges)
}
pub fn marked_text_offsets(marked_text: &str) -> (String, Vec<usize>) {
let (text, ranges) = marked_text_ranges(marked_text, false);
(
text,
ranges
.into_iter()
.map(|range| {
assert_eq!(range.start, range.end);
range.start
})
.collect(),
)
}
pub fn generate_marked_text(
unmarked_text: &str,
ranges: &[Range<usize>],
indicate_cursors: bool,
) -> String {
let mut marked_text = unmarked_text.to_string();
for range in ranges.iter().rev() {
if indicate_cursors {
match range.start.cmp(&range.end) {
Ordering::Less => {
marked_text.insert_str(range.end, "ˇ»");
marked_text.insert_str(range.start, "«");
}
Ordering::Equal => {
marked_text.insert_str(range.start, "ˇ");
}
Ordering::Greater => {
marked_text.insert_str(range.start, "»");
marked_text.insert_str(range.end, "«ˇ");
}
}
} else {
marked_text.insert_str(range.end, "»");
marked_text.insert_str(range.start, "«");
}
}
marked_text
} }
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
@ -53,75 +242,24 @@ impl From<(char, char)> for TextRangeMarker {
} }
} }
pub fn marked_text_ranges_by( #[cfg(test)]
marked_text: &str, mod tests {
markers: Vec<TextRangeMarker>, use super::{generate_marked_text, marked_text_ranges};
) -> (String, HashMap<TextRangeMarker, Vec<Range<usize>>>) {
let all_markers = markers.iter().flat_map(|m| m.markers()).collect();
let (unmarked_text, mut marker_offsets) = marked_text_by(marked_text, all_markers); #[test]
let range_lookup = markers fn test_marked_text() {
.into_iter() let (text, ranges) = marked_text_ranges("one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six", true);
.map(|marker| match marker {
TextRangeMarker::Empty(empty_marker_char) => {
let ranges = marker_offsets
.remove(&empty_marker_char)
.unwrap_or_default()
.into_iter()
.map(|empty_index| empty_index..empty_index)
.collect::<Vec<Range<usize>>>();
(marker, ranges)
}
TextRangeMarker::Range(start_marker, end_marker) => {
let starts = marker_offsets.remove(&start_marker).unwrap_or_default();
let ends = marker_offsets.remove(&end_marker).unwrap_or_default();
assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced");
let ranges = starts assert_eq!(text, "one two three four five six");
.into_iter() assert_eq!(ranges.len(), 4);
.zip(ends) assert_eq!(ranges[0], 7..4);
.map(|(start, end)| { assert_eq!(ranges[1], 8..13);
assert!(end >= start, "marked ranges must be disjoint"); assert_eq!(ranges[2], 18..14);
start..end assert_eq!(ranges[3], 23..23);
})
.collect::<Vec<Range<usize>>>();
(marker, ranges)
}
TextRangeMarker::ReverseRange(start_marker, end_marker) => {
let starts = marker_offsets.remove(&start_marker).unwrap_or_default();
let ends = marker_offsets.remove(&end_marker).unwrap_or_default();
assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced");
let ranges = starts assert_eq!(
.into_iter() generate_marked_text(&text, &ranges, true),
.zip(ends) "one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six"
.map(|(start, end)| { );
assert!(end >= start, "marked ranges must be disjoint"); }
end..start
})
.collect::<Vec<Range<usize>>>();
(marker, ranges)
}
})
.collect();
(unmarked_text, range_lookup)
}
// Returns ranges delimited by (), [], and <> ranges. Ranges using the same markers
// must not be overlapping. May also include | for empty ranges
pub fn marked_text_ranges(full_marked_text: &str) -> (String, Vec<Range<usize>>) {
let (unmarked, range_lookup) = marked_text_ranges_by(
&full_marked_text,
vec![
'|'.into(),
('[', ']').into(),
('(', ')').into(),
('<', '>').into(),
],
);
let mut combined_ranges: Vec<_> = range_lookup.into_values().flatten().collect();
combined_ranges.sort_by_key(|range| range.start);
(unmarked, combined_ranges)
} }

View file

@ -34,9 +34,9 @@ mod test {
cx.simulate_keystroke("i"); cx.simulate_keystroke("i");
assert_eq!(cx.mode(), Mode::Insert); assert_eq!(cx.mode(), Mode::Insert);
cx.simulate_keystrokes(["T", "e", "s", "t"]); cx.simulate_keystrokes(["T", "e", "s", "t"]);
cx.assert_editor_state("Test|"); cx.assert_editor_state("Testˇ");
cx.simulate_keystroke("escape"); cx.simulate_keystroke("escape");
assert_eq!(cx.mode(), Mode::Normal); assert_eq!(cx.mode(), Mode::Normal);
cx.assert_editor_state("Tes|t"); cx.assert_editor_state("Tesˇt");
} }
} }

View file

@ -297,8 +297,7 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext<Workspace>) {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use indoc::indoc; use indoc::indoc;
use language::Selection; use util::test::marked_text_offsets;
use util::test::marked_text;
use crate::{ use crate::{
state::{ state::{
@ -312,15 +311,15 @@ mod test {
async fn test_h(cx: &mut gpui::TestAppContext) { async fn test_h(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["h"]); let mut cx = cx.binding(["h"]);
cx.assert("The q|uick", "The |quick"); cx.assert("The qˇuick", "The ˇquick");
cx.assert("|The quick", "|The quick"); cx.assert("ˇThe quick", "ˇThe quick");
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
); );
} }
@ -328,15 +327,15 @@ mod test {
async fn test_backspace(cx: &mut gpui::TestAppContext) { async fn test_backspace(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["backspace"]); let mut cx = cx.binding(["backspace"]);
cx.assert("The q|uick", "The |quick"); cx.assert("The qˇuick", "The ˇquick");
cx.assert("|The quick", "|The quick"); cx.assert("ˇThe quick", "ˇThe quick");
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
); );
} }
@ -346,35 +345,35 @@ mod test {
let mut cx = cx.binding(["j"]); let mut cx = cx.binding(["j"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick The ˇquick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
brow|n fox"}, browˇn fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brow|n fox"}, browˇn fox"},
indoc! {" indoc! {"
The quick The quick
brow|n fox"}, browˇn fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
indoc! {" indoc! {"
The quick The quick
brow|n"}, browˇn"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
indoc! {" indoc! {"
The quick The quick
|brown"}, ˇbrown"},
); );
} }
@ -384,26 +383,26 @@ mod test {
let mut cx = cx.binding(["k"]); let mut cx = cx.binding(["k"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick The ˇquick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The |quick The ˇquick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brow|n fox"}, browˇn fox"},
indoc! {" indoc! {"
The |quick The ˇquick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The The
quic|k"}, quicˇk"},
indoc! {" indoc! {"
Th|e Thˇe
quick"}, quick"},
); );
} }
@ -412,14 +411,14 @@ mod test {
async fn test_l(cx: &mut gpui::TestAppContext) { async fn test_l(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["l"]); let mut cx = cx.binding(["l"]);
cx.assert("The q|uick", "The qu|ick"); cx.assert("The qˇuick", "The quˇick");
cx.assert("The quic|k", "The quic|k"); cx.assert("The quicˇk", "The quicˇk");
cx.assert( cx.assert(
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
); );
} }
@ -428,42 +427,42 @@ mod test {
async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) { async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["$"]); let mut cx = cx.binding(["$"]);
cx.assert("T|est test", "Test tes|t"); cx.assert("Tˇest test", "Test tesˇt");
cx.assert("Test tes|t", "Test tes|t"); cx.assert("Test tesˇt", "Test tesˇt");
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick The ˇquick
brown"}, brown"},
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
indoc! {" indoc! {"
The quic|k The quicˇk
brown"}, brown"},
); );
let mut cx = cx.binding(["0"]); let mut cx = cx.binding(["0"]);
cx.assert("Test |test", "|Test test"); cx.assert("Test ˇtest", "ˇTest test");
cx.assert("|Test test", "|Test test"); cx.assert("ˇTest test", "ˇTest test");
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick The ˇquick
brown"}, brown"},
indoc! {" indoc! {"
|The quick ˇThe quick
brown"}, brown"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
|The quick ˇThe quick
brown"}, brown"},
indoc! {" indoc! {"
|The quick ˇThe quick
brown"}, brown"},
); );
} }
@ -475,7 +474,7 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick The ˇquick
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
@ -483,54 +482,54 @@ mod test {
The quick The quick
brown fox jumps brown fox jumps
over| the lazy dog"}, overˇ the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox jumps brown fox jumps
over| the lazy dog"}, overˇ the lazy dog"},
indoc! {" indoc! {"
The quick The quick
brown fox jumps brown fox jumps
over| the lazy dog"}, overˇ the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The qui|ck The quiˇck
brown"}, brown"},
indoc! {" indoc! {"
The quick The quick
brow|n"}, browˇn"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The qui|ck The quiˇck
"}, "},
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
} }
#[gpui::test] #[gpui::test]
async fn test_w(cx: &mut gpui::TestAppContext) { async fn test_w(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await; let mut cx = VimTestContext::new(cx, true).await;
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
The |quick|-|brown The ˇquickˇ-ˇbrown
| ˇ
| ˇ
|fox_jumps |over ˇfox_jumps ˇover
|th||e"}); ˇthˇˇe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
|The quick-brown ˇThe quick-brown
fox_jumps over fox_jumps over
@ -540,19 +539,19 @@ mod test {
for cursor_offset in cursor_offsets { for cursor_offset in cursor_offsets {
cx.simulate_keystroke("w"); cx.simulate_keystroke("w");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
// Reset and test ignoring punctuation // Reset and test ignoring punctuation
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
The |quick-brown The ˇquick-brown
| ˇ
| ˇ
|fox_jumps |over ˇfox_jumps ˇover
|th||e"}); ˇthˇˇe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
|The quick-brown ˇThe quick-brown
fox_jumps over fox_jumps over
@ -562,22 +561,22 @@ mod test {
for cursor_offset in cursor_offsets { for cursor_offset in cursor_offsets {
cx.simulate_keystroke("shift-w"); cx.simulate_keystroke("shift-w");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
} }
#[gpui::test] #[gpui::test]
async fn test_e(cx: &mut gpui::TestAppContext) { async fn test_e(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await; let mut cx = VimTestContext::new(cx, true).await;
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
Th|e quic|k|-brow|n Thˇe quicˇkˇ-browˇn
fox_jump|s ove|r fox_jumpˇs oveˇr
th|e"}); thˇe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
|The quick-brown ˇThe quick-brown
fox_jumps over fox_jumps over
@ -587,19 +586,19 @@ mod test {
for cursor_offset in cursor_offsets { for cursor_offset in cursor_offsets {
cx.simulate_keystroke("e"); cx.simulate_keystroke("e");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
// Reset and test ignoring punctuation // Reset and test ignoring punctuation
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
Th|e quick-brow|n Thˇe quick-browˇn
fox_jump|s ove|r fox_jumpˇs oveˇr
th||e"}); thˇˇe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
|The quick-brown ˇThe quick-brown
fox_jumps over fox_jumps over
@ -608,53 +607,53 @@ mod test {
); );
for cursor_offset in cursor_offsets { for cursor_offset in cursor_offsets {
cx.simulate_keystroke("shift-e"); cx.simulate_keystroke("shift-e");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
} }
#[gpui::test] #[gpui::test]
async fn test_b(cx: &mut gpui::TestAppContext) { async fn test_b(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await; let mut cx = VimTestContext::new(cx, true).await;
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
||The |quick|-|brown ˇˇThe ˇquickˇ-ˇbrown
| ˇ
| ˇ
|fox_jumps |over ˇfox_jumps ˇover
|the"}); ˇthe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick-brown The quick-brown
fox_jumps over fox_jumps over
th|e"}, thˇe"},
Mode::Normal, Mode::Normal,
); );
for cursor_offset in cursor_offsets.into_iter().rev() { for cursor_offset in cursor_offsets.into_iter().rev() {
cx.simulate_keystroke("b"); cx.simulate_keystroke("b");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
// Reset and test ignoring punctuation // Reset and test ignoring punctuation
let (_, cursor_offsets) = marked_text(indoc! {" let (_, cursor_offsets) = marked_text_offsets(indoc! {"
||The |quick-brown ˇˇThe ˇquick-brown
| ˇ
| ˇ
|fox_jumps |over ˇfox_jumps ˇover
|the"}); ˇthe"});
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick-brown The quick-brown
fox_jumps over fox_jumps over
th|e"}, thˇe"},
Mode::Normal, Mode::Normal,
); );
for cursor_offset in cursor_offsets.into_iter().rev() { for cursor_offset in cursor_offsets.into_iter().rev() {
cx.simulate_keystroke("shift-b"); cx.simulate_keystroke("shift-b");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]); cx.assert_editor_selections(vec![cursor_offset..cursor_offset]);
} }
} }
@ -683,21 +682,21 @@ mod test {
The quick The quick
brown fox jumps brown fox jumps
over |the lazy dog"}, over ˇthe lazy dog"},
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
@ -707,9 +706,9 @@ mod test {
The quick The quick
brown fox jumps brown fox jumps
over the la|zy dog"}, over the laˇzy dog"},
indoc! {" indoc! {"
The quic|k The quicˇk
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
@ -719,9 +718,9 @@ mod test {
brown fox jumps brown fox jumps
over the la|zy dog"}, over the laˇzy dog"},
indoc! {" indoc! {"
| ˇ
brown fox jumps brown fox jumps
over the lazy dog"}, over the lazy dog"},
@ -733,31 +732,31 @@ mod test {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["a"]).mode_after(Mode::Insert); let mut cx = cx.binding(["a"]).mode_after(Mode::Insert);
cx.assert("The q|uick", "The qu|ick"); cx.assert("The qˇuick", "The quˇick");
cx.assert("The quic|k", "The quick|"); cx.assert("The quicˇk", "The quickˇ");
} }
#[gpui::test] #[gpui::test]
async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) { async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-a"]).mode_after(Mode::Insert); let mut cx = cx.binding(["shift-a"]).mode_after(Mode::Insert);
cx.assert("The q|uick", "The quick|"); cx.assert("The qˇuick", "The quickˇ");
cx.assert("The q|uick ", "The quick |"); cx.assert("The qˇuick ", "The quick ˇ");
cx.assert("|", "|"); cx.assert("ˇ", "ˇ");
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick| The quickˇ
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
); );
} }
@ -766,50 +765,50 @@ mod test {
async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) { async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["^"]); let mut cx = cx.binding(["^"]);
cx.assert("The q|uick", "|The quick"); cx.assert("The qˇuick", "ˇThe quick");
cx.assert(" The q|uick", " |The quick"); cx.assert(" The qˇuick", " ˇThe quick");
cx.assert("|", "|"); cx.assert("ˇ", "ˇ");
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
|The quick ˇThe quick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
); );
// Indoc disallows trailing whitspace. // Indoc disallows trailing whitspace.
cx.assert(" | \nThe quick", " | \nThe quick"); cx.assert(" ˇ \nThe quick", " ˇ \nThe quick");
} }
#[gpui::test] #[gpui::test]
async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) { async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-i"]).mode_after(Mode::Insert); let mut cx = cx.binding(["shift-i"]).mode_after(Mode::Insert);
cx.assert("The q|uick", "|The quick"); cx.assert("The qˇuick", "ˇThe quick");
cx.assert(" The q|uick", " |The quick"); cx.assert(" The qˇuick", " ˇThe quick");
cx.assert("|", "|"); cx.assert("ˇ", "ˇ");
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
|The quick ˇThe quick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
); );
} }
@ -820,20 +819,20 @@ mod test {
let mut cx = cx.binding(["shift-d"]); let mut cx = cx.binding(["shift-d"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The |q The ˇq
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -842,15 +841,15 @@ mod test {
async fn test_x(cx: &mut gpui::TestAppContext) { async fn test_x(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["x"]); let mut cx = cx.binding(["x"]);
cx.assert("|Test", "|est"); cx.assert("ˇTest", "ˇest");
cx.assert("Te|st", "Te|t"); cx.assert("Teˇst", "Teˇt");
cx.assert("Tes|t", "Te|s"); cx.assert("Tesˇt", "Teˇs");
cx.assert( cx.assert(
indoc! {" indoc! {"
Tes|t Tesˇt
test"}, test"},
indoc! {" indoc! {"
Te|s Teˇs
test"}, test"},
); );
} }
@ -859,16 +858,16 @@ mod test {
async fn test_delete_left(cx: &mut gpui::TestAppContext) { async fn test_delete_left(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-x"]); let mut cx = cx.binding(["shift-x"]);
cx.assert("Te|st", "T|st"); cx.assert("Teˇst", "st");
cx.assert("T|est", "|est"); cx.assert("Tˇest", "ˇest");
cx.assert("|Test", "|Test"); cx.assert("ˇTest", "ˇTest");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
); );
} }
@ -878,78 +877,84 @@ mod test {
let mut cx = cx.binding(["o"]).mode_after(Mode::Insert); let mut cx = cx.binding(["o"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
"|", "ˇ",
indoc! {" indoc! {"
|"}, ˇ"},
); );
cx.assert( cx.assert(
"The |quick", "The ˇquick",
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
| ˇ
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
fn test() fn test() {
println!(|);"}, println!(ˇ);
}
"},
indoc! {" indoc! {"
fn test() fn test() {
println!(); println!();
|"}, ˇ
}
"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
fn test(|) fn test(ˇ) {
println!();"}, println!();
}"},
indoc! {" indoc! {"
fn test() fn test() {
| ˇ
println!();"}, println!();
}"},
); );
} }
@ -959,25 +964,25 @@ mod test {
let mut cx = cx.binding(["shift-o"]).mode_after(Mode::Insert); let mut cx = cx.binding(["shift-o"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
"|", "ˇ",
indoc! {" indoc! {"
| ˇ
"}, "},
); );
cx.assert( cx.assert(
"The |quick", "The ˇquick",
indoc! {" indoc! {"
| ˇ
The quick"}, The quick"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
); );
@ -985,20 +990,20 @@ mod test {
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
| ˇ
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
The quick The quick
brown fox brown fox
jumps over"}, jumps over"},
@ -1006,31 +1011,33 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
fn test() fn test()
println!(|);"}, println!(ˇ);"},
indoc! {" indoc! {"
fn test() fn test()
| ˇ
println!();"}, println!();"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
fn test(|) fn test(ˇ) {
println!();"}, println!();
}"},
indoc! {" indoc! {"
| ˇ
fn test() fn test() {
println!();"}, println!();
}"},
); );
} }
@ -1039,43 +1046,43 @@ mod test {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "d"]); let mut cx = cx.binding(["d", "d"]);
cx.assert("|", "|"); cx.assert("ˇ", "ˇ");
cx.assert("The |quick", "|"); cx.assert("The ˇquick", "ˇ");
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
jumps |over"}, jumps ˇover"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown |fox"}, brown ˇfox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
brown| fox brownˇ fox
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
|brown fox"}, ˇbrown fox"},
); );
} }
@ -1084,46 +1091,46 @@ mod test {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert);
cx.assert("|", "|"); cx.assert("ˇ", "ˇ");
cx.assert("The |quick", "|"); cx.assert("The ˇquick", "ˇ");
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
| ˇ
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -1134,7 +1141,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );
@ -1142,21 +1149,21 @@ mod test {
cx.simulate_keystrokes(["d", "d"]); cx.simulate_keystrokes(["d", "d"]);
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
The quick brown The quick brown
the la|zy dog"}); the laˇzy dog"});
cx.simulate_keystroke("p"); cx.simulate_keystroke("p");
cx.assert_state( cx.assert_state(
indoc! {" indoc! {"
The quick brown The quick brown
the lazy dog the lazy dog
|fox jumps over"}, ˇfox jumps over"},
Mode::Normal, Mode::Normal,
); );
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox [jump}s over fox «jumpˇ»s over
the lazy dog"}, the lazy dog"},
Mode::Visual { line: false }, Mode::Visual { line: false },
); );
@ -1164,7 +1171,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps ove|r fox jumps oveˇr
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );
@ -1172,7 +1179,7 @@ mod test {
cx.assert_state( cx.assert_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over|jumps fox jumps overˇjumps
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );

View file

@ -84,16 +84,16 @@ mod 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "h"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "h"]).mode_after(Mode::Insert);
cx.assert("Te|st", "T|st"); cx.assert("Teˇst", "st");
cx.assert("T|est", "|est"); cx.assert("Tˇest", "ˇest");
cx.assert("|Test", "|Test"); cx.assert("ˇTest", "ˇTest");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
); );
} }
@ -101,111 +101,111 @@ mod 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "l"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "l"]).mode_after(Mode::Insert);
cx.assert("Te|st", "Te|t"); cx.assert("Teˇst", "Teˇt");
cx.assert("Tes|t", "Tes|"); 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "w"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "w"]).mode_after(Mode::Insert);
cx.assert("Te|st", "Te|"); cx.assert("Teˇst", "Teˇ");
cx.assert("T|est test", "T| test"); cx.assert("Tˇest test", " test");
cx.assert("Test| test", "Test|test"); cx.assert("Testˇ test", "Testˇtest");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test te|st Test teˇst
test"}, test"},
indoc! {" indoc! {"
Test te| Test teˇ
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test tes|t Test tesˇt
test"}, test"},
indoc! {" indoc! {"
Test tes| Test tesˇ
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
indoc! {" indoc! {"
Test test Test 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", "Test teˇ test");
} }
#[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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "e"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "e"]).mode_after(Mode::Insert);
cx.assert("Te|st Test", "Te| Test"); cx.assert("Teˇst Test", "Teˇ Test");
cx.assert("T|est test", "T| test"); cx.assert("Tˇest test", " test");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test te|st Test teˇst
test"}, test"},
indoc! {" indoc! {"
Test te| Test teˇ
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test tes|t Test tesˇt
test"}, test"},
"Test tes|", "Test tesˇ",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
indoc! {" indoc! {"
Test test Test test
| ˇ
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", "Test teˇ test");
} }
#[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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "b"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "b"]).mode_after(Mode::Insert);
cx.assert("Te|st Test", "|st Test"); cx.assert("Teˇst Test", "ˇst Test");
cx.assert("Test |test", "|test"); cx.assert("Test ˇtest", "ˇtest");
cx.assert("Test1 test2 |test3", "Test1 |test3"); cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
|test"}, ˇtest"},
indoc! {" indoc! {"
Test | Test ˇ
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
indoc! {" indoc! {"
Test | Test ˇ
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", "Test ˇtest");
} }
#[gpui::test] #[gpui::test]
@ -214,20 +214,20 @@ mod test {
let mut cx = cx.binding(["c", "$"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "$"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The q| The qˇ
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -238,20 +238,20 @@ mod test {
let mut cx = cx.binding(["c", "0"]).mode_after(Mode::Insert); let mut cx = cx.binding(["c", "0"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
|uick ˇuick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -263,38 +263,38 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over"}, jumps over"},
); );
@ -307,40 +307,40 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
| ˇ
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|"}, ˇ"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|"}, ˇ"},
); );
} }
@ -351,46 +351,46 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
The quick The quick
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the l|azy"}, the lˇazy"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
|"}, ˇ"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
|"}, ˇ"},
); );
} }
@ -401,11 +401,11 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
| ˇ
jumps over jumps over
the lazy"}, the lazy"},
); );
@ -414,29 +414,29 @@ mod test {
The quick The quick
brown fox brown fox
jumps over jumps over
the l|azy"}, the lˇazy"},
"|", "ˇ",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},

View file

@ -46,16 +46,16 @@ mod 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "h"]); let mut cx = cx.binding(["d", "h"]);
cx.assert("Te|st", "T|st"); cx.assert("Teˇst", "st");
cx.assert("T|est", "|est"); cx.assert("Tˇest", "ˇest");
cx.assert("|Test", "|Test"); cx.assert("ˇTest", "ˇTest");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
indoc! {" indoc! {"
Test Test
|test"}, ˇtest"},
); );
} }
@ -63,15 +63,15 @@ mod 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "l"]); let mut cx = cx.binding(["d", "l"]);
cx.assert("|Test", "|est"); cx.assert("ˇTest", "ˇest");
cx.assert("Te|st", "Te|t"); cx.assert("Teˇst", "Teˇt");
cx.assert("Tes|t", "Te|s"); cx.assert("Tesˇt", "Teˇs");
cx.assert( cx.assert(
indoc! {" indoc! {"
Tes|t Tesˇt
test"}, test"},
indoc! {" indoc! {"
Te|s Teˇs
test"}, test"},
); );
} }
@ -80,104 +80,104 @@ mod 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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "w"]); let mut cx = cx.binding(["d", "w"]);
cx.assert("Te|st", "T|e"); cx.assert("Teˇst", "e");
cx.assert("T|est test", "T|test"); cx.assert("Tˇest test", "test");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test te|st Test teˇst
test"}, test"},
indoc! {" indoc! {"
Test t|e Test tˇe
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test tes|t Test tesˇt
test"}, test"},
indoc! {" indoc! {"
Test te|s Test teˇs
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, 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", "Test teˇtest");
} }
#[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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "e"]); let mut cx = cx.binding(["d", "e"]);
cx.assert("Te|st Test", "Te| Test"); cx.assert("Teˇst Test", "Teˇ Test");
cx.assert("T|est test", "T| test"); cx.assert("Tˇest test", " test");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test te|st Test teˇst
test"}, test"},
indoc! {" indoc! {"
Test t|e Test tˇe
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test tes|t Test tesˇt
test"}, test"},
"Test te|s", "Test teˇs",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
); );
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", "Test teˇ test");
} }
#[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 cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "b"]); let mut cx = cx.binding(["d", "b"]);
cx.assert("Te|st Test", "|st Test"); cx.assert("Teˇst Test", "ˇst Test");
cx.assert("Test |test", "|test"); cx.assert("Test ˇtest", "ˇtest");
cx.assert("Test1 test2 |test3", "Test1 |test3"); cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3");
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
|test"}, ˇtest"},
// Trailing whitespace after cursor // Trailing whitespace after cursor
indoc! {" indoc! {"
Test| Testˇ
test"}, test"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
Test test Test test
| ˇ
test"}, test"},
// Trailing whitespace after cursor // Trailing whitespace after cursor
indoc! {" indoc! {"
Test| Testˇ
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", "Test ˇtest");
} }
#[gpui::test] #[gpui::test]
@ -186,20 +186,20 @@ mod test {
let mut cx = cx.binding(["d", "$"]); let mut cx = cx.binding(["d", "$"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
The |q The ˇq
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -210,20 +210,20 @@ mod test {
let mut cx = cx.binding(["d", "0"]); let mut cx = cx.binding(["d", "0"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox"}, brown fox"},
indoc! {" indoc! {"
|uick ˇuick
brown fox"}, brown fox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
indoc! {" indoc! {"
The quick The quick
| ˇ
brown fox"}, brown fox"},
); );
} }
@ -235,31 +235,31 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
"jumps |over", "jumps ˇover",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
"The qu|ick", "The quˇick",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
indoc! {" indoc! {"
brown| fox brownˇ fox
jumps over"}, jumps over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
|brown fox ˇbrown fox
jumps over"}, jumps over"},
"|jumps over", "ˇjumps over",
); );
} }
@ -270,34 +270,34 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown |fox brown ˇfox
jumps over"}, jumps over"},
"The qu|ick", "The quˇick",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps |over"}, jumps ˇover"},
indoc! {" indoc! {"
The quick The quick
brown |fox"}, brown ˇfox"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over"}, jumps over"},
"jumps| over", "jumpsˇ over",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|"}, ˇ"},
indoc! {" indoc! {"
The quick The quick
|brown fox"}, ˇbrown fox"},
); );
} }
@ -308,40 +308,40 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
"The q|uick", "The qˇuick",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
"The q|uick", "The qˇuick",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
the l|azy"}, the lˇazy"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps| over"}, jumpsˇ over"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
jumps over jumps over
|"}, ˇ"},
indoc! {" indoc! {"
The quick The quick
brown fox brown fox
|jumps over"}, ˇjumps over"},
); );
} }
@ -352,11 +352,11 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick The quick
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
jumps| over jumpsˇ over
the lazy"}, the lazy"},
); );
cx.assert( cx.assert(
@ -364,28 +364,28 @@ mod test {
The quick The quick
brown fox brown fox
jumps over jumps over
the l|azy"}, the lˇazy"},
"|", "ˇ",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The q|uick The qˇuick
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
brown| fox brownˇ fox
jumps over jumps over
the lazy"}, the lazy"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
| ˇ
brown fox brown fox
jumps over jumps over
the lazy"}, the lazy"},
indoc! {" indoc! {"
|brown fox ˇbrown fox
jumps over jumps over
the lazy"}, the lazy"},
); );
@ -397,7 +397,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );
@ -407,7 +407,7 @@ mod test {
assert_eq!(cx.active_operator(), None); assert_eq!(cx.active_operator(), None);
assert_eq!(cx.mode(), Mode::Normal); assert_eq!(cx.mode(), Mode::Normal);
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
The qu|ick brown The quˇick brown
fox jumps over fox jumps over
the lazy dog"}); the lazy dog"});
} }
@ -418,7 +418,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );

View file

@ -216,7 +216,7 @@ mod test {
async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, false).await; let mut cx = VimTestContext::new(cx, false).await;
cx.simulate_keystrokes(["h", "j", "k", "l"]); cx.simulate_keystrokes(["h", "j", "k", "l"]);
cx.assert_editor_state("hjkl|"); cx.assert_editor_state("hjklˇ");
} }
#[gpui::test] #[gpui::test]
@ -229,24 +229,24 @@ mod test {
// Editor acts as though vim is disabled // Editor acts as though vim is disabled
cx.disable_vim(); cx.disable_vim();
cx.simulate_keystrokes(["h", "j", "k", "l"]); cx.simulate_keystrokes(["h", "j", "k", "l"]);
cx.assert_editor_state("hjkl|"); cx.assert_editor_state("hjklˇ");
// Selections aren't changed if editor is blurred but vim-mode is still disabled. // Selections aren't changed if editor is blurred but vim-mode is still disabled.
cx.set_state("[hjkl}", Mode::Normal); cx.set_state("«hjklˇ»", Mode::Normal);
cx.assert_editor_state("[hjkl}"); cx.assert_editor_state("«hjklˇ»");
cx.update_editor(|_, cx| cx.blur()); cx.update_editor(|_, cx| cx.blur());
cx.assert_editor_state("[hjkl}"); cx.assert_editor_state("«hjklˇ»");
cx.update_editor(|_, cx| cx.focus_self()); cx.update_editor(|_, cx| cx.focus_self());
cx.assert_editor_state("[hjkl}"); cx.assert_editor_state("«hjklˇ»");
// Enabling dynamically sets vim mode again and restores normal mode // Enabling dynamically sets vim mode again and restores normal mode
cx.enable_vim(); cx.enable_vim();
assert_eq!(cx.mode(), Mode::Normal); assert_eq!(cx.mode(), Mode::Normal);
cx.simulate_keystrokes(["h", "h", "h", "l"]); cx.simulate_keystrokes(["h", "h", "h", "l"]);
assert_eq!(cx.buffer_text(), "hjkl".to_owned()); assert_eq!(cx.buffer_text(), "hjkl".to_owned());
cx.assert_editor_state("h|jkl"); cx.assert_editor_state("hˇjkl");
cx.simulate_keystrokes(["i", "T", "e", "s", "t"]); cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
cx.assert_editor_state("hTest|jkl"); cx.assert_editor_state("hTestˇjkl");
// Disabling and enabling resets to normal mode // Disabling and enabling resets to normal mode
assert_eq!(cx.mode(), Mode::Insert); assert_eq!(cx.mode(), Mode::Insert);
@ -262,7 +262,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );

View file

@ -284,44 +284,44 @@ mod test {
.mode_after(Mode::Visual { line: false }); .mode_after(Mode::Visual { line: false });
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The [quick brown The «quick brown
fox jumps }over fox jumps ˇ»over
the lazy dog"}, the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the [lazy }dog"}, the «lazy ˇ»dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps [over fox jumps «over
}the lazy dog"}, ˇ»the lazy dog"},
); );
let mut cx = cx let mut cx = cx
.binding(["v", "b", "k"]) .binding(["v", "b", "k"])
.mode_after(Mode::Visual { line: false }); .mode_after(Mode::Visual { line: false });
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
{The q]uick brown «ˇThe q»uick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -329,20 +329,20 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
{fox jumps over «ˇfox jumps over
the l]azy dog"}, the l»azy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The {quick brown The «ˇquick brown
fox jumps o]ver fox jumps o»ver
the lazy dog"}, the lazy dog"},
); );
} }
@ -351,51 +351,51 @@ mod test {
async fn test_visual_delete(cx: &mut gpui::TestAppContext) { async fn test_visual_delete(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["v", "w", "x"]); let mut cx = cx.binding(["v", "w", "x"]);
cx.assert("The quick |brown", "The quick| "); cx.assert("The quick ˇbrown", "The quickˇ ");
let mut cx = cx.binding(["v", "w", "j", "x"]); let mut cx = cx.binding(["v", "w", "j", "x"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |ver The ˇver
the lazy dog"}, the lazy dog"},
); );
// Test pasting code copied on delete // Test pasting code copied on delete
cx.simulate_keystrokes(["j", "p"]); cx.simulate_keystrokes(["j", "p"]);
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
The ver The ver
the l|quick brown the lˇquick brown
fox jumps oazy dog"}); fox jumps oazy dog"});
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |og"}, the ˇog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |he lazy dog"}, fox jumps ˇhe lazy dog"},
); );
let mut cx = cx.binding(["v", "b", "k", "x"]); let mut cx = cx.binding(["v", "b", "k", "x"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
|uick brown ˇuick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -403,18 +403,18 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
|azy dog"}, ˇazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |ver The ˇver
the lazy dog"}, the lazy dog"},
); );
} }
@ -425,68 +425,68 @@ mod test {
let mut cx = cx.binding(["shift-v", "x"]); let mut cx = cx.binding(["shift-v", "x"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The qu|ick brown The quˇick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
); );
// Test pasting code copied on delete // Test pasting code copied on delete
cx.simulate_keystroke("p"); cx.simulate_keystroke("p");
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
fox jumps over fox jumps over
|The quick brown ˇThe quick brown
the lazy dog"}); the lazy dog"});
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
the la|zy dog"}, the laˇzy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the la|zy dog"}, the laˇzy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over"}, fox juˇmps over"},
); );
let mut cx = cx.binding(["shift-v", "j", "x"]); let mut cx = cx.binding(["shift-v", "j", "x"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The qu|ick brown The quˇick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
"the la|zy dog", "the laˇzy dog",
); );
// Test pasting code copied on delete // Test pasting code copied on delete
cx.simulate_keystroke("p"); cx.simulate_keystroke("p");
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
the lazy dog the lazy dog
|The quick brown ˇThe quick brown
fox jumps over"}); fox jumps over"});
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
"The qu|ick brown", "The quˇick brown",
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the la|zy dog"}, the laˇzy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over"}, fox juˇmps over"},
); );
} }
@ -494,44 +494,44 @@ mod test {
async fn test_visual_change(cx: &mut gpui::TestAppContext) { async fn test_visual_change(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["v", "w", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["v", "w", "c"]).mode_after(Mode::Insert);
cx.assert("The quick |brown", "The quick |"); cx.assert("The quick ˇbrown", "The quick ˇ");
let mut cx = cx.binding(["v", "w", "j", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["v", "w", "j", "c"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |ver The ˇver
the lazy dog"}, the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |og"}, the ˇog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |he lazy dog"}, fox jumps ˇhe lazy dog"},
); );
let mut cx = cx.binding(["v", "b", "k", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["v", "b", "k", "c"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
|uick brown ˇuick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -539,18 +539,18 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
|azy dog"}, ˇazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |ver The ˇver
the lazy dog"}, the lazy dog"},
); );
} }
@ -561,11 +561,11 @@ mod test {
let mut cx = cx.binding(["shift-v", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["shift-v", "c"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The qu|ick brown The quˇick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
| ˇ
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -574,37 +574,37 @@ mod test {
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
fox jumps over fox jumps over
|The quick brown ˇThe quick brown
the lazy dog"}); the lazy dog"});
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
| ˇ
the lazy dog"}, the lazy dog"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the la|zy dog"}, the laˇzy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
|"}, ˇ"},
); );
let mut cx = cx.binding(["shift-v", "j", "c"]).mode_after(Mode::Insert); let mut cx = cx.binding(["shift-v", "j", "c"]).mode_after(Mode::Insert);
cx.assert( cx.assert(
indoc! {" indoc! {"
The qu|ick brown The quˇick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
| ˇ
the lazy dog"}, the lazy dog"},
); );
// Test pasting code copied on delete // Test pasting code copied on delete
@ -612,26 +612,26 @@ mod test {
cx.assert_editor_state(indoc! {" cx.assert_editor_state(indoc! {"
the lazy dog the lazy dog
|The quick brown ˇThe quick brown
fox jumps over"}); fox jumps over"});
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
|"}, ˇ"},
); );
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the la|zy dog"}, the laˇzy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
|"}, ˇ"},
); );
} }
@ -639,16 +639,16 @@ mod test {
async fn test_visual_yank(cx: &mut gpui::TestAppContext) { async fn test_visual_yank(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await; let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["v", "w", "y"]); let mut cx = cx.binding(["v", "w", "y"]);
cx.assert("The quick |brown", "The quick |brown"); cx.assert("The quick ˇbrown", "The quick ˇbrown");
cx.assert_clipboard_content(Some("brown")); cx.assert_clipboard_content(Some("brown"));
let mut cx = cx.binding(["v", "w", "j", "y"]); let mut cx = cx.binding(["v", "w", "j", "y"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -659,21 +659,21 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
); );
cx.assert_clipboard_content(Some("lazy d")); cx.assert_clipboard_content(Some("lazy d"));
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
); );
cx.assert_clipboard_content(Some(indoc! {" cx.assert_clipboard_content(Some(indoc! {"
@ -682,11 +682,11 @@ mod test {
let mut cx = cx.binding(["v", "b", "k", "y"]); let mut cx = cx.binding(["v", "b", "k", "y"]);
cx.assert( cx.assert(
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
|The quick brown ˇThe quick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -695,10 +695,10 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps over fox jumps over
the |lazy dog"}, the ˇlazy dog"},
indoc! {" indoc! {"
The quick brown The quick brown
|fox jumps over ˇfox jumps over
the lazy dog"}, the lazy dog"},
); );
cx.assert_clipboard_content(Some(indoc! {" cx.assert_clipboard_content(Some(indoc! {"
@ -707,10 +707,10 @@ mod test {
cx.assert( cx.assert(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps |over fox jumps ˇover
the lazy dog"}, the lazy dog"},
indoc! {" indoc! {"
The |quick brown The ˇquick brown
fox jumps over fox jumps over
the lazy dog"}, the lazy dog"},
); );
@ -725,7 +725,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox [jump}s over fox «jumpˇ»s over
the lazy dog"}, the lazy dog"},
Mode::Visual { line: false }, Mode::Visual { line: false },
); );
@ -733,7 +733,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox jump|s over fox jumpˇs over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );
@ -741,7 +741,7 @@ mod test {
cx.assert_state( cx.assert_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox jumps|jumps over fox jumpsˇjumps over
the lazy dog"}, the lazy dog"},
Mode::Normal, Mode::Normal,
); );
@ -749,7 +749,7 @@ mod test {
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
fox ju|mps over fox juˇmps over
the lazy dog"}, the lazy dog"},
Mode::Visual { line: true }, Mode::Visual { line: true },
); );
@ -757,13 +757,13 @@ mod test {
cx.assert_state( cx.assert_state(
indoc! {" indoc! {"
The quick brown The quick brown
the la|zy dog"}, the laˇzy dog"},
Mode::Normal, Mode::Normal,
); );
cx.set_state( cx.set_state(
indoc! {" indoc! {"
The quick brown The quick brown
the [laz}y dog"}, the «lazˇ»y dog"},
Mode::Visual { line: false }, Mode::Visual { line: false },
); );
cx.simulate_keystroke("p"); cx.simulate_keystroke("p");
@ -771,7 +771,7 @@ mod test {
indoc! {" indoc! {"
The quick brown The quick brown
the the
|fox jumps over ˇfox jumps over
dog"}, dog"},
Mode::Normal, Mode::Normal,
); );