diff --git a/assets/icons/plus_12.svg b/assets/icons/plus_12.svg new file mode 100644 index 0000000000..7997c97c06 --- /dev/null +++ b/assets/icons/plus_12.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/plus_16.svg b/assets/icons/plus_16.svg new file mode 100644 index 0000000000..1fa008ac64 --- /dev/null +++ b/assets/icons/plus_16.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/plus_8.svg b/assets/icons/plus_8.svg new file mode 100644 index 0000000000..9abfd46744 --- /dev/null +++ b/assets/icons/plus_8.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 93a4c372ec..ab5b7155a5 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1170,7 +1170,7 @@ pub mod tests { ); 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)); buffer.condition(&cx, |buf, _| !buf.is_parsing()).await; @@ -1246,28 +1246,28 @@ pub mod tests { } use Bias::{Left, Right}; - assert("||α", false, Left, cx); - assert("||α", true, Left, cx); - assert("||α", false, Right, cx); - assert("|α|", true, Right, cx); - assert("||✋", false, Left, cx); - assert("||✋", true, Left, cx); - assert("||✋", false, Right, cx); - assert("|✋|", true, Right, cx); - assert("||🍐", false, Left, cx); - assert("||🍐", true, Left, cx); - assert("||🍐", false, Right, cx); - assert("|🍐|", true, Right, cx); - assert("||\t", false, Left, cx); - assert("||\t", true, Left, cx); - assert("||\t", false, Right, cx); - assert("|\t|", true, Right, cx); - assert(" ||\t", false, Left, cx); - assert(" ||\t", true, Left, cx); - assert(" ||\t", false, Right, cx); - assert(" |\t|", true, Right, cx); - assert(" ||\t", false, Left, cx); - assert(" ||\t", false, Right, cx); + assert("ˇˇα", false, Left, cx); + assert("ˇˇα", true, Left, cx); + assert("ˇˇα", false, Right, cx); + assert("ˇαˇ", true, Right, cx); + assert("ˇˇ✋", false, Left, cx); + assert("ˇˇ✋", true, Left, cx); + assert("ˇˇ✋", false, Right, cx); + assert("ˇ✋ˇ", true, Right, cx); + assert("ˇˇ🍐", false, Left, cx); + assert("ˇˇ🍐", true, Left, cx); + assert("ˇˇ🍐", false, Right, cx); + assert("ˇ🍐ˇ", true, Right, cx); + assert("ˇˇ\t", false, Left, cx); + assert("ˇˇ\t", true, Left, cx); + assert("ˇˇ\t", false, Right, cx); + assert("ˇ\tˇ", true, Right, cx); + assert(" ˇˇ\t", false, Left, cx); + assert(" ˇˇ\t", true, Left, cx); + assert(" ˇˇ\t", false, Right, cx); + assert(" ˇ\tˇ", true, Right, cx); + assert(" ˇˇ\t", false, Left, cx); + assert(" ˇˇ\t", false, Right, cx); } #[gpui::test] @@ -1283,10 +1283,10 @@ pub mod tests { ); } - assert("||", cx); - assert("|a|", cx); - assert("a|b|", cx); - assert("a|α|", cx); + assert("ˇˇ", cx); + assert("ˇaˇ", cx); + assert("aˇbˇ", cx); + assert("aˇαˇ", cx); } #[gpui::test] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 5211466e94..7c2d560a41 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -76,7 +76,7 @@ const MAX_LINE_LEN: usize = 1024; const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10; const MAX_SELECTION_HISTORY_LEN: usize = 1024; -#[derive(Clone, Deserialize, PartialEq)] +#[derive(Clone, Deserialize, PartialEq, Default)] pub struct SelectNext { #[serde(default)] pub replace_newest: bool, @@ -6644,9 +6644,7 @@ mod tests { use unindent::Unindent; use util::{ assert_set_eq, - test::{ - marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker, - }, + test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker}, }; use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane}; @@ -7044,13 +7042,16 @@ mod tests { #[gpui::test] fn test_clone(cx: &mut gpui::MutableAppContext) { - let (text, selection_ranges) = marked_text_ranges(indoc! {" - one - two - three[] - four - five[] - "}); + let (text, selection_ranges) = marked_text_ranges( + indoc! {" + one + two + threeˇ + four + fiveˇ + "}, + true, + ); cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple(&text, cx); @@ -7732,93 +7733,38 @@ mod tests { }); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_selection_ranges( - "use std::<>str::{foo, bar}\n\n {[]baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_selection_ranges( - "use std<>::str::{foo, bar}\n\n []{baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n ˇ{baz.qux()}", view, cx); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_selection_ranges( - "use <>std::str::{foo, bar}\n\n[] {baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ {baz.qux()}", view, cx); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_selection_ranges( - "<>use std::str::{foo, bar}\n[]\n {baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx); view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_selection_ranges( - "<>use std::str::{foo, bar[]}\n\n {baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n {baz.qux()}", view, cx); view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_selection_ranges( - "use<> std::str::{foo, bar}[]\n\n {baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n {baz.qux()}", view, cx); view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_selection_ranges( - "use std<>::str::{foo, bar}\n[]\n {baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n {baz.qux()}", view, cx); view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_selection_ranges( - "use std::<>str::{foo, bar}\n\n {[]baz.qux()}", - vec![('<', '>'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n {ˇbaz.qux()}", view, cx); view.move_right(&MoveRight, cx); view.select_to_previous_word_start(&SelectToPreviousWordStart, cx); - assert_selection_ranges( - "use std::>s'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx); view.select_to_previous_word_start(&SelectToPreviousWordStart, cx); - assert_selection_ranges( - "use std>::s'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n «ˇ{b»az.qux()}", view, cx); view.select_to_next_word_end(&SelectToNextWordEnd, cx); - assert_selection_ranges( - "use std::>s'), ('[', ']')], - view, - cx, - ); + assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n {«ˇb»az.qux()}", view, cx); }); } @@ -7878,15 +7824,10 @@ mod tests { } #[gpui::test] - fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let (text, ranges) = marked_text_ranges("one [two three] four"); - let buffer = MultiBuffer::build_simple(&text, cx); - - let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); - - editor.update(cx, |editor, cx| { - editor.change_selections(None, cx, |s| s.select_ranges(ranges)); + async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx).await; + cx.set_state("one «two threeˇ» four"); + cx.update_editor(|editor, cx| { editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); assert_eq!(editor.text(cx), " four"); }); @@ -8050,33 +7991,33 @@ mod tests { cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); cx.set_state(indoc! {" - const a: |A = ( - (| - [const_function}(|), - so{m]et[h}ing_|else,| - )| - |);| - "}); + const a: ˇA = ( + (ˇ + «const_functionˇ»(ˇ), + so«mˇ»et«hˇ»ing_ˇelse,ˇ + )ˇ + ˇ);ˇ + "}); cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx)); cx.assert_editor_state(indoc! {" const a: A = ( - | + ˇ ( - | + ˇ const_function(), - | - | + ˇ + ˇ something_else, - | - | - | - | + ˇ + ˇ + ˇ + ˇ ) - | + ˇ ); - | - | - "}); + ˇ + ˇ + "}); } #[gpui::test] @@ -8115,25 +8056,25 @@ mod tests { }); }); cx.set_state(indoc! {" - |ab|c - |🏀|🏀|efg - d| + ˇabˇc + ˇ🏀ˇ🏀ˇefg + dˇ "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - |ab |c - |🏀 |🏀 |efg - d | + ˇab ˇc + ˇ🏀 ˇ🏀 ˇefg + d ˇ "}); cx.set_state(indoc! {" a - [🏀}🏀[🏀}🏀[🏀} + «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" a - [🏀}🏀[🏀}🏀[🏀} + «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» "}); } @@ -8154,26 +8095,26 @@ mod tests { // a soft tab. cursors that are to the left of the suggested indent // auto-indent their line. cx.set_state(indoc! {" - | + ˇ const a: B = ( c( d( - | + ˇ ) - | - | ) + ˇ + ˇ ) ); "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - | + ˇ const a: B = ( c( d( - | + ˇ ) - | - |) + ˇ + ˇ) ); "}); @@ -8181,16 +8122,16 @@ mod tests { cx.set_state(indoc! {" const a: B = ( c( - | | - | ) + ˇ ˇ + ˇ ) ); "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" const a: B = ( c( - | - |) + ˇ + ˇ) ); "}); } @@ -8200,58 +8141,68 @@ mod tests { let mut cx = EditorTestContext::new(cx).await; cx.set_state(indoc! {" - [one} [two} + «oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - [one} [two} + «oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - [one} [two} + «oneˇ» «twoˇ» three - four"}); + four + "}); // select across line ending cx.set_state(indoc! {" one two - t[hree - } four"}); + t«hree + ˇ» four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" one two - t[hree - } four"}); + t«hree + ˇ» four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" one two - t[hree - } four"}); + t«hree + ˇ» four + "}); // Ensure that indenting/outdenting works when the cursor is at column 0. cx.set_state(indoc! {" one two - |three - four"}); + ˇthree + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" one two - |three - four"}); + ˇthree + four + "}); cx.set_state(indoc! {" one two - | three - four"}); + ˇ three + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" one two - |three - four"}); + ˇthree + four + "}); } #[gpui::test] @@ -8265,75 +8216,90 @@ mod tests { // select two ranges on one line cx.set_state(indoc! {" - [one} [two} + «oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - \t[one} [two} + \t«oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - \t\t[one} [two} + \t\t«oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - \t[one} [two} + \t«oneˇ» «twoˇ» three - four"}); + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - [one} [two} + «oneˇ» «twoˇ» three - four"}); + four + "}); // select across a line ending cx.set_state(indoc! {" one two - t[hree - }four"}); + t«hree + ˇ»four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" one two - \tt[hree - }four"}); + \tt«hree + ˇ»four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" one two - \t\tt[hree - }four"}); + \t\tt«hree + ˇ»four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" one two - \tt[hree - }four"}); + \tt«hree + ˇ»four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" one two - t[hree - }four"}); + t«hree + ˇ»four + "}); // Ensure that indenting/outdenting works when the cursor is at column 0. cx.set_state(indoc! {" one two - |three - four"}); - cx.assert_editor_state(indoc! {" - one two - |three - four"}); - cx.update_editor(|e, cx| e.tab(&Tab, cx)); - cx.assert_editor_state(indoc! {" - one two - \t|three - four"}); + ˇthree + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" one two - |three - four"}); + ˇthree + four + "}); + cx.update_editor(|e, cx| e.tab(&Tab, cx)); + cx.assert_editor_state(indoc! {" + one two + \tˇthree + four + "}); + cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); + cx.assert_editor_state(indoc! {" + one two + ˇthree + four + "}); } #[gpui::test] @@ -8412,10 +8378,10 @@ mod tests { select_ranges( &mut editor, indoc! {" - [a] = 1 + «aˇ» = 1 b = 2 - [const c:] usize = 3; + «const c:ˇ» usize = 3; "}, cx, ); @@ -8424,10 +8390,10 @@ mod tests { assert_text_with_selections( &mut editor, indoc! {" - [a] = 1 + «aˇ» = 1 b = 2 - [const c:] usize = 3; + «const c:ˇ» usize = 3; "}, cx, ); @@ -8435,10 +8401,10 @@ mod tests { assert_text_with_selections( &mut editor, indoc! {" - [a] = 1 + «aˇ» = 1 b = 2 - [const c:] usize = 3; + «const c:ˇ» usize = 3; "}, cx, ); @@ -8450,43 +8416,48 @@ mod tests { #[gpui::test] async fn test_backspace(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx).await; + // Basic backspace cx.set_state(indoc! {" - on|e two three - fou[r} five six - seven {eight nine - ]ten"}); + onˇe two three + fou«rˇ» five six + seven «ˇeight nine + »ten + "}); cx.update_editor(|e, cx| e.backspace(&Backspace, cx)); cx.assert_editor_state(indoc! {" - o|e two three - fou| five six - seven |ten"}); + oˇe two three + fouˇ five six + seven ˇten + "}); // Test backspace inside and around indents cx.set_state(indoc! {" zero - |one - |two - | | | three - | | four"}); + ˇone + ˇtwo + ˇ ˇ ˇ three + ˇ ˇ four + "}); cx.update_editor(|e, cx| e.backspace(&Backspace, cx)); cx.assert_editor_state(indoc! {" zero - |one - |two - | three| four"}); + ˇone + ˇtwo + ˇ threeˇ four + "}); // Test backspace with line_mode set to true cx.update_editor(|e, _| e.selections.line_mode = true); cx.set_state(indoc! {" - The |quick |brown + The ˇquick ˇbrown fox jumps over the lazy dog - |The qu[ick b}rown"}); + ˇThe qu«ick bˇ»rown"}); cx.update_editor(|e, cx| e.backspace(&Backspace, cx)); cx.assert_editor_state(indoc! {" - |fox jumps over - the lazy dog|"}); + ˇfox jumps over + the lazy dogˇ"}); } #[gpui::test] @@ -8494,25 +8465,27 @@ mod tests { let mut cx = EditorTestContext::new(cx).await; cx.set_state(indoc! {" - on|e two three - fou[r} five six - seven {eight nine - ]ten"}); + onˇe two three + fou«rˇ» five six + seven «ˇeight nine + »ten + "}); cx.update_editor(|e, cx| e.delete(&Delete, cx)); cx.assert_editor_state(indoc! {" - on| two three - fou| five six - seven |ten"}); + onˇ two three + fouˇ five six + seven ˇten + "}); // Test backspace with line_mode set to true cx.update_editor(|e, _| e.selections.line_mode = true); cx.set_state(indoc! {" - The |quick |brown - fox {jum]ps over + The ˇquick ˇbrown + fox «ˇjum»ps over the lazy dog - |The qu[ick b}rown"}); + ˇThe qu«ick bˇ»rown"}); cx.update_editor(|e, cx| e.backspace(&Backspace, cx)); - cx.assert_editor_state("|the lazy dog|"); + cx.assert_editor_state("ˇthe lazy dogˇ"); } #[gpui::test] @@ -8824,19 +8797,19 @@ mod tests { async fn test_clipboard(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx).await; - cx.set_state("[one✅ }two [three }four [five }six "); + cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six "); cx.update_editor(|e, cx| e.cut(&Cut, cx)); - cx.assert_editor_state("|two |four |six "); + cx.assert_editor_state("ˇtwo ˇfour ˇsix "); // Paste with three cursors. Each cursor pastes one slice of the clipboard text. - cx.set_state("two |four |six |"); + cx.set_state("two ˇfour ˇsix ˇ"); cx.update_editor(|e, cx| e.paste(&Paste, cx)); - cx.assert_editor_state("two one✅ |four three |six five |"); + cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ"); // Paste again but with only two cursors. Since the number of cursors doesn't // match the number of slices in the clipboard, the entire clipboard text // is pasted at each cursor. - cx.set_state("|two one✅ four three six five |"); + cx.set_state("ˇtwo one✅ four three six five ˇ"); cx.update_editor(|e, cx| { e.handle_input("( ", cx); e.paste(&Paste, cx); @@ -8845,37 +8818,37 @@ mod tests { cx.assert_editor_state(indoc! {" ( one✅ three - five ) |two one✅ four three six five ( one✅ + five ) ˇtwo one✅ four three six five ( one✅ three - five ) |"}); + five ) ˇ"}); // Cut with three selections, one of which is full-line. cx.set_state(indoc! {" - 1[2}3 - 4|567 - [8}9"}); + 1«2ˇ»3 + 4ˇ567 + «8ˇ»9"}); cx.update_editor(|e, cx| e.cut(&Cut, cx)); cx.assert_editor_state(indoc! {" - 1|3 - |9"}); + 1ˇ3 + ˇ9"}); // Paste with three selections, noticing how the copied selection that was full-line // gets inserted before the second cursor. cx.set_state(indoc! {" - 1|3 - 9| - [o}ne"}); + 1ˇ3 + 9ˇ + «oˇ»ne"}); cx.update_editor(|e, cx| e.paste(&Paste, cx)); cx.assert_editor_state(indoc! {" - 12|3 + 12ˇ3 4567 - 9| - 8|ne"}); + 9ˇ + 8ˇne"}); // Copy with a single cursor only, which writes the whole line into the clipboard. cx.set_state(indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}); cx.update_editor(|e, cx| e.copy(&Copy, cx)); cx.cx.assert_clipboard_content(Some("fox jumps over\n")); @@ -8883,17 +8856,17 @@ mod tests { // Paste with three selections, noticing how the copied full-line selection is inserted // before the empty selections but replaces the selection that is non-empty. cx.set_state(indoc! {" - T|he quick brown - [fo}x jumps over - t|he lazy dog"}); + Tˇhe quick brown + «foˇ»x jumps over + tˇhe lazy dog"}); cx.update_editor(|e, cx| e.paste(&Paste, cx)); cx.assert_editor_state(indoc! {" fox jumps over - T|he quick brown + Tˇhe quick brown fox jumps over - |x jumps over + ˇx jumps over fox jumps over - t|he lazy dog"}); + tˇhe lazy dog"}); } #[gpui::test] @@ -8909,17 +8882,17 @@ mod tests { cx.set_state(indoc! {" const a: B = ( c(), - [d( + «d( e, f - )} + )ˇ» ); "}); cx.update_editor(|e, cx| e.cut(&Cut, cx)); cx.assert_editor_state(indoc! {" const a: B = ( c(), - | + ˇ ); "}); @@ -8931,13 +8904,13 @@ mod tests { d( e, f - )| + )ˇ ); "}); // Paste it at a line with a lower indent level. cx.set_state(indoc! {" - | + ˇ const a: B = ( c(), ); @@ -8947,7 +8920,7 @@ mod tests { d( e, f - )| + )ˇ const a: B = ( c(), ); @@ -8957,17 +8930,17 @@ mod tests { cx.set_state(indoc! {" const a: B = ( c(), - [ d( + « d( e, f ) - }); + ˇ»); "}); cx.update_editor(|e, cx| e.cut(&Cut, cx)); cx.assert_editor_state(indoc! {" const a: B = ( c(), - |); + ˇ); "}); // Paste it at the same position. @@ -8979,7 +8952,7 @@ mod tests { e, f ) - |); + ˇ); "}); // Paste it at a line with a higher indent level. @@ -8988,7 +8961,7 @@ mod tests { c(), d( e, - f| + fˇ ) ); "}); @@ -9002,7 +8975,7 @@ mod tests { e, f ) - | + ˇ ) ); "}); @@ -9326,55 +9299,27 @@ mod tests { } #[gpui::test] - fn test_select_next(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); + async fn test_select_next(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx).await; + cx.set_state("abc\nˇabc abc\ndefabc\nabc"); - let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]"); - let buffer = MultiBuffer::build_simple(&text, cx); - let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_ranges([ranges[1].start + 1..ranges[1].start + 1]) - }); - view.select_next( - &SelectNext { - replace_newest: false, - }, - cx, - ); - assert_eq!(view.selections.ranges(cx), &ranges[1..2]); + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc"); - view.select_next( - &SelectNext { - replace_newest: false, - }, - cx, - ); - assert_eq!(view.selections.ranges(cx), &ranges[1..3]); + cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx)); + cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); - view.undo_selection(&UndoSelection, cx); - assert_eq!(view.selections.ranges(cx), &ranges[1..2]); + cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx)); + cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc"); - view.redo_selection(&RedoSelection, cx); - assert_eq!(view.selections.ranges(cx), &ranges[1..3]); + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); - view.select_next( - &SelectNext { - replace_newest: false, - }, - cx, - ); - assert_eq!(view.selections.ranges(cx), &ranges[1..4]); - - view.select_next( - &SelectNext { - replace_newest: false, - }, - cx, - ); - assert_eq!(view.selections.ranges(cx), &ranges[0..4]); - }); + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); } #[gpui::test] @@ -9952,10 +9897,15 @@ mod tests { async fn test_snippets(cx: &mut gpui::TestAppContext) { cx.update(|cx| cx.set_global(Settings::test(cx))); - let (text, insertion_ranges) = marked_text_ranges(indoc! {" - a.| b - a.| b - a.| b"}); + let (text, insertion_ranges) = marked_text_ranges( + indoc! {" + a.ˇ b + a.ˇ b + a.ˇ b + "}, + false, + ); + let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); @@ -9966,23 +9916,20 @@ mod tests { .insert_snippet(&insertion_ranges, snippet, cx) .unwrap(); - fn assert(editor: &mut Editor, cx: &mut ViewContext, marked_text_ranges: &str) { - let range_markers = ('<', '>'); - let (expected_text, mut selection_ranges_lookup) = - marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]); - let selection_ranges = selection_ranges_lookup - .remove(&range_markers.into()) - .unwrap(); + fn assert(editor: &mut Editor, cx: &mut ViewContext, marked_text: &str) { + let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false); assert_eq!(editor.text(cx), expected_text); assert_eq!(editor.selections.ranges::(cx), selection_ranges); } + assert( editor, cx, indoc! {" - a.f(, two, ) b - a.f(, two, ) b - a.f(, two, ) b"}, + a.f(«one», two, «three») b + a.f(«one», two, «three») b + a.f(«one», two, «three») b + "}, ); // Can't move earlier than the first tab stop @@ -9991,9 +9938,10 @@ mod tests { editor, cx, indoc! {" - a.f(, two, ) b - a.f(, two, ) b - a.f(, two, ) b"}, + a.f(«one», two, «three») b + a.f(«one», two, «three») b + a.f(«one», two, «three») b + "}, ); assert!(editor.move_to_next_snippet_tabstop(cx)); @@ -10001,9 +9949,10 @@ mod tests { editor, cx, indoc! {" - a.f(one, , three) b - a.f(one, , three) b - a.f(one, , three) b"}, + a.f(one, «two», three) b + a.f(one, «two», three) b + a.f(one, «two», three) b + "}, ); editor.move_to_prev_snippet_tabstop(cx); @@ -10011,9 +9960,10 @@ mod tests { editor, cx, indoc! {" - a.f(, two, ) b - a.f(, two, ) b - a.f(, two, ) b"}, + a.f(«one», two, «three») b + a.f(«one», two, «three») b + a.f(«one», two, «three») b + "}, ); assert!(editor.move_to_next_snippet_tabstop(cx)); @@ -10021,18 +9971,20 @@ mod tests { editor, cx, indoc! {" - a.f(one, , three) b - a.f(one, , three) b - a.f(one, , three) b"}, + a.f(one, «two», three) b + a.f(one, «two», three) b + a.f(one, «two», three) b + "}, ); assert!(editor.move_to_next_snippet_tabstop(cx)); assert( editor, cx, indoc! {" - a.f(one, two, three)<> b - a.f(one, two, three)<> b - a.f(one, two, three)<> b"}, + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + "}, ); // As soon as the last tab stop is reached, snippet state is gone @@ -10041,9 +9993,10 @@ mod tests { editor, cx, indoc! {" - a.f(one, two, three)<> b - a.f(one, two, three)<> b - a.f(one, two, three)<> b"}, + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + "}, ); }); } @@ -10293,16 +10246,18 @@ mod tests { .await; cx.set_state(indoc! {" - one| + oneˇ two - three"}); + three + "}); cx.simulate_keystroke("."); handle_completion_request( &mut cx, indoc! {" one.|<> two - three"}, + three + "}, vec!["first_completion", "second_completion"], ) .await; @@ -10315,9 +10270,10 @@ mod tests { .unwrap() }); cx.assert_editor_state(indoc! {" - one.second_completion| + one.second_completionˇ two - three"}); + three + "}); handle_resolve_completion_request( &mut cx, @@ -10325,23 +10281,26 @@ mod tests { indoc! {" one.second_completion two - three<>"}, + threeˇ + "}, "\nadditional edit", )), ) .await; apply_additional_edits.await.unwrap(); cx.assert_editor_state(indoc! {" - one.second_completion| + one.second_completionˇ two three - additional edit"}); + additional edit + "}); cx.set_state(indoc! {" one.second_completion - two| - three| - additional edit"}); + twoˇ + threeˇ + additional edit + "}); cx.simulate_keystroke(" "); assert!(cx.editor(|e, _| e.context_menu.is_none())); cx.simulate_keystroke("s"); @@ -10349,16 +10308,19 @@ mod tests { cx.assert_editor_state(indoc! {" one.second_completion - two s| - three s| - additional edit"}); + two sˇ + three sˇ + additional edit + "}); + // handle_completion_request( &mut cx, indoc! {" one.second_completion two s three - additional edit"}, + additional edit + "}, vec!["fourth_completion", "fifth_completion", "sixth_completion"], ) .await; @@ -10373,7 +10335,8 @@ mod tests { one.second_completion two si three - additional edit"}, + additional edit + "}, vec!["fourth_completion", "fifth_completion", "sixth_completion"], ) .await; @@ -10387,9 +10350,10 @@ mod tests { }); cx.assert_editor_state(indoc! {" one.second_completion - two sixth_completion| - three sixth_completion| - additional edit"}); + two sixth_completionˇ + three sixth_completionˇ + additional edit + "}); handle_resolve_completion_request(&mut cx, None).await; apply_additional_edits.await.unwrap(); @@ -10399,13 +10363,13 @@ mod tests { settings.show_completions_on_input = false; }) }); - cx.set_state("editor|"); + cx.set_state("editorˇ"); cx.simulate_keystroke("."); assert!(cx.editor(|e, _| e.context_menu.is_none())); cx.simulate_keystroke("c"); cx.simulate_keystroke("l"); cx.simulate_keystroke("o"); - cx.assert_editor_state("editor.clo|"); + cx.assert_editor_state("editor.cloˇ"); assert!(cx.editor(|e, _| e.context_menu.is_none())); cx.update_editor(|editor, cx| { editor.show_completions(&ShowCompletions, cx); @@ -10418,7 +10382,7 @@ mod tests { .confirm_completion(&ConfirmCompletion::default(), cx) .unwrap() }); - cx.assert_editor_state("editor.close|"); + cx.assert_editor_state("editor.closeˇ"); handle_resolve_completion_request(&mut cx, None).await; apply_additional_edits.await.unwrap(); @@ -10474,13 +10438,8 @@ mod tests { edit: Option<(&'static str, &'static str)>, ) { let edit = edit.map(|(marked_string, new_text)| { - let replace_range_marker: TextRangeMarker = ('<', '>').into(); - let (_, mut marked_ranges) = - marked_text_ranges_by(marked_string, vec![replace_range_marker.clone()]); - - let replace_range = cx - .to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone()); - + let (_, marked_ranges) = marked_text_ranges(marked_string, false); + let replace_range = cx.to_lsp_range(marked_ranges[0].clone()); vec![lsp::TextEdit::new(replace_range, new_text.to_string())] }); @@ -10631,13 +10590,21 @@ mod tests { #[gpui::test] fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); - let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {" + let markers = vec![('[', ']').into(), ('(', ')').into()]; + let (initial_text, mut excerpt_ranges) = marked_text_ranges_by( + indoc! {" [aaaa (bbbb] - cccc)"}); - let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange { - context, - primary: None, + cccc)", + }, + markers.clone(), + ); + let excerpt_ranges = markers.into_iter().map(|marker| { + let context = excerpt_ranges.remove(&marker).unwrap()[0].clone(); + ExcerptRange { + context, + primary: None, + } }); let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); let multibuffer = cx.add_model(|cx| { @@ -10648,34 +10615,46 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx)); view.update(cx, |view, cx| { - let (expected_text, selection_ranges) = marked_text_ranges(indoc! {" - aaaa - b|bbb - b|bb|b - cccc"}); + let (expected_text, selection_ranges) = marked_text_ranges( + indoc! {" + aaaa + bˇbbb + bˇbbˇb + cccc" + }, + true, + ); assert_eq!(view.text(cx), expected_text); view.change_selections(None, cx, |s| s.select_ranges(selection_ranges)); view.handle_input("X", cx); - let (expected_text, expected_selections) = marked_text_ranges(indoc! {" - aaaa - bX|bbXb - bX|bbX|b - cccc"}); + let (expected_text, expected_selections) = marked_text_ranges( + indoc! {" + aaaa + bXˇbbXb + bXˇbbXˇb + cccc" + }, + false, + ); assert_eq!(view.text(cx), expected_text); assert_eq!(view.selections.ranges(cx), expected_selections); view.newline(&Newline, cx); - let (expected_text, expected_selections) = marked_text_ranges(indoc! {" - aaaa - bX - |bbX - b - bX - |bbX - |b - cccc"}); + let (expected_text, expected_selections) = marked_text_ranges( + indoc! {" + aaaa + bX + ˇbbX + b + bX + ˇbbX + ˇb + cccc" + }, + false, + ); assert_eq!(view.text(cx), expected_text); assert_eq!(view.selections.ranges(cx), expected_selections); }); @@ -11152,30 +11131,12 @@ mod tests { point..point } - fn assert_selection_ranges( - marked_text: &str, - selection_marker_pairs: Vec<(char, char)>, - view: &mut Editor, - cx: &mut ViewContext, - ) { - let snapshot = view.snapshot(cx).display_snapshot; - let mut marker_chars = Vec::new(); - for (start, end) in selection_marker_pairs.iter() { - marker_chars.push(*start); - marker_chars.push(*end); - } - let (_, markers) = marked_text_by(marked_text, marker_chars); - let asserted_ranges: Vec> = selection_marker_pairs - .iter() - .map(|(start, end)| { - let start = markers.get(start).unwrap()[0].to_display_point(&snapshot); - let end = markers.get(end).unwrap()[0].to_display_point(&snapshot); - start..end - }) - .collect(); + fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext) { + let (text, ranges) = marked_text_ranges(marked_text, true); + assert_eq!(view.text(cx), text); assert_eq!( - view.selections.display_ranges(cx), - &asserted_ranges[..], + view.selections.ranges(cx), + ranges, "Assert selections are {}", marked_text ); diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index 30bf1091f2..789393d70b 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -32,13 +32,10 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon #[cfg(test)] mod tests { - use indoc::indoc; - - use language::{BracketPair, Language, LanguageConfig}; - - use crate::test::EditorLspTestContext; - use super::*; + use crate::test::EditorLspTestContext; + use indoc::indoc; + use language::{BracketPair, Language, LanguageConfig}; #[gpui::test] async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) { @@ -76,67 +73,61 @@ mod tests { .await; // positioning cursor inside bracket highlights both - cx.set_state_by( - vec!['|'.into()], - indoc! {r#" - pub fn test("Test |argument") { - another_test(1, 2, 3); - }"#}, - ); + cx.set_state(indoc! {r#" + pub fn test("Test ˇargument") { + another_test(1, 2, 3); + } + "#}); cx.assert_editor_background_highlights::(indoc! {r#" - pub fn test[(]"Test argument"[)] { - another_test(1, 2, 3); - }"#}); + pub fn test«(»"Test argument"«)» { + another_test(1, 2, 3); + } + "#}); - cx.set_state_by( - vec!['|'.into()], - indoc! {r#" - pub fn test("Test argument") { - another_test(1, |2, 3); - }"#}, - ); + cx.set_state(indoc! {r#" + pub fn test("Test argument") { + another_test(1, ˇ2, 3); + } + "#}); cx.assert_editor_background_highlights::(indoc! {r#" pub fn test("Test argument") { - another_test[(]1, 2, 3[)]; - }"#}); + another_test«(»1, 2, 3«)»; + } + "#}); - cx.set_state_by( - vec!['|'.into()], - indoc! {r#" - pub fn test("Test argument") { - another|_test(1, 2, 3); - }"#}, - ); + cx.set_state(indoc! {r#" + pub fn test("Test argument") { + anotherˇ_test(1, 2, 3); + } + "#}); cx.assert_editor_background_highlights::(indoc! {r#" - pub fn test("Test argument") [{] + pub fn test("Test argument") «{» another_test(1, 2, 3); - [}]"#}); + «}» + "#}); // positioning outside of brackets removes highlight - cx.set_state_by( - vec!['|'.into()], - indoc! {r#" - pub f|n test("Test argument") { - another_test(1, 2, 3); - }"#}, - ); + cx.set_state(indoc! {r#" + pub fˇn test("Test argument") { + another_test(1, 2, 3); + } + "#}); cx.assert_editor_background_highlights::(indoc! {r#" pub fn test("Test argument") { another_test(1, 2, 3); - }"#}); + } + "#}); // non empty selection dismisses highlight - // positioning outside of brackets removes highlight - cx.set_state_by( - vec![('<', '>').into()], - indoc! {r#" - pub fn test("Teument") { - another_test(1, 2, 3); - }"#}, - ); + cx.set_state(indoc! {r#" + pub fn test("Te«st argˇ»ument") { + another_test(1, 2, 3); + } + "#}); cx.assert_editor_background_highlights::(indoc! {r#" pub fn test("Test argument") { another_test(1, 2, 3); - }"#}); + } + "#}); } } diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 99669cb15d..37a2a66e05 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -439,11 +439,11 @@ mod tests { // Basic hover delays and then pops without moving the mouse cx.set_state(indoc! {" - fn |test() - println!();"}); + fn ˇtest() { println!(); } + "}); let hover_point = cx.display_point(indoc! {" - fn test() - print|ln!();"}); + fn test() { printˇln!(); } + "}); cx.update_editor(|editor, cx| { hover_at( @@ -458,16 +458,16 @@ mod tests { // After delay, hover should be visible. let symbol_range = cx.lsp_range(indoc! {" - fn test() - [println!]();"}); + fn test() { «println!»(); } + "}); let mut requests = cx.handle_request::(move |_, _, _| async move { Ok(Some(lsp::Hover { contents: lsp::HoverContents::Markup(lsp::MarkupContent { kind: lsp::MarkupKind::Markdown, value: indoc! {" - # Some basic docs - Some test documentation"} + # Some basic docs + Some test documentation"} .to_string(), }), range: Some(symbol_range), @@ -496,8 +496,8 @@ mod tests { // Mouse moved with no hover response dismisses let hover_point = cx.display_point(indoc! {" - fn te|st() - println!();"}); + fn teˇst() { println!(); } + "}); let mut request = cx .lsp .handle_request::(|_, _| async move { Ok(None) }); @@ -531,12 +531,12 @@ mod tests { // Hover with keyboard has no delay cx.set_state(indoc! {" - f|n test() - println!();"}); + fˇn test() { println!(); } + "}); cx.update_editor(|editor, cx| hover(editor, &Hover, cx)); let symbol_range = cx.lsp_range(indoc! {" - [fn] test() - println!();"}); + «fn» test() { println!(); } + "}); cx.handle_request::(move |_, _, _| async move { Ok(Some(lsp::Hover { contents: lsp::HoverContents::Markup(lsp::MarkupContent { @@ -584,13 +584,13 @@ mod tests { // Hover with just diagnostic, pops DiagnosticPopover immediately and then // info popover once request completes cx.set_state(indoc! {" - fn te|st() - println!();"}); + fn teˇst() { println!(); } + "}); // Send diagnostic to client let range = cx.text_anchor_range(indoc! {" - fn [test]() - println!();"}); + fn «test»() { println!(); } + "}); cx.update_buffer(|buffer, cx| { let snapshot = buffer.text_snapshot(); let set = DiagnosticSet::from_sorted_entries( @@ -616,15 +616,15 @@ mod tests { // Info Popover shows after request responded to let range = cx.lsp_range(indoc! {" - fn [test]() - println!();"}); + fn «test»() { println!(); } + "}); cx.handle_request::(move |_, _, _| async move { Ok(Some(lsp::Hover { contents: lsp::HoverContents::Markup(lsp::MarkupContent { kind: lsp::MarkupKind::Markdown, value: indoc! {" - # Some other basic docs - Some other test documentation"} + # Some other basic docs + Some other test documentation"} .to_string(), }), range: Some(range), diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index b57179c07d..cc4592b6d4 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -405,20 +405,20 @@ mod tests { cx.set_state(indoc! {" struct A; - let v|ariable = A; + let vˇariable = A; "}); // Basic hold cmd+shift, expect highlight in region if response contains type definition let hover_point = cx.display_point(indoc! {" struct A; - let v|ariable = A; + let vˇariable = A; "}); let symbol_range = cx.lsp_range(indoc! {" struct A; - let [variable] = A; + let «variable» = A; "}); let target_range = cx.lsp_range(indoc! {" - struct [A]; + struct «A»; let variable = A; "}); @@ -450,7 +450,7 @@ mod tests { cx.foreground().run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" struct A; - let [variable] = A; + let «variable» = A; "}); // 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 let hover_point = cx.display_point(indoc! {" struct A; - let v|ariable = A; + let vˇariable = A; "}); let target_range = cx.lsp_range(indoc! {" - struct [A]; + struct «A»; let variable = A; "}); @@ -503,7 +503,7 @@ mod tests { cx.foreground().run_until_parked(); cx.assert_editor_state(indoc! {" - struct [A}; + struct «Aˇ»; let variable = A; "}); } @@ -520,34 +520,22 @@ mod tests { .await; cx.set_state(indoc! {" - fn |test() - do_work(); - - fn do_work() - test(); + fn ˇtest() { do_work(); } + fn do_work() { test(); } "}); // Basic hold cmd, expect highlight in region if response contains definition let hover_point = cx.display_point(indoc! {" - fn test() - do_w|ork(); - - fn do_work() - test(); + fn test() { do_wˇork(); } + fn do_work() { test(); } "}); let symbol_range = cx.lsp_range(indoc! {" - fn test() - [do_work](); - - fn do_work() - test(); + fn test() { «do_work»(); } + fn do_work() { test(); } "}); let target_range = cx.lsp_range(indoc! {" - fn test() - do_work(); - - fn [do_work]() - test(); + fn test() { do_work(); } + fn «do_work»() { test(); } "}); let mut requests = cx.handle_request::(move |url, _, _| async move { @@ -575,11 +563,8 @@ mod tests { requests.next().await; cx.foreground().run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" - fn test() - [do_work](); - - fn do_work() - test(); + fn test() { «do_work»(); } + fn do_work() { test(); } "}); // Unpress cmd causes highlight to go away @@ -593,13 +578,11 @@ mod tests { cx, ); }); + // Assert no link highlights cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - test(); + fn test() { do_work(); } + fn do_work() { test(); } "}); // Response without source range still highlights word @@ -630,20 +613,14 @@ mod tests { cx.foreground().run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" - fn test() - [do_work](); - - fn do_work() - test(); + fn test() { «do_work»(); } + fn do_work() { test(); } "}); // Moving mouse to location with no response dismisses highlight let hover_point = cx.display_point(indoc! {" - f|n test() - do_work(); - - fn do_work() - test(); + fˇn test() { do_work(); } + fn do_work() { test(); } "}); let mut requests = cx .lsp @@ -667,20 +644,14 @@ mod tests { // Assert no link highlights cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - test(); + fn test() { do_work(); } + fn do_work() { test(); } "}); // Move mouse without cmd and then pressing cmd triggers highlight let hover_point = cx.display_point(indoc! {" - fn test() - do_work(); - - fn do_work() - te|st(); + fn test() { do_work(); } + fn do_work() { teˇst(); } "}); cx.update_editor(|editor, cx| { update_go_to_definition_link( @@ -697,26 +668,17 @@ mod tests { // Assert no link highlights cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - test(); + fn test() { do_work(); } + fn do_work() { test(); } "}); let symbol_range = cx.lsp_range(indoc! {" - fn test() - do_work(); - - fn do_work() - [test](); + fn test() { do_work(); } + fn do_work() { «test»(); } "}); let target_range = cx.lsp_range(indoc! {" - fn [test]() - do_work(); - - fn do_work() - test(); + fn «test»() { do_work(); } + fn do_work() { test(); } "}); let mut requests = cx.handle_request::(move |url, _, _| async move { @@ -743,20 +705,14 @@ mod tests { cx.foreground().run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - [test](); + fn test() { do_work(); } + fn do_work() { «test»(); } "}); // Moving within symbol range doesn't re-request let hover_point = cx.display_point(indoc! {" - fn test() - do_work(); - - fn do_work() - tes|t(); + fn test() { do_work(); } + fn do_work() { tesˇt(); } "}); cx.update_editor(|editor, cx| { update_go_to_definition_link( @@ -771,11 +727,8 @@ mod tests { }); cx.foreground().run_until_parked(); cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - [test](); + fn test() { do_work(); } + fn do_work() { «test»(); } "}); // Cmd click with existing definition doesn't re-request and dismisses highlight @@ -790,35 +743,24 @@ mod tests { Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) }); cx.assert_editor_state(indoc! {" - fn [test}() - do_work(); - - fn do_work() - test(); + fn «testˇ»() { do_work(); } + fn do_work() { test(); } "}); + // Assert no link highlights after jump cx.assert_editor_text_highlights::(indoc! {" - fn test() - do_work(); - - fn do_work() - test(); + fn test() { do_work(); } + fn do_work() { test(); } "}); // Cmd click without existing definition requests and jumps let hover_point = cx.display_point(indoc! {" - fn test() - do_w|ork(); - - fn do_work() - test(); + fn test() { do_wˇork(); } + fn do_work() { test(); } "}); let target_range = cx.lsp_range(indoc! {" - fn test() - do_work(); - - fn [do_work]() - test(); + fn test() { do_work(); } + fn «do_work»() { test(); } "}); let mut requests = cx.handle_request::(move |url, _, _| async move { @@ -836,13 +778,9 @@ mod tests { }); requests.next().await; cx.foreground().run_until_parked(); - cx.assert_editor_state(indoc! {" - fn test() - do_work(); - - fn [do_work}() - test(); + fn test() { do_work(); } + fn «do_workˇ»() { test(); } "}); } } diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 513a9ed99c..3098e96e07 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -67,11 +67,9 @@ pub fn deploy_context_menu( #[cfg(test)] mod tests { - use indoc::indoc; - - use crate::test::EditorLspTestContext; - use super::*; + use crate::test::EditorLspTestContext; + use indoc::indoc; #[gpui::test] async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) { @@ -85,11 +83,15 @@ mod tests { .await; cx.set_state(indoc! {" - fn te|st() - do_work();"}); + fn teˇst() { + do_work(); + } + "}); let point = cx.display_point(indoc! {" - fn test() - do_w|ork();"}); + fn test() { + do_wˇork(); + } + "}); cx.update_editor(|editor, cx| { deploy_context_menu( editor, @@ -102,8 +104,10 @@ mod tests { }); cx.assert_editor_state(indoc! {" - fn test() - do_w|ork();"}); + fn test() { + do_wˇork(); + } + "}); cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible())); } } diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 656473dbe8..0db5cc0812 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -287,20 +287,20 @@ mod tests { ); } - assert("\n| |lorem", cx); - assert("|\n| lorem", cx); - assert(" |lorem|", cx); - assert("| |lorem", cx); - assert(" |lor|em", cx); - assert("\nlorem\n| |ipsum", cx); - assert("\n\n|\n|", cx); - assert(" |lorem |ipsum", cx); - assert("lorem|-|ipsum", cx); - assert("lorem|-#$@|ipsum", cx); - assert("|lorem_|ipsum", cx); - assert(" |defγ|", cx); - assert(" |bcΔ|", cx); - assert(" ab|——|cd", cx); + assert("\nˇ ˇlorem", cx); + assert("ˇ\nˇ lorem", cx); + assert(" ˇloremˇ", cx); + assert("ˇ ˇlorem", cx); + assert(" ˇlorˇem", cx); + assert("\nlorem\nˇ ˇipsum", cx); + assert("\n\nˇ\nˇ", cx); + assert(" ˇlorem ˇipsum", cx); + assert("loremˇ-ˇipsum", cx); + assert("loremˇ-#$@ˇipsum", cx); + assert("ˇlorem_ˇipsum", cx); + assert(" ˇdefγˇ", cx); + assert(" ˇbcΔˇ", cx); + assert(" abˇ——ˇcd", cx); } #[gpui::test] @@ -315,26 +315,26 @@ mod tests { } // Subword boundaries are respected - assert("lorem_|ip|sum", cx); - assert("lorem_|ipsum|", cx); - assert("|lorem_|ipsum", cx); - assert("lorem_|ipsum_|dolor", cx); - assert("lorem|Ip|sum", cx); - assert("lorem|Ipsum|", cx); + assert("lorem_ˇipˇsum", cx); + assert("lorem_ˇipsumˇ", cx); + assert("ˇlorem_ˇipsum", cx); + assert("lorem_ˇipsum_ˇdolor", cx); + assert("loremˇIpˇsum", cx); + assert("loremˇIpsumˇ", cx); // Word boundaries are still respected - assert("\n| |lorem", cx); - assert(" |lorem|", cx); - assert(" |lor|em", cx); - assert("\nlorem\n| |ipsum", cx); - assert("\n\n|\n|", cx); - assert(" |lorem |ipsum", cx); - assert("lorem|-|ipsum", cx); - assert("lorem|-#$@|ipsum", cx); - assert(" |defγ|", cx); - assert(" bc|Δ|", cx); - assert(" |bcδ|", cx); - assert(" ab|——|cd", cx); + assert("\nˇ ˇlorem", cx); + assert(" ˇloremˇ", cx); + assert(" ˇlorˇem", cx); + assert("\nlorem\nˇ ˇipsum", cx); + assert("\n\nˇ\nˇ", cx); + assert(" ˇlorem ˇipsum", cx); + assert("loremˇ-ˇipsum", cx); + assert("loremˇ-#$@ˇipsum", cx); + assert(" ˇdefγˇ", cx); + assert(" bcˇΔˇ", cx); + assert(" ˇbcδˇ", cx); + assert(" abˇ——ˇcd", cx); } #[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' }); - assert("abcdef\n|gh\nij|k", cx, |left, right| { + assert("abcdef\nˇgh\nijˇk", cx, |left, right| { left == '\n' && right == 'g' }); let mut line_count = 0; - assert("abcdef\n|gh\nij|k", cx, |left, _| { + assert("abcdef\nˇgh\nijˇk", cx, |left, _| { if left == '\n' { line_count += 1; line_count == 2 @@ -380,17 +380,17 @@ mod tests { ); } - assert("\n| lorem|", cx); - assert(" |lorem|", cx); - assert(" lor|em|", cx); - assert(" lorem| |\nipsum\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(" |bcΔ|", cx); - assert(" ab|——|cd", cx); + assert("\nˇ loremˇ", cx); + assert(" ˇloremˇ", cx); + assert(" lorˇemˇ", cx); + assert(" loremˇ ˇ\nipsum\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(" ˇbcΔˇ", cx); + assert(" abˇ——ˇcd", cx); } #[gpui::test] @@ -405,25 +405,25 @@ mod tests { } // Subword boundaries are respected - assert("lo|rem|_ipsum", cx); - assert("|lorem|_ipsum", cx); - assert("lorem|_ipsum|", cx); - assert("lorem|_ipsum|_dolor", cx); - assert("lo|rem|Ipsum", cx); - assert("lorem|Ipsum|Dolor", cx); + assert("loˇremˇ_ipsum", cx); + assert("ˇloremˇ_ipsum", cx); + assert("loremˇ_ipsumˇ", cx); + assert("loremˇ_ipsumˇ_dolor", cx); + assert("loˇremˇIpsum", cx); + assert("loremˇIpsumˇDolor", cx); // Word boundaries are still respected - assert("\n| lorem|", cx); - assert(" |lorem|", cx); - assert(" lor|em|", cx); - assert(" lorem| |\nipsum\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(" |bc|Δ", cx); - assert(" ab|——|cd", cx); + assert("\nˇ loremˇ", cx); + assert(" ˇloremˇ", cx); + assert(" lorˇemˇ", cx); + assert(" loremˇ ˇ\nipsum\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(" ˇbcˇΔ", cx); + assert(" abˇ——ˇcd", cx); } #[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' }); - assert("ab|cdef\ngh\n|ijk", cx, |left, right| { + assert("abˇcdef\ngh\nˇijk", cx, |left, right| { left == '\n' && right == 'i' }); let mut line_count = 0; - assert("abc|def\ngh\n|ijk", cx, |left, _| { + assert("abcˇdef\ngh\nˇijk", cx, |left, _| { if left == '\n' { line_count += 1; line_count == 2 @@ -469,14 +469,14 @@ mod tests { ); } - assert("||lorem| ipsum", cx); - assert("|lo|rem| ipsum", cx); - assert("|lorem|| ipsum", cx); - assert("lorem| | |ipsum", cx); - assert("lorem\n|||\nipsum", cx); - assert("lorem\n||ipsum|", cx); - assert("lorem,|| |ipsum", cx); - assert("|lorem||, ipsum", cx); + assert("ˇˇloremˇ ipsum", cx); + assert("ˇloˇremˇ ipsum", cx); + assert("ˇloremˇˇ ipsum", cx); + assert("loremˇ ˇ ˇipsum", cx); + assert("lorem\nˇˇˇ\nipsum", cx); + assert("lorem\nˇˇipsumˇ", cx); + assert("lorem,ˇˇ ˇipsum", cx); + assert("ˇloremˇˇ, ipsum", cx); } #[gpui::test] diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 18c13a4ba6..5f6528db1c 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -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::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, multi_buffer::ToPointUtf16, 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)] #[ctor::ctor] @@ -43,7 +37,7 @@ pub fn marked_display_snapshot( text: &str, cx: &mut gpui::MutableAppContext, ) -> (DisplaySnapshot, Vec) { - 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 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) { - 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); editor.change_selections(None, cx, |s| s.select_ranges(text_ranges)); } @@ -75,8 +69,7 @@ pub fn assert_text_with_selections( marked_text: &str, cx: &mut ViewContext, ) { - 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.selections.ranges(cx), text_ranges); } @@ -190,94 +183,58 @@ impl<'a> EditorTestContext<'a> { } } - pub fn display_point(&mut self, cursor_location: &str) -> DisplayPoint { - let (_, locations) = marked_text(cursor_location); + fn ranges(&self, marked_text: &str) -> Vec> { + 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 .editor .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 { - let range_marker: TextRangeMarker = ('[', ']').into(); - 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 ranges = self.ranges(marked_text); let snapshot = self.buffer_snapshot(); - - snapshot.anchor_before(offset_range.start)..snapshot.anchor_after(offset_range.end) + snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end) } - // Sets the editor state via a marked string. - // `|` characters represent empty selections - // `[` to `}` represents a non empty selection with the head at `}` - // `{` to `]` represents a non empty selection with the head at `{` - pub fn set_state(&mut self, text: &str) { - self.set_state_by( - vec![ - '|'.into(), - ('[', '}').into(), - TextRangeMarker::ReverseRange('{', ']'), - ], - text, - ); - } - - pub fn set_state_by(&mut self, range_markers: Vec, text: &str) { + /// Change the editor's text and selections using a string containing + /// embedded range markers that represent the ranges and directions of + /// each selection. + /// + /// See the `util::test::marked_text_ranges` function for more information. + pub fn set_state(&mut self, marked_text: &str) { + let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); 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); - - let selection_ranges: Vec> = selection_ranges - .values() - .into_iter() - .flatten() - .cloned() - .collect(); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select_ranges(selection_ranges) }) }) } - // Asserts the editor state via a marked string. - // `|` characters represent empty selections - // `[` to `}` represents a non empty selection with the head at `}` - // `{` to `]` represents a non empty selection with the head at `{` - pub fn assert_editor_state(&mut self, text: &str) { - let (unmarked_text, mut selection_ranges) = marked_text_ranges_by( - &text, - vec!['|'.into(), ('[', '}').into(), ('{', ']').into()], - ); + /// Make an assertion about the editor's text and the ranges and directions + /// of its selections using a string containing embedded range markers. + /// + /// See the `util::test::marked_text_ranges` function for more information. + pub fn assert_editor_state(&mut self, marked_text: &str) { + let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); let buffer_text = self.buffer_text(); assert_eq!( buffer_text, unmarked_text, "Unmarked text doesn't match buffer text" ); - - 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()), - ) + self.assert_selections(expected_selections, marked_text.to_string()) } pub fn assert_editor_background_highlights(&mut self, marked_text: &str) { - let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); - assert_eq!(unmarked, self.buffer_text()); - - let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap(); + let expected_ranges = self.ranges(marked_text); let actual_ranges: Vec> = self.update_editor(|editor, cx| { let snapshot = editor.snapshot(cx); editor @@ -289,176 +246,62 @@ impl<'a> EditorTestContext<'a> { .map(|range| range.to_offset(&snapshot.buffer_snapshot)) .collect() }); - - assert_set_eq!(asserted_ranges, actual_ranges); + assert_set_eq!(actual_ranges, expected_ranges); } pub fn assert_editor_text_highlights(&mut self, marked_text: &str) { - let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); - assert_eq!(unmarked, self.buffer_text()); - - let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap(); + let expected_ranges = self.ranges(marked_text); let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); let actual_ranges: Vec> = snapshot - .display_snapshot .highlight_ranges::() .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default() .into_iter() .map(|range| range.to_offset(&snapshot.buffer_snapshot)) .collect(); - - assert_set_eq!(asserted_ranges, actual_ranges); + assert_set_eq!(actual_ranges, expected_ranges); } - pub fn assert_editor_selections(&mut self, expected_selections: Vec>) { - let mut empty_selections = Vec::new(); - let mut reverse_selections = Vec::new(); - let mut forward_selections = Vec::new(); - - 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, - ) + pub fn assert_editor_selections(&mut self, expected_selections: Vec>) { + let expected_marked_text = + generate_marked_text(&self.buffer_text(), &expected_selections, true); + self.assert_selections(expected_selections, expected_marked_text) } fn assert_selections( &mut self, - expected_empty_selections: Vec>, - expected_reverse_selections: Vec>, - expected_forward_selections: Vec>, - asserted_text: Option, + expected_selections: Vec>, + expected_marked_text: String, ) { - let (empty_selections, reverse_selections, forward_selections) = - self.editor.read_with(self.cx, |editor, cx| { - let mut empty_selections = Vec::new(); - let mut reverse_selections = Vec::new(); - let mut forward_selections = Vec::new(); - - for selection in editor.selections.all::(cx) { - 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) - } + let actual_selections = self + .editor + .read_with(self.cx, |editor, cx| editor.selections.all::(cx)) + .into_iter() + .map(|s| { + if s.reversed { + s.end..s.start + } else { + s.start..s.end } + }) + .collect::>(); + 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(|| { - self.insert_markers( - &expected_empty_selections, - &expected_reverse_selections, - &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> = - 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, - ); - } - _ => {} + Actual selections: + {} + "}, + expected_marked_text, actual_marked_text, + ); } } - - fn insert_markers( - &mut self, - empty_selections: &Vec>, - reverse_selections: &Vec>, - forward_selections: &Vec>, - ) -> 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> { @@ -575,10 +418,8 @@ impl<'a> EditorLspTestContext<'a> { // Constructs lsp range using a marked string with '[', ']' range delimiters pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { - let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]); - assert_eq!(unmarked, self.buffer_text()); - let offset_range = ranges.remove(&('[', ']').into()).unwrap()[0].clone(); - self.to_lsp_range(offset_range) + let ranges = self.ranges(marked_text); + self.to_lsp_range(ranges[0].clone()) } pub fn to_lsp_range(&mut self, range: Range) -> lsp::Range { diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 87ec77d2df..4ec214fef1 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -15,6 +15,9 @@ futures = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } rand = { version = "0.8", optional = true } tempdir = { version = "0.3.7", optional = true } -serde_json = { version = "1.0", features = [ - "preserve_order", -], optional = true } +serde_json = { version = "1.0", features = ["preserve_order"], optional = true } + +[dev-dependencies] +rand = { version = "0.8" } +tempdir = { version = "0.3.7" } +serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index e5a98fd675..0616b2a75e 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "test-support")] +#[cfg(any(test, feature = "test-support"))] pub mod test; use futures::Future; diff --git a/crates/util/src/test/marked_text.rs b/crates/util/src/test/marked_text.rs index 2a5969c265..b5b68c22c9 100644 --- a/crates/util/src/test/marked_text.rs +++ b/crates/util/src/test/marked_text.rs @@ -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, markers: Vec, ) -> (String, HashMap>) { @@ -19,9 +21,196 @@ pub fn marked_text_by( (unmarked_text, extracted_markers) } -pub fn marked_text(marked_text: &str) -> (String, Vec) { - let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['|']); - (unmarked_text, markers.remove(&'|').unwrap_or_default()) +/// Construct a string and a list of ranges within that string using a single +/// string containing embedded range markers, using arbitrary characters as +/// 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, +) -> (String, HashMap>>) { + 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::>>(), + 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::>>() + } + 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::>>() + } + }, + ) + }) + .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>) { + 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) { + 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], + 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)] @@ -53,75 +242,24 @@ impl From<(char, char)> for TextRangeMarker { } } -pub fn marked_text_ranges_by( - marked_text: &str, - markers: Vec, -) -> (String, HashMap>>) { - let all_markers = markers.iter().flat_map(|m| m.markers()).collect(); +#[cfg(test)] +mod tests { + use super::{generate_marked_text, marked_text_ranges}; - let (unmarked_text, mut marker_offsets) = marked_text_by(marked_text, all_markers); - let range_lookup = markers - .into_iter() - .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::>>(); - (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"); + #[test] + fn test_marked_text() { + let (text, ranges) = marked_text_ranges("one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six", true); - let ranges = starts - .into_iter() - .zip(ends) - .map(|(start, end)| { - assert!(end >= start, "marked ranges must be disjoint"); - start..end - }) - .collect::>>(); - (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"); + assert_eq!(text, "one two three four five six"); + assert_eq!(ranges.len(), 4); + assert_eq!(ranges[0], 7..4); + assert_eq!(ranges[1], 8..13); + assert_eq!(ranges[2], 18..14); + assert_eq!(ranges[3], 23..23); - let ranges = starts - .into_iter() - .zip(ends) - .map(|(start, end)| { - assert!(end >= start, "marked ranges must be disjoint"); - end..start - }) - .collect::>>(); - (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>) { - 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) + assert_eq!( + generate_marked_text(&text, &ranges, true), + "one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six" + ); + } } diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index b8387e7165..a1d1c7b404 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -34,9 +34,9 @@ mod test { cx.simulate_keystroke("i"); assert_eq!(cx.mode(), Mode::Insert); cx.simulate_keystrokes(["T", "e", "s", "t"]); - cx.assert_editor_state("Test|"); + cx.assert_editor_state("Testˇ"); cx.simulate_keystroke("escape"); assert_eq!(cx.mode(), Mode::Normal); - cx.assert_editor_state("Tes|t"); + cx.assert_editor_state("Tesˇt"); } } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 39663e0db4..508477ea46 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -297,8 +297,7 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { #[cfg(test)] mod test { use indoc::indoc; - use language::Selection; - use util::test::marked_text; + use util::test::marked_text_offsets; use crate::{ state::{ @@ -312,15 +311,15 @@ mod test { async fn test_h(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["h"]); - cx.assert("The q|uick", "The |quick"); - cx.assert("|The quick", "|The quick"); + cx.assert("The qˇuick", "The ˇquick"); + cx.assert("ˇThe quick", "ˇThe quick"); cx.assert( indoc! {" The quick - |brown"}, + ˇbrown"}, indoc! {" The quick - |brown"}, + ˇbrown"}, ); } @@ -328,15 +327,15 @@ mod test { async fn test_backspace(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["backspace"]); - cx.assert("The q|uick", "The |quick"); - cx.assert("|The quick", "|The quick"); + cx.assert("The qˇuick", "The ˇquick"); + cx.assert("ˇThe quick", "ˇThe quick"); cx.assert( indoc! {" The quick - |brown"}, + ˇbrown"}, indoc! {" The quick - |brown"}, + ˇbrown"}, ); } @@ -346,35 +345,35 @@ mod test { let mut cx = cx.binding(["j"]); cx.assert( indoc! {" - The |quick + The ˇquick brown fox"}, indoc! {" The quick - brow|n fox"}, + browˇn fox"}, ); cx.assert( indoc! {" The quick - brow|n fox"}, + browˇn fox"}, indoc! {" The quick - brow|n fox"}, + browˇn fox"}, ); cx.assert( indoc! {" - The quic|k + The quicˇk brown"}, indoc! {" The quick - brow|n"}, + browˇn"}, ); cx.assert( indoc! {" The quick - |brown"}, + ˇbrown"}, indoc! {" The quick - |brown"}, + ˇbrown"}, ); } @@ -384,26 +383,26 @@ mod test { let mut cx = cx.binding(["k"]); cx.assert( indoc! {" - The |quick + The ˇquick brown fox"}, indoc! {" - The |quick + The ˇquick brown fox"}, ); cx.assert( indoc! {" The quick - brow|n fox"}, + browˇn fox"}, indoc! {" - The |quick + The ˇquick brown fox"}, ); cx.assert( indoc! {" The - quic|k"}, + quicˇk"}, indoc! {" - Th|e + Thˇe quick"}, ); } @@ -412,14 +411,14 @@ mod test { async fn test_l(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["l"]); - cx.assert("The q|uick", "The qu|ick"); - cx.assert("The quic|k", "The quic|k"); + cx.assert("The qˇuick", "The quˇick"); + cx.assert("The quicˇk", "The quicˇk"); cx.assert( indoc! {" - The quic|k + The quicˇk brown"}, indoc! {" - The quic|k + The quicˇk brown"}, ); } @@ -428,42 +427,42 @@ mod test { async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["$"]); - cx.assert("T|est test", "Test tes|t"); - cx.assert("Test tes|t", "Test tes|t"); + cx.assert("Tˇest test", "Test tesˇt"); + cx.assert("Test tesˇt", "Test tesˇt"); cx.assert( indoc! {" - The |quick + The ˇquick brown"}, indoc! {" - The quic|k + The quicˇk brown"}, ); cx.assert( indoc! {" - The quic|k + The quicˇk brown"}, indoc! {" - The quic|k + The quicˇk brown"}, ); 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( indoc! {" - The |quick + The ˇquick brown"}, indoc! {" - |The quick + ˇThe quick brown"}, ); cx.assert( indoc! {" - |The quick + ˇThe quick brown"}, indoc! {" - |The quick + ˇThe quick brown"}, ); } @@ -475,7 +474,7 @@ mod test { cx.assert( indoc! {" - The |quick + The ˇquick brown fox jumps over the lazy dog"}, @@ -483,54 +482,54 @@ mod test { The quick brown fox jumps - over| the lazy dog"}, + overˇ the lazy dog"}, ); cx.assert( indoc! {" The quick brown fox jumps - over| the lazy dog"}, + overˇ the lazy dog"}, indoc! {" The quick brown fox jumps - over| the lazy dog"}, + overˇ the lazy dog"}, ); cx.assert( indoc! {" - The qui|ck + The quiˇck brown"}, indoc! {" The quick - brow|n"}, + browˇn"}, ); cx.assert( indoc! {" - The qui|ck + The quiˇck "}, indoc! {" The quick - |"}, + ˇ"}, ); } #[gpui::test] async fn test_w(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text(indoc! {" - The |quick|-|brown - | - | - |fox_jumps |over - |th||e"}); + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + The ˇquickˇ-ˇbrown + ˇ + ˇ + ˇfox_jumps ˇover + ˇthˇˇe"}); cx.set_state( indoc! {" - |The quick-brown + ˇThe quick-brown fox_jumps over @@ -540,19 +539,19 @@ mod test { for cursor_offset in cursor_offsets { 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 - let (_, cursor_offsets) = marked_text(indoc! {" - The |quick-brown - | - | - |fox_jumps |over - |th||e"}); + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + The ˇquick-brown + ˇ + ˇ + ˇfox_jumps ˇover + ˇthˇˇe"}); cx.set_state( indoc! {" - |The quick-brown + ˇThe quick-brown fox_jumps over @@ -562,22 +561,22 @@ mod test { for cursor_offset in cursor_offsets { 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] async fn test_e(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text(indoc! {" - Th|e quic|k|-brow|n + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + Thˇe quicˇkˇ-browˇn - fox_jump|s ove|r - th|e"}); + fox_jumpˇs oveˇr + thˇe"}); cx.set_state( indoc! {" - |The quick-brown + ˇThe quick-brown fox_jumps over @@ -587,19 +586,19 @@ mod test { for cursor_offset in cursor_offsets { 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 - let (_, cursor_offsets) = marked_text(indoc! {" - Th|e quick-brow|n + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + Thˇe quick-browˇn - fox_jump|s ove|r - th||e"}); + fox_jumpˇs oveˇr + thˇˇe"}); cx.set_state( indoc! {" - |The quick-brown + ˇThe quick-brown fox_jumps over @@ -608,53 +607,53 @@ mod test { ); for cursor_offset in cursor_offsets { 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] async fn test_b(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text(indoc! {" - ||The |quick|-|brown - | - | - |fox_jumps |over - |the"}); + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + ˇˇThe ˇquickˇ-ˇbrown + ˇ + ˇ + ˇfox_jumps ˇover + ˇthe"}); cx.set_state( indoc! {" The quick-brown fox_jumps over - th|e"}, + thˇe"}, Mode::Normal, ); for cursor_offset in cursor_offsets.into_iter().rev() { 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 - let (_, cursor_offsets) = marked_text(indoc! {" - ||The |quick-brown - | - | - |fox_jumps |over - |the"}); + let (_, cursor_offsets) = marked_text_offsets(indoc! {" + ˇˇThe ˇquick-brown + ˇ + ˇ + ˇfox_jumps ˇover + ˇthe"}); cx.set_state( indoc! {" The quick-brown fox_jumps over - th|e"}, + thˇe"}, Mode::Normal, ); for cursor_offset in cursor_offsets.into_iter().rev() { 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 brown fox jumps - over |the lazy dog"}, + over ˇthe lazy dog"}, indoc! {" - The q|uick + The qˇuick brown fox jumps over the lazy dog"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over the lazy dog"}, indoc! {" - The q|uick + The qˇuick brown fox jumps over the lazy dog"}, @@ -707,9 +706,9 @@ mod test { The quick brown fox jumps - over the la|zy dog"}, + over the laˇzy dog"}, indoc! {" - The quic|k + The quicˇk brown fox jumps over the lazy dog"}, @@ -719,9 +718,9 @@ mod test { brown fox jumps - over the la|zy dog"}, + over the laˇzy dog"}, indoc! {" - | + ˇ brown fox jumps over the lazy dog"}, @@ -733,31 +732,31 @@ mod test { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["a"]).mode_after(Mode::Insert); - cx.assert("The q|uick", "The qu|ick"); - cx.assert("The quic|k", "The quick|"); + cx.assert("The qˇuick", "The quˇick"); + cx.assert("The quicˇk", "The quickˇ"); } #[gpui::test] async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; 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("|", "|"); + cx.assert("The qˇuick", "The quickˇ"); + cx.assert("The qˇuick ", "The quick ˇ"); + cx.assert("ˇ", "ˇ"); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - The quick| + The quickˇ brown fox"}, ); cx.assert( indoc! {" - | + ˇ The quick"}, indoc! {" - | + ˇ The quick"}, ); } @@ -766,50 +765,50 @@ mod test { async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["^"]); - cx.assert("The q|uick", "|The quick"); - cx.assert(" The q|uick", " |The quick"); - cx.assert("|", "|"); + cx.assert("The qˇuick", "ˇThe quick"); + cx.assert(" The qˇuick", " ˇThe quick"); + cx.assert("ˇ", "ˇ"); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - |The quick + ˇThe quick brown fox"}, ); cx.assert( indoc! {" - | + ˇ The quick"}, indoc! {" - | + ˇ The quick"}, ); // Indoc disallows trailing whitspace. - cx.assert(" | \nThe quick", " | \nThe quick"); + cx.assert(" ˇ \nThe quick", " ˇ \nThe quick"); } #[gpui::test] async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; 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("|", "|"); + cx.assert("The qˇuick", "ˇThe quick"); + cx.assert(" The qˇuick", " ˇThe quick"); + cx.assert("ˇ", "ˇ"); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - |The quick + ˇThe quick brown fox"}, ); cx.assert( indoc! {" - | + ˇ The quick"}, indoc! {" - | + ˇ The quick"}, ); } @@ -820,20 +819,20 @@ mod test { let mut cx = cx.binding(["shift-d"]); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - The |q + The ˇq brown fox"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -842,15 +841,15 @@ mod test { async fn test_x(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["x"]); - cx.assert("|Test", "|est"); - cx.assert("Te|st", "Te|t"); - cx.assert("Tes|t", "Te|s"); + cx.assert("ˇTest", "ˇest"); + cx.assert("Teˇst", "Teˇt"); + cx.assert("Tesˇt", "Teˇs"); cx.assert( indoc! {" - Tes|t + Tesˇt test"}, indoc! {" - Te|s + Teˇs test"}, ); } @@ -859,16 +858,16 @@ mod test { async fn test_delete_left(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["shift-x"]); - cx.assert("Te|st", "T|st"); - cx.assert("T|est", "|est"); - cx.assert("|Test", "|Test"); + cx.assert("Teˇst", "Tˇst"); + cx.assert("Tˇest", "ˇest"); + cx.assert("ˇTest", "ˇTest"); cx.assert( indoc! {" Test - |test"}, + ˇtest"}, indoc! {" Test - |test"}, + ˇtest"}, ); } @@ -878,78 +877,84 @@ mod test { let mut cx = cx.binding(["o"]).mode_after(Mode::Insert); cx.assert( - "|", + "ˇ", indoc! {" - |"}, + ˇ"}, ); cx.assert( - "The |quick", + "The ˇquick", indoc! {" The quick - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" The quick brown fox - | + ˇ jumps over"}, ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" The quick - | + ˇ brown fox jumps over"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); cx.assert( indoc! {" - fn test() - println!(|);"}, + fn test() { + println!(ˇ); + } + "}, indoc! {" - fn test() + fn test() { println!(); - |"}, + ˇ + } + "}, ); cx.assert( indoc! {" - fn test(|) - println!();"}, + fn test(ˇ) { + println!(); + }"}, indoc! {" - fn test() - | - println!();"}, + fn test() { + ˇ + println!(); + }"}, ); } @@ -959,25 +964,25 @@ mod test { let mut cx = cx.binding(["shift-o"]).mode_after(Mode::Insert); cx.assert( - "|", + "ˇ", indoc! {" - | + ˇ "}, ); cx.assert( - "The |quick", + "The ˇquick", indoc! {" - | + ˇ The quick"}, ); cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" The quick - | + ˇ brown fox jumps over"}, ); @@ -985,20 +990,20 @@ mod test { indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick brown fox - | + ˇ jumps over"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - | + ˇ The quick brown fox jumps over"}, @@ -1006,31 +1011,33 @@ mod test { cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); cx.assert( indoc! {" fn test() - println!(|);"}, + println!(ˇ);"}, indoc! {" fn test() - | + ˇ println!();"}, ); cx.assert( indoc! {" - fn test(|) - println!();"}, + fn test(ˇ) { + println!(); + }"}, indoc! {" - | - fn test() - println!();"}, + ˇ + fn test() { + println!(); + }"}, ); } @@ -1039,43 +1046,43 @@ mod test { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "d"]); - cx.assert("|", "|"); - cx.assert("The |quick", "|"); + cx.assert("ˇ", "ˇ"); + cx.assert("The ˇquick", "ˇ"); cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" The quick - jumps |over"}, + jumps ˇover"}, ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick - brown |fox"}, + brown ˇfox"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - brown| fox + brownˇ fox jumps over"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - |brown fox"}, + ˇbrown fox"}, ); } @@ -1084,46 +1091,46 @@ mod test { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert); - cx.assert("|", "|"); - cx.assert("The |quick", "|"); + cx.assert("ˇ", "ˇ"); + cx.assert("The ˇquick", "ˇ"); cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" The quick - | + ˇ jumps over"}, ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick brown fox - |"}, + ˇ"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - | + ˇ brown fox jumps over"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -1134,7 +1141,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, Mode::Normal, ); @@ -1142,21 +1149,21 @@ mod test { cx.simulate_keystrokes(["d", "d"]); cx.assert_editor_state(indoc! {" The quick brown - the la|zy dog"}); + the laˇzy dog"}); cx.simulate_keystroke("p"); cx.assert_state( indoc! {" The quick brown the lazy dog - |fox jumps over"}, + ˇfox jumps over"}, Mode::Normal, ); cx.set_state( indoc! {" The quick brown - fox [jump}s over + fox «jumpˇ»s over the lazy dog"}, Mode::Visual { line: false }, ); @@ -1164,7 +1171,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox jumps ove|r + fox jumps oveˇr the lazy dog"}, Mode::Normal, ); @@ -1172,7 +1179,7 @@ mod test { cx.assert_state( indoc! {" The quick brown - fox jumps over|jumps + fox jumps overˇjumps the lazy dog"}, Mode::Normal, ); diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 03f213b584..1a7b934a31 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -84,16 +84,16 @@ mod test { async fn test_change_h(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "h"]).mode_after(Mode::Insert); - cx.assert("Te|st", "T|st"); - cx.assert("T|est", "|est"); - cx.assert("|Test", "|Test"); + cx.assert("Teˇst", "Tˇst"); + cx.assert("Tˇest", "ˇest"); + cx.assert("ˇTest", "ˇTest"); cx.assert( indoc! {" Test - |test"}, + ˇtest"}, indoc! {" Test - |test"}, + ˇtest"}, ); } @@ -101,111 +101,111 @@ mod test { async fn test_change_l(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "l"]).mode_after(Mode::Insert); - cx.assert("Te|st", "Te|t"); - cx.assert("Tes|t", "Tes|"); + cx.assert("Teˇst", "Teˇt"); + cx.assert("Tesˇt", "Tesˇ"); } #[gpui::test] async fn test_change_w(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "w"]).mode_after(Mode::Insert); - cx.assert("Te|st", "Te|"); - cx.assert("T|est test", "T| test"); - cx.assert("Test| test", "Test|test"); + cx.assert("Teˇst", "Teˇ"); + cx.assert("Tˇest test", "Tˇ test"); + cx.assert("Testˇ test", "Testˇtest"); cx.assert( indoc! {" - Test te|st + Test teˇst test"}, indoc! {" - Test te| + Test teˇ test"}, ); cx.assert( indoc! {" - Test tes|t + Test tesˇt test"}, indoc! {" - Test tes| + Test tesˇ test"}, ); cx.assert( indoc! {" Test test - | + ˇ test"}, indoc! {" Test test - | + ˇ test"}, ); 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] async fn test_change_e(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "e"]).mode_after(Mode::Insert); - cx.assert("Te|st Test", "Te| Test"); - cx.assert("T|est test", "T| test"); + cx.assert("Teˇst Test", "Teˇ Test"); + cx.assert("Tˇest test", "Tˇ test"); cx.assert( indoc! {" - Test te|st + Test teˇst test"}, indoc! {" - Test te| + Test teˇ test"}, ); cx.assert( indoc! {" - Test tes|t + Test tesˇt test"}, - "Test tes|", + "Test tesˇ", ); cx.assert( indoc! {" Test test - | + ˇ test"}, indoc! {" Test test - | + ˇ test"}, ); 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] async fn test_change_b(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["c", "b"]).mode_after(Mode::Insert); - cx.assert("Te|st Test", "|st Test"); - cx.assert("Test |test", "|test"); - cx.assert("Test1 test2 |test3", "Test1 |test3"); + cx.assert("Teˇst Test", "ˇst Test"); + cx.assert("Test ˇtest", "ˇtest"); + cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3"); cx.assert( indoc! {" Test test - |test"}, + ˇtest"}, indoc! {" - Test | + Test ˇ test"}, ); cx.assert( indoc! {" Test test - | + ˇ test"}, indoc! {" - Test | + Test ˇ test"}, ); 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] @@ -214,20 +214,20 @@ mod test { let mut cx = cx.binding(["c", "$"]).mode_after(Mode::Insert); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - The q| + The qˇ brown fox"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -238,20 +238,20 @@ mod test { let mut cx = cx.binding(["c", "0"]).mode_after(Mode::Insert); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - |uick + ˇuick brown fox"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -263,38 +263,38 @@ mod test { cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" - | + ˇ jumps over"}, ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick - |"}, + ˇ"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - | + ˇ brown fox jumps over"}, ); cx.assert( indoc! {" - | + ˇ brown fox jumps over"}, indoc! {" - | + ˇ brown fox jumps over"}, ); @@ -307,40 +307,40 @@ mod test { cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, indoc! {" The quick - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick brown fox - |"}, + ˇ"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - | + ˇ jumps over"}, ); cx.assert( indoc! {" The quick brown fox - |"}, + ˇ"}, indoc! {" The quick brown fox - |"}, + ˇ"}, ); } @@ -351,46 +351,46 @@ mod test { cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, indoc! {" The quick - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, indoc! {" The quick - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the l|azy"}, + the lˇazy"}, indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, ); } @@ -401,11 +401,11 @@ mod test { cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, indoc! {" - | + ˇ jumps over the lazy"}, ); @@ -414,29 +414,29 @@ mod test { The quick brown fox jumps over - the l|azy"}, - "|", + the lˇazy"}, + "ˇ", ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over the lazy"}, indoc! {" - | + ˇ brown fox jumps over the lazy"}, ); cx.assert( indoc! {" - | + ˇ brown fox jumps over the lazy"}, indoc! {" - | + ˇ brown fox jumps over the lazy"}, diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index ca2c27cf70..c639e604dc 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -46,16 +46,16 @@ mod test { async fn test_delete_h(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "h"]); - cx.assert("Te|st", "T|st"); - cx.assert("T|est", "|est"); - cx.assert("|Test", "|Test"); + cx.assert("Teˇst", "Tˇst"); + cx.assert("Tˇest", "ˇest"); + cx.assert("ˇTest", "ˇTest"); cx.assert( indoc! {" Test - |test"}, + ˇtest"}, indoc! {" Test - |test"}, + ˇtest"}, ); } @@ -63,15 +63,15 @@ mod test { async fn test_delete_l(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "l"]); - cx.assert("|Test", "|est"); - cx.assert("Te|st", "Te|t"); - cx.assert("Tes|t", "Te|s"); + cx.assert("ˇTest", "ˇest"); + cx.assert("Teˇst", "Teˇt"); + cx.assert("Tesˇt", "Teˇs"); cx.assert( indoc! {" - Tes|t + Tesˇt test"}, indoc! {" - Te|s + Teˇs test"}, ); } @@ -80,104 +80,104 @@ mod test { async fn test_delete_w(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "w"]); - cx.assert("Te|st", "T|e"); - cx.assert("T|est test", "T|test"); + cx.assert("Teˇst", "Tˇe"); + cx.assert("Tˇest test", "Tˇtest"); cx.assert( indoc! {" - Test te|st + Test teˇst test"}, indoc! {" - Test t|e + Test tˇe test"}, ); cx.assert( indoc! {" - Test tes|t + Test tesˇt test"}, indoc! {" - Test te|s + Test teˇs test"}, ); cx.assert( indoc! {" Test test - | + ˇ test"}, indoc! {" Test test - | + ˇ test"}, ); 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] async fn test_delete_e(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "e"]); - cx.assert("Te|st Test", "Te| Test"); - cx.assert("T|est test", "T| test"); + cx.assert("Teˇst Test", "Teˇ Test"); + cx.assert("Tˇest test", "Tˇ test"); cx.assert( indoc! {" - Test te|st + Test teˇst test"}, indoc! {" - Test t|e + Test tˇe test"}, ); cx.assert( indoc! {" - Test tes|t + Test tesˇt test"}, - "Test te|s", + "Test teˇs", ); cx.assert( indoc! {" Test test - | + ˇ test"}, indoc! {" Test test - | + ˇ test"}, ); 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] async fn test_delete_b(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; let mut cx = cx.binding(["d", "b"]); - cx.assert("Te|st Test", "|st Test"); - cx.assert("Test |test", "|test"); - cx.assert("Test1 test2 |test3", "Test1 |test3"); + cx.assert("Teˇst Test", "ˇst Test"); + cx.assert("Test ˇtest", "ˇtest"); + cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3"); cx.assert( indoc! {" Test test - |test"}, + ˇtest"}, // Trailing whitespace after cursor indoc! {" - Test| + Testˇ test"}, ); cx.assert( indoc! {" Test test - | + ˇ test"}, // Trailing whitespace after cursor indoc! {" - Test| + Testˇ test"}, ); 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] @@ -186,20 +186,20 @@ mod test { let mut cx = cx.binding(["d", "$"]); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - The |q + The ˇq brown fox"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -210,20 +210,20 @@ mod test { let mut cx = cx.binding(["d", "0"]); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox"}, indoc! {" - |uick + ˇuick brown fox"}, ); cx.assert( indoc! {" The quick - | + ˇ brown fox"}, indoc! {" The quick - | + ˇ brown fox"}, ); } @@ -235,31 +235,31 @@ mod test { cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, - "jumps |over", + "jumps ˇover", ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, - "The qu|ick", + jumps ˇover"}, + "The quˇick", ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, indoc! {" - brown| fox + brownˇ fox jumps over"}, ); cx.assert( indoc! {" - |brown fox + ˇbrown fox jumps over"}, - "|jumps over", + "ˇjumps over", ); } @@ -270,34 +270,34 @@ mod test { cx.assert( indoc! {" The quick - brown |fox + brown ˇfox jumps over"}, - "The qu|ick", + "The quˇick", ); cx.assert( indoc! {" The quick brown fox - jumps |over"}, + jumps ˇover"}, indoc! {" The quick - brown |fox"}, + brown ˇfox"}, ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over"}, - "jumps| over", + "jumpsˇ over", ); cx.assert( indoc! {" The quick brown fox - |"}, + ˇ"}, indoc! {" The quick - |brown fox"}, + ˇbrown fox"}, ); } @@ -308,40 +308,40 @@ mod test { cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, - "The q|uick", + "The qˇuick", ); cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, - "The q|uick", + "The qˇuick", ); cx.assert( indoc! {" The quick brown fox jumps over - the l|azy"}, + the lˇazy"}, indoc! {" The quick brown fox - jumps| over"}, + jumpsˇ over"}, ); cx.assert( indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, indoc! {" The quick brown fox - |jumps over"}, + ˇjumps over"}, ); } @@ -352,11 +352,11 @@ mod test { cx.assert( indoc! {" The quick - brown| fox + brownˇ fox jumps over the lazy"}, indoc! {" - jumps| over + jumpsˇ over the lazy"}, ); cx.assert( @@ -364,28 +364,28 @@ mod test { The quick brown fox jumps over - the l|azy"}, - "|", + the lˇazy"}, + "ˇ", ); cx.assert( indoc! {" - The q|uick + The qˇuick brown fox jumps over the lazy"}, indoc! {" - brown| fox + brownˇ fox jumps over the lazy"}, ); cx.assert( indoc! {" - | + ˇ brown fox jumps over the lazy"}, indoc! {" - |brown fox + ˇbrown fox jumps over the lazy"}, ); @@ -397,7 +397,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, Mode::Normal, ); @@ -407,7 +407,7 @@ mod test { assert_eq!(cx.active_operator(), None); assert_eq!(cx.mode(), Mode::Normal); cx.assert_editor_state(indoc! {" - The qu|ick brown + The quˇick brown fox jumps over the lazy dog"}); } @@ -418,7 +418,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, Mode::Normal, ); diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index f5764ba09b..ecad33ce3f 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -216,7 +216,7 @@ mod test { async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, false).await; cx.simulate_keystrokes(["h", "j", "k", "l"]); - cx.assert_editor_state("hjkl|"); + cx.assert_editor_state("hjklˇ"); } #[gpui::test] @@ -229,24 +229,24 @@ mod test { // Editor acts as though vim is disabled cx.disable_vim(); 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. - cx.set_state("[hjkl}", Mode::Normal); - cx.assert_editor_state("[hjkl}"); + cx.set_state("«hjklˇ»", Mode::Normal); + cx.assert_editor_state("«hjklˇ»"); cx.update_editor(|_, cx| cx.blur()); - cx.assert_editor_state("[hjkl}"); + cx.assert_editor_state("«hjklˇ»"); 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 cx.enable_vim(); assert_eq!(cx.mode(), Mode::Normal); cx.simulate_keystrokes(["h", "h", "h", "l"]); 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.assert_editor_state("hTest|jkl"); + cx.assert_editor_state("hTestˇjkl"); // Disabling and enabling resets to normal mode assert_eq!(cx.mode(), Mode::Insert); @@ -262,7 +262,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, Mode::Normal, ); diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 76fea2e205..3f7b9c0683 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -284,44 +284,44 @@ mod test { .mode_after(Mode::Visual { line: false }); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - The [quick brown - fox jumps }over + The «quick brown + fox jumps ˇ»over the lazy dog"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown fox jumps over - the [lazy }dog"}, + the «lazy ˇ»dog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" The quick brown - fox jumps [over - }the lazy dog"}, + fox jumps «over + ˇ»the lazy dog"}, ); let mut cx = cx .binding(["v", "b", "k"]) .mode_after(Mode::Visual { line: false }); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - {The q]uick brown + «ˇThe q»uick brown fox jumps over the lazy dog"}, ); @@ -329,20 +329,20 @@ mod test { indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown - {fox jumps over - the l]azy dog"}, + «ˇfox jumps over + the l»azy dog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" - The {quick brown - fox jumps o]ver + The «ˇquick brown + fox jumps o»ver the lazy dog"}, ); } @@ -351,51 +351,51 @@ mod test { async fn test_visual_delete(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; 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"]); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - The |ver + The ˇver the lazy dog"}, ); // Test pasting code copied on delete cx.simulate_keystrokes(["j", "p"]); cx.assert_editor_state(indoc! {" The ver - the l|quick brown + the lˇquick brown fox jumps oazy dog"}); cx.assert( indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown fox jumps over - the |og"}, + the ˇog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" The quick brown - fox jumps |he lazy dog"}, + fox jumps ˇhe lazy dog"}, ); let mut cx = cx.binding(["v", "b", "k", "x"]); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - |uick brown + ˇuick brown fox jumps over the lazy dog"}, ); @@ -403,18 +403,18 @@ mod test { indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown - |azy dog"}, + ˇazy dog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" - The |ver + The ˇver the lazy dog"}, ); } @@ -425,68 +425,68 @@ mod test { let mut cx = cx.binding(["shift-v", "x"]); cx.assert( indoc! {" - The qu|ick brown + The quˇick brown fox jumps over the lazy dog"}, indoc! {" - fox ju|mps over + fox juˇmps over the lazy dog"}, ); // Test pasting code copied on delete cx.simulate_keystroke("p"); cx.assert_editor_state(indoc! {" fox jumps over - |The quick brown + ˇThe quick brown the lazy dog"}); cx.assert( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, indoc! {" The quick brown - the la|zy dog"}, + the laˇzy dog"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the la|zy dog"}, + the laˇzy dog"}, indoc! {" The quick brown - fox ju|mps over"}, + fox juˇmps over"}, ); let mut cx = cx.binding(["shift-v", "j", "x"]); cx.assert( indoc! {" - The qu|ick brown + The quˇick brown fox jumps over the lazy dog"}, - "the la|zy dog", + "the laˇzy dog", ); // Test pasting code copied on delete cx.simulate_keystroke("p"); cx.assert_editor_state(indoc! {" the lazy dog - |The quick brown + ˇThe quick brown fox jumps over"}); cx.assert( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, - "The qu|ick brown", + "The quˇick brown", ); cx.assert( indoc! {" The quick brown fox jumps over - the la|zy dog"}, + the laˇzy dog"}, indoc! {" 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) { let cx = VimTestContext::new(cx, true).await; 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); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - The |ver + The ˇver the lazy dog"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown fox jumps over - the |og"}, + the ˇog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" 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); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - |uick brown + ˇuick brown fox jumps over the lazy dog"}, ); @@ -539,18 +539,18 @@ mod test { indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown - |azy dog"}, + ˇazy dog"}, ); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" - The |ver + The ˇver the lazy dog"}, ); } @@ -561,11 +561,11 @@ mod test { let mut cx = cx.binding(["shift-v", "c"]).mode_after(Mode::Insert); cx.assert( indoc! {" - The qu|ick brown + The quˇick brown fox jumps over the lazy dog"}, indoc! {" - | + ˇ fox jumps over the lazy dog"}, ); @@ -574,37 +574,37 @@ mod test { cx.assert_editor_state(indoc! {" fox jumps over - |The quick brown + ˇThe quick brown the lazy dog"}); cx.assert( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, indoc! {" The quick brown - | + ˇ the lazy dog"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the la|zy dog"}, + the laˇzy dog"}, indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, ); let mut cx = cx.binding(["shift-v", "j", "c"]).mode_after(Mode::Insert); cx.assert( indoc! {" - The qu|ick brown + The quˇick brown fox jumps over the lazy dog"}, indoc! {" - | + ˇ the lazy dog"}, ); // Test pasting code copied on delete @@ -612,26 +612,26 @@ mod test { cx.assert_editor_state(indoc! {" the lazy dog - |The quick brown + ˇThe quick brown fox jumps over"}); cx.assert( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, indoc! {" The quick brown - |"}, + ˇ"}, ); cx.assert( indoc! {" The quick brown fox jumps over - the la|zy dog"}, + the laˇzy dog"}, indoc! {" The quick brown fox jumps over - |"}, + ˇ"}, ); } @@ -639,16 +639,16 @@ mod test { async fn test_visual_yank(cx: &mut gpui::TestAppContext) { let cx = VimTestContext::new(cx, true).await; 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")); let mut cx = cx.binding(["v", "w", "j", "y"]); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, ); @@ -659,21 +659,21 @@ mod test { indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, ); cx.assert_clipboard_content(Some("lazy d")); cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, ); cx.assert_clipboard_content(Some(indoc! {" @@ -682,11 +682,11 @@ mod test { let mut cx = cx.binding(["v", "b", "k", "y"]); cx.assert( indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, indoc! {" - |The quick brown + ˇThe quick brown fox jumps over the lazy dog"}, ); @@ -695,10 +695,10 @@ mod test { indoc! {" The quick brown fox jumps over - the |lazy dog"}, + the ˇlazy dog"}, indoc! {" The quick brown - |fox jumps over + ˇfox jumps over the lazy dog"}, ); cx.assert_clipboard_content(Some(indoc! {" @@ -707,10 +707,10 @@ mod test { cx.assert( indoc! {" The quick brown - fox jumps |over + fox jumps ˇover the lazy dog"}, indoc! {" - The |quick brown + The ˇquick brown fox jumps over the lazy dog"}, ); @@ -725,7 +725,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox [jump}s over + fox «jumpˇ»s over the lazy dog"}, Mode::Visual { line: false }, ); @@ -733,7 +733,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox jump|s over + fox jumpˇs over the lazy dog"}, Mode::Normal, ); @@ -741,7 +741,7 @@ mod test { cx.assert_state( indoc! {" The quick brown - fox jumps|jumps over + fox jumpsˇjumps over the lazy dog"}, Mode::Normal, ); @@ -749,7 +749,7 @@ mod test { cx.set_state( indoc! {" The quick brown - fox ju|mps over + fox juˇmps over the lazy dog"}, Mode::Visual { line: true }, ); @@ -757,13 +757,13 @@ mod test { cx.assert_state( indoc! {" The quick brown - the la|zy dog"}, + the laˇzy dog"}, Mode::Normal, ); cx.set_state( indoc! {" The quick brown - the [laz}y dog"}, + the «lazˇ»y dog"}, Mode::Visual { line: false }, ); cx.simulate_keystroke("p"); @@ -771,7 +771,7 @@ mod test { indoc! {" The quick brown the - |fox jumps over + ˇfox jumps over dog"}, Mode::Normal, );