Merge branch 'main' into new-button

Mikayla Maki created

Change summary

assets/icons/plus_12.svg                        |   3 
assets/icons/plus_16.svg                        |   3 
assets/icons/plus_8.svg                         |   3 
crates/editor/src/display_map.rs                |  54 
crates/editor/src/editor.rs                     | 565 ++++++++----------
crates/editor/src/highlight_matching_bracket.rs |  91 +-
crates/editor/src/hover_popover.rs              |  44 
crates/editor/src/link_go_to_definition.rs      | 166 +---
crates/editor/src/mouse_context_menu.rs         |  24 
crates/editor/src/movement.rs                   | 148 ++--
crates/editor/src/test.rs                       | 317 ++--------
crates/util/Cargo.toml                          |   9 
crates/util/src/lib.rs                          |   2 
crates/util/src/test/marked_text.rs             | 284 +++++++--
crates/vim/src/insert.rs                        |   4 
crates/vim/src/normal.rs                        | 411 +++++++------
crates/vim/src/normal/change.rs                 | 148 ++--
crates/vim/src/normal/delete.rs                 | 158 ++--
crates/vim/src/vim.rs                           |  18 
crates/vim/src/visual.rs                        | 182 +++---
20 files changed, 1,257 insertions(+), 1,377 deletions(-)

Detailed changes

assets/icons/plus_12.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6 2.5V9.5M2.5 6H9.5" stroke="white" stroke-linecap="round"/>
+</svg>

assets/icons/plus_16.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 2V14M2 8H14" stroke="white" stroke-width="1.71429" stroke-linecap="round"/>
+</svg>

assets/icons/plus_8.svg 🔗

@@ -0,0 +1,3 @@
+<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 1.5V6.5M1.5 4H6.5" stroke="white" stroke-linecap="round"/>
+</svg>

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]

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<tr::{foo, bar}\n\n  {]b[az.qux()}",
-                vec![('<', '>'), ('[', ']')],
-                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<tr::{foo, bar}\n\n  ]{b[az.qux()}",
-                vec![('<', '>'), ('[', ']')],
-                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<tr::{foo, bar}\n\n  {]b[az.qux()}",
-                vec![('<', '>'), ('[', ']')],
-                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"});
+            ˇ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"});
+            \tˇ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]
@@ -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<Editor>, 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<Editor>, 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::<usize>(cx), selection_ranges);
             }
+
             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
+                "},
             );
 
             // Can't move earlier than the first tab stop
@@ -9991,9 +9938,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
+                "},
             );
 
             assert!(editor.move_to_next_snippet_tabstop(cx));
@@ -10001,9 +9949,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
+                "},
             );
 
             editor.move_to_prev_snippet_tabstop(cx);

crates/editor/src/highlight_matching_bracket.rs 🔗

@@ -32,14 +32,11 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
 
 #[cfg(test)]
 mod tests {
+    use super::*;
+    use crate::test::EditorLspTestContext;
     use indoc::indoc;
-
     use language::{BracketPair, Language, LanguageConfig};
 
-    use crate::test::EditorLspTestContext;
-
-    use super::*;
-
     #[gpui::test]
     async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
         let mut cx = EditorLspTestContext::new(
@@ -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::<MatchingBracketHighlight>(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::<MatchingBracketHighlight>(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::<MatchingBracketHighlight>(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::<MatchingBracketHighlight>(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("Te<st arg>ument") {
-                    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::<MatchingBracketHighlight>(indoc! {r#"
             pub fn test("Test argument") {
                 another_test(1, 2, 3);
-            }"#});
+            }
+        "#});
     }
 }

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::<lsp::request::HoverRequest, _, _>(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::<lsp::request::HoverRequest, _, _>(|_, _| 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::<lsp::request::HoverRequest, _, _>(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::<lsp::request::HoverRequest, _, _>(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),
@@ -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::<LinkGoToDefinitionState>(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::<GotoDefinition, _, _>(move |url, _, _| async move {
@@ -575,11 +563,8 @@ mod tests {
         requests.next().await;
         cx.foreground().run_until_parked();
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<GotoDefinition, _, _>(move |url, _, _| async move {
@@ -743,20 +705,14 @@ mod tests {
         cx.foreground().run_until_parked();
 
         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<LinkGoToDefinitionState>(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::<GotoDefinition, _, _>(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(); }
         "});
     }
 }

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()));
     }
 }

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]

crates/editor/src/test.rs 🔗

@@ -1,35 +1,29 @@
-use std::{
-    any::TypeId,
-    ops::{Deref, DerefMut, Range},
-    sync::Arc,
+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 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 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::{marked_text, marked_text_ranges, marked_text_ranges_by, SetEqError, TextRangeMarker},
+    test::{generate_marked_text, marked_text_offsets, marked_text_ranges},
 };
 use workspace::{pane, AppState, Workspace, WorkspaceHandle};
 
-use crate::{
-    display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
-    multi_buffer::ToPointUtf16,
-    AnchorRangeExt, Autoscroll, DisplayPoint, Editor, EditorMode, MultiBuffer, ToPoint,
-};
-
 #[cfg(test)]
 #[ctor::ctor]
 fn init_logger() {
@@ -43,7 +37,7 @@ pub fn marked_display_snapshot(
     text: &str,
     cx: &mut gpui::MutableAppContext,
 ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
-    let (unmarked_text, markers) = marked_text(text);
+    let (unmarked_text, markers) = marked_text_offsets(text);
 
     let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
     let font_id = cx
@@ -65,7 +59,7 @@ pub fn marked_display_snapshot(
 }
 
 pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
-    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<Editor>,
 ) {
-    let (unmarked_text, text_ranges) = marked_text_ranges(marked_text);
-
+    let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
     assert_eq!(editor.text(cx), unmarked_text);
     assert_eq!(editor.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<Range<usize>> {
+        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<language::Anchor> {
-        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)
-    }
-
-    // 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,
-        );
+        snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
     }
 
-    pub fn set_state_by(&mut self, range_markers: Vec<TextRangeMarker>, 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<Range<usize>> = 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<Tag: 'static>(&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<Range<usize>> = self.update_editor(|editor, cx| {
             let snapshot = editor.snapshot(cx);
             editor
@@ -289,175 +246,61 @@ 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<Tag: ?Sized + 'static>(&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<Range<usize>> = snapshot
-            .display_snapshot
             .highlight_ranges::<Tag>()
             .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<Selection<usize>>) {
-        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<Range<usize>>) {
+        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<Range<usize>>,
-        expected_reverse_selections: Vec<Range<usize>>,
-        expected_forward_selections: Vec<Range<usize>>,
-        asserted_text: Option<String>,
+        expected_selections: Vec<Range<usize>>,
+        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::<usize>(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::<usize>(cx))
+            .into_iter()
+            .map(|s| {
+                if s.reversed {
+                    s.end..s.start
+                } else {
+                    s.start..s.end
                 }
-
-                (empty_selections, reverse_selections, forward_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<String>> =
-            set_eq!(expected_empty_selections, empty_selections)
-                .map_err(|err| {
-                    err.map(|missing| {
-                        let mut error_text = unmarked_text.clone();
-                        error_text.insert(missing.start, '|');
-                        error_text
-                    })
-                })
-                .and_then(|_| {
-                    set_eq!(expected_reverse_selections, reverse_selections).map_err(|err| {
-                        err.map(|missing| {
-                            let mut error_text = unmarked_text.clone();
-                            error_text.insert(missing.start, '{');
-                            error_text.insert(missing.end, ']');
-                            error_text
-                        })
-                    })
-                })
-                .and_then(|_| {
-                    set_eq!(expected_forward_selections, forward_selections).map_err(|err| {
-                        err.map(|missing| {
-                            let mut error_text = unmarked_text.clone();
-                            error_text.insert(missing.start, '[');
-                            error_text.insert(missing.end, '}');
-                            error_text
-                        })
-                    })
-                });
-
-        match all_eq {
-            Err(SetEqError::LeftMissing(location_text)) => {
-                panic!(
-                    indoc! {"
-                        Editor has extra selection
-                        Extra Selection Location:
-                        {}
-                        Asserted selections:
-                        {}
-                        Actual selections:
-                        {}"},
-                    location_text, asserted_selections, actual_selections,
-                );
-            }
-            Err(SetEqError::RightMissing(location_text)) => {
-                panic!(
-                    indoc! {"
-                        Editor is missing empty selection
-                        Missing Selection Location:
-                        {}
-                        Asserted selections:
-                        {}
-                        Actual selections:
-                        {}"},
-                    location_text, asserted_selections, actual_selections,
-                );
-            }
-            _ => {}
-        }
-    }
-
-    fn insert_markers(
-        &mut self,
-        empty_selections: &Vec<Range<usize>>,
-        reverse_selections: &Vec<Range<usize>>,
-        forward_selections: &Vec<Range<usize>>,
-    ) -> String {
-        let mut editor_text_with_selections = self.buffer_text();
-        let mut selection_marks = BTreeMap::new();
-        for range in empty_selections {
-            selection_marks.insert(&range.start, '|');
-        }
-        for range in reverse_selections {
-            selection_marks.insert(&range.start, '{');
-            selection_marks.insert(&range.end, ']');
-        }
-        for range in forward_selections {
-            selection_marks.insert(&range.start, '[');
-            selection_marks.insert(&range.end, '}');
-        }
-        for (offset, mark) in selection_marks.into_iter().rev() {
-            editor_text_with_selections.insert(*offset, mark);
+            })
+            .collect::<Vec<_>>();
+        let actual_marked_text =
+            generate_marked_text(&self.buffer_text(), &actual_selections, true);
+        if expected_selections != actual_selections {
+            panic!(
+                indoc! {"
+                    Editor has unexpected selections.
+
+                    Expected selections:
+                    {}
+
+                    Actual selections:
+                    {}
+                "},
+                expected_marked_text, actual_marked_text,
+            );
         }
-
-        editor_text_with_selections
     }
 }
 
@@ -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<usize>) -> lsp::Range {

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"] }

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;

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

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");
     }
 }

crates/vim/src/normal.rs 🔗

@@ -297,8 +297,7 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext<Workspace>) {
 #[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 },
         );

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"},

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,
         );

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,
         );

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,
         );