vim2 (#3618)

Conrad Irwin created

- Uncomment editor event tests
- Uncomment vim search tests

Just command left

Release Notes:

- N/A

Change summary

Cargo.lock                       |   1 
crates/editor2/src/editor.rs     |   1 
crates/vim/src/editor_events.rs  |  47 +++---
crates/vim2/src/editor_events.rs |  61 ++++---
crates/vim2/src/normal/search.rs | 260 +++++++++++++++++-----------------
crates/welcome2/Cargo.toml       |   2 
crates/welcome2/src/welcome.rs   |  26 +-
7 files changed, 200 insertions(+), 198 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -11423,6 +11423,7 @@ dependencies = [
  "theme_selector2",
  "ui2",
  "util",
+ "vim2",
  "workspace2",
 ]
 

crates/editor2/src/editor.rs 🔗

@@ -2171,7 +2171,6 @@ impl Editor {
 
         self.blink_manager.update(cx, BlinkManager::pause_blinking);
         cx.emit(EditorEvent::SelectionsChanged { local });
-        cx.emit(SearchEvent::MatchesInvalidated);
 
         if self.selections.disjoint_anchors().len() == 1 {
             cx.emit(SearchEvent::ActiveMatchChanged)

crates/vim/src/editor_events.rs 🔗

@@ -63,31 +63,30 @@ fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) {
     });
 }
 
-// #[cfg(test)]
-// mod test {
-//     use crate::{test::VimTestContext, Vim};
-//     use editor::Editor;
-//     use gpui::View;
-//     use language::Buffer;
+#[cfg(test)]
+mod test {
+    use crate::{test::VimTestContext, Vim};
+    use editor::Editor;
+    use gpui::View;
+    use language::Buffer;
 
-//     // regression test for blur called with a different active editor
-//     #[gpui::test]
-//     async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
-//         let mut cx = VimTestContext::new(cx, true).await;
+    // regression test for blur called with a different active editor
+    #[gpui::test]
+    async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, true).await;
 
-//         let buffer = cx.add_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
-//         let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
-//         let editor2 = cx.read(|cx| window2.root(cx)).unwrap();
+        let buffer = cx.add_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
+        let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
+        let editor2 = cx.read(|cx| window2.root(cx)).unwrap();
 
-//         cx.update(|cx| {
-//             let vim = Vim::read(cx);
-//             assert_eq!(vim.active_editor.unwrap().id(), editor2.id())
-//         });
+        cx.update(|cx| {
+            let vim = Vim::read(cx);
+            assert_eq!(vim.active_editor.unwrap().id(), editor2.id())
+        });
 
-//         // no panic when blurring an editor in a different window.
-//         cx.update_editor(|editor1, cx| {
-//             todo!()
-//             // editor1.focus_out(cx.handle().into_any(), cx);
-//         });
-//     }
-// }
+        // no panic when blurring an editor in a different window.
+        cx.update_editor(|editor1, cx| {
+            editor1.focus_out(cx.handle().into_any(), cx);
+        });
+    }
+}

crates/vim2/src/editor_events.rs 🔗

@@ -65,35 +65,40 @@ fn released(entity_id: EntityId, cx: &mut WindowContext) {
     });
 }
 
-// #[cfg(test)]
-// mod test {
-//     use crate::{test::VimTestContext, Vim};
-//     use editor::Editor;
-//     use gpui::{Context, Entity};
-//     use language::Buffer;
+#[cfg(test)]
+mod test {
+    use crate::{test::VimTestContext, Vim};
+    use editor::Editor;
+    use gpui::{Context, Entity};
+    use language::Buffer;
 
-//     // regression test for blur called with a different active editor
-//     #[gpui::test]
-//     async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
-//         let mut cx = VimTestContext::new(cx, true).await;
+    // regression test for blur called with a different active editor
+    #[gpui::test]
+    async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, true).await;
 
-//         let buffer = cx.build_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
-//         let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
-//         let editor2 = cx
-//             .update(|cx| window2.update(cx, |editor, cx| cx.view()))
-//             .unwrap();
+        let buffer = cx.build_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
+        let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
+        let editor2 = cx
+            .update(|cx| {
+                window2.update(cx, |_, cx| {
+                    cx.focus_self();
+                    cx.view().clone()
+                })
+            })
+            .unwrap();
 
-//         cx.update(|cx| {
-//             let vim = Vim::read(cx);
-//             assert_eq!(
-//                 vim.active_editor.unwrap().entity_id().unwrap(),
-//                 editor2.entity_id()
-//             )
-//         });
+        cx.update(|cx| {
+            let vim = Vim::read(cx);
+            assert_eq!(
+                vim.active_editor.as_ref().unwrap().entity_id(),
+                editor2.entity_id(),
+            )
+        });
 
-//         // no panic when blurring an editor in a different window.
-//         cx.update_editor(|editor1, cx| {
-//             editor1.focus_out(cx.handle().into_any(), cx);
-//         });
-//     }
-// }
+        // no panic when blurring an editor in a different window.
+        cx.update_editor(|editor1, cx| {
+            editor1.handle_blur(cx);
+        });
+    }
+}

crates/vim2/src/normal/search.rs 🔗

@@ -345,133 +345,133 @@ fn parse_replace_all(query: &str) -> Replacement {
     replacement
 }
 
-// #[cfg(test)]
-// mod test {
-//     use editor::DisplayPoint;
-//     use search::BufferSearchBar;
-
-//     use crate::{state::Mode, test::VimTestContext};
-
-//     #[gpui::test]
-//     async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
-//         let mut cx = VimTestContext::new(cx, true).await;
-//         cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["*"]);
-//         cx.run_until_parked();
-//         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["*"]);
-//         cx.run_until_parked();
-//         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["#"]);
-//         cx.run_until_parked();
-//         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["#"]);
-//         cx.run_until_parked();
-//         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["2", "*"]);
-//         cx.run_until_parked();
-//         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["g", "*"]);
-//         cx.run_until_parked();
-//         cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["n"]);
-//         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-//         cx.simulate_keystrokes(["g", "#"]);
-//         cx.run_until_parked();
-//         cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
-//     }
-
-//     #[gpui::test]
-//     async fn test_search(cx: &mut gpui::TestAppContext) {
-//         let mut cx = VimTestContext::new(cx, true).await;
-
-//         cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
-//         cx.simulate_keystrokes(["/", "c", "c"]);
-
-//         let search_bar = cx.workspace(|workspace, cx| {
-//             workspace
-//                 .active_pane()
-//                 .read(cx)
-//                 .toolbar()
-//                 .read(cx)
-//                 .item_of_type::<BufferSearchBar>()
-//                 .expect("Buffer search bar should be deployed")
-//         });
-
-//         cx.update_view(search_bar, |bar, cx| {
-//             assert_eq!(bar.query(cx), "cc");
-//         });
-
-//         cx.run_until_parked();
-
-//         cx.update_editor(|editor, cx| {
-//             let highlights = editor.all_text_background_highlights(cx);
-//             assert_eq!(3, highlights.len());
-//             assert_eq!(
-//                 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
-//                 highlights[0].0
-//             )
-//         });
-
-//         cx.simulate_keystrokes(["enter"]);
-//         cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
-
-//         // n to go to next/N to go to previous
-//         cx.simulate_keystrokes(["n"]);
-//         cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
-//         cx.simulate_keystrokes(["shift-n"]);
-//         cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
-
-//         // ?<enter> to go to previous
-//         cx.simulate_keystrokes(["?", "enter"]);
-//         cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
-//         cx.simulate_keystrokes(["?", "enter"]);
-//         cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
-
-//         // /<enter> to go to next
-//         cx.simulate_keystrokes(["/", "enter"]);
-//         cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
-
-//         // ?{search}<enter> to search backwards
-//         cx.simulate_keystrokes(["?", "b", "enter"]);
-//         cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
-
-//         // works with counts
-//         cx.simulate_keystrokes(["4", "/", "c"]);
-//         cx.simulate_keystrokes(["enter"]);
-//         cx.assert_state("aa\nbb\ncc\ncˇc\ncc\n", Mode::Normal);
-
-//         // check that searching resumes from cursor, not previous match
-//         cx.set_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
-//         cx.simulate_keystrokes(["/", "d"]);
-//         cx.simulate_keystrokes(["enter"]);
-//         cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
-//         cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
-//         cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
-//         cx.simulate_keystrokes(["/", "b"]);
-//         cx.simulate_keystrokes(["enter"]);
-//         cx.assert_state("aa\nˇbb\ndd\ncc\nbb\n", Mode::Normal);
-//     }
-
-//     #[gpui::test]
-//     async fn test_non_vim_search(cx: &mut gpui::TestAppContext) {
-//         let mut cx = VimTestContext::new(cx, false).await;
-//         cx.set_state("ˇone one one one", Mode::Normal);
-//         cx.simulate_keystrokes(["cmd-f"]);
-//         cx.run_until_parked();
-
-//         cx.assert_editor_state("«oneˇ» one one one");
-//         cx.simulate_keystrokes(["enter"]);
-//         cx.assert_editor_state("one «oneˇ» one one");
-//         cx.simulate_keystrokes(["shift-enter"]);
-//         cx.assert_editor_state("«oneˇ» one one one");
-//     }
-// }
+#[cfg(test)]
+mod test {
+    use editor::DisplayPoint;
+    use search::BufferSearchBar;
+
+    use crate::{state::Mode, test::VimTestContext};
+
+    #[gpui::test]
+    async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, true).await;
+        cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["*"]);
+        cx.run_until_parked();
+        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["*"]);
+        cx.run_until_parked();
+        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["#"]);
+        cx.run_until_parked();
+        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["#"]);
+        cx.run_until_parked();
+        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["2", "*"]);
+        cx.run_until_parked();
+        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["g", "*"]);
+        cx.run_until_parked();
+        cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["n"]);
+        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
+
+        cx.simulate_keystrokes(["g", "#"]);
+        cx.run_until_parked();
+        cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
+    }
+
+    #[gpui::test]
+    async fn test_search(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, true).await;
+
+        cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
+        cx.simulate_keystrokes(["/", "c", "c"]);
+
+        let search_bar = cx.workspace(|workspace, cx| {
+            workspace
+                .active_pane()
+                .read(cx)
+                .toolbar()
+                .read(cx)
+                .item_of_type::<BufferSearchBar>()
+                .expect("Buffer search bar should be deployed")
+        });
+
+        cx.update_view(search_bar, |bar, cx| {
+            assert_eq!(bar.query(cx), "cc");
+        });
+
+        cx.run_until_parked();
+
+        cx.update_editor(|editor, cx| {
+            let highlights = editor.all_text_background_highlights(cx);
+            assert_eq!(3, highlights.len());
+            assert_eq!(
+                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
+                highlights[0].0
+            )
+        });
+
+        cx.simulate_keystrokes(["enter"]);
+        cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
+
+        // n to go to next/N to go to previous
+        cx.simulate_keystrokes(["n"]);
+        cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
+        cx.simulate_keystrokes(["shift-n"]);
+        cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
+
+        // ?<enter> to go to previous
+        cx.simulate_keystrokes(["?", "enter"]);
+        cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
+        cx.simulate_keystrokes(["?", "enter"]);
+        cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
+
+        // /<enter> to go to next
+        cx.simulate_keystrokes(["/", "enter"]);
+        cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
+
+        // ?{search}<enter> to search backwards
+        cx.simulate_keystrokes(["?", "b", "enter"]);
+        cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
+
+        // works with counts
+        cx.simulate_keystrokes(["4", "/", "c"]);
+        cx.simulate_keystrokes(["enter"]);
+        cx.assert_state("aa\nbb\ncc\ncˇc\ncc\n", Mode::Normal);
+
+        // check that searching resumes from cursor, not previous match
+        cx.set_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
+        cx.simulate_keystrokes(["/", "d"]);
+        cx.simulate_keystrokes(["enter"]);
+        cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
+        cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
+        cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
+        cx.simulate_keystrokes(["/", "b"]);
+        cx.simulate_keystrokes(["enter"]);
+        cx.assert_state("aa\nˇbb\ndd\ncc\nbb\n", Mode::Normal);
+    }
+
+    #[gpui::test]
+    async fn test_non_vim_search(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new(cx, false).await;
+        cx.set_state("ˇone one one one", Mode::Normal);
+        cx.simulate_keystrokes(["cmd-f"]);
+        cx.run_until_parked();
+
+        cx.assert_editor_state("«oneˇ» one one one");
+        cx.simulate_keystrokes(["enter"]);
+        cx.assert_editor_state("one «oneˇ» one one");
+        cx.simulate_keystrokes(["shift-enter"]);
+        cx.assert_editor_state("«oneˇ» one one one");
+    }
+}

crates/welcome2/Cargo.toml 🔗

@@ -26,7 +26,7 @@ theme_selector = { package = "theme_selector2", path = "../theme_selector2" }
 util = { path = "../util" }
 picker = { package = "picker2", path = "../picker2" }
 workspace = { package = "workspace2", path = "../workspace2" }
-# vim = { package = "vim2", path = "../vim2" }
+vim = { package = "vim2", path = "../vim2" }
 
 anyhow.workspace = true
 log.workspace = true

crates/welcome2/src/welcome.rs 🔗

@@ -11,6 +11,7 @@ use gpui::{
 use settings::{Settings, SettingsStore};
 use std::sync::Arc;
 use ui::{prelude::*, Checkbox};
+use vim::VimModeSetting;
 use workspace::{
     dock::DockPosition,
     item::{Item, ItemEvent},
@@ -128,29 +129,26 @@ impl Render for WelcomePage {
                         .border_color(cx.theme().colors().border)
                         .rounded_md()
                         .child(
-                            // todo!("vim setting")
                             h_stack()
                                 .gap_2()
                                 .child(
                                     Checkbox::new(
                                         "enable-vim",
-                                        if false
-                                        /* VimSettings::get_global(cx).enabled */
-                                        {
+                                        if VimModeSetting::get_global(cx).0 {
                                             ui::Selection::Selected
                                         } else {
                                             ui::Selection::Unselected
                                         },
-                                    ),
-                                    // .on_click(cx.listener(
-                                    //     move |this, selection, cx| {
-                                    //         this.update_settings::<VimSettings>(
-                                    //             selection,
-                                    //             cx,
-                                    //             |settings, value| settings.enabled = value,
-                                    //         );
-                                    //     },
-                                    // )),
+                                    )
+                                    .on_click(cx.listener(
+                                        move |this, selection, cx| {
+                                            this.update_settings::<VimModeSetting>(
+                                                selection,
+                                                cx,
+                                                |setting, value| *setting = Some(value),
+                                            );
+                                        },
+                                    )),
                                 )
                                 .child(Label::new("Enable vim mode")),
                         )