vim_test_context.rs

  1use std::ops::{Deref, DerefMut};
  2
  3use editor::test::EditorTestContext;
  4use gpui::json::json;
  5use project::Project;
  6use workspace::{pane, AppState, WorkspaceHandle};
  7
  8use crate::{state::Operator, *};
  9
 10pub struct VimTestContext<'a> {
 11    cx: EditorTestContext<'a>,
 12}
 13
 14impl<'a> VimTestContext<'a> {
 15    pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
 16        cx.update(|cx| {
 17            editor::init(cx);
 18            pane::init(cx);
 19            crate::init(cx);
 20
 21            settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap();
 22        });
 23
 24        let params = cx.update(AppState::test);
 25        let project = Project::test(params.fs.clone(), [], cx).await;
 26
 27        cx.update(|cx| {
 28            cx.update_global(|settings: &mut Settings, _| {
 29                settings.vim_mode = enabled;
 30            });
 31        });
 32
 33        params
 34            .fs
 35            .as_fake()
 36            .insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
 37            .await;
 38
 39        let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
 40        project
 41            .update(cx, |project, cx| {
 42                project.find_or_create_local_worktree("/root", true, cx)
 43            })
 44            .await
 45            .unwrap();
 46        cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
 47            .await;
 48
 49        let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
 50        let item = workspace
 51            .update(cx, |workspace, cx| workspace.open_path(file, true, cx))
 52            .await
 53            .expect("Could not open test file");
 54
 55        let editor = cx.update(|cx| {
 56            item.act_as::<Editor>(cx)
 57                .expect("Opened test file wasn't an editor")
 58        });
 59        editor.update(cx, |_, cx| cx.focus_self());
 60
 61        Self {
 62            cx: EditorTestContext {
 63                cx,
 64                window_id,
 65                editor,
 66            },
 67        }
 68    }
 69
 70    pub fn enable_vim(&mut self) {
 71        self.cx.update(|cx| {
 72            cx.update_global(|settings: &mut Settings, _| {
 73                settings.vim_mode = true;
 74            });
 75        })
 76    }
 77
 78    pub fn disable_vim(&mut self) {
 79        self.cx.update(|cx| {
 80            cx.update_global(|settings: &mut Settings, _| {
 81                settings.vim_mode = false;
 82            });
 83        })
 84    }
 85
 86    pub fn mode(&mut self) -> Mode {
 87        self.cx.read(|cx| cx.global::<Vim>().state.mode)
 88    }
 89
 90    pub fn active_operator(&mut self) -> Option<Operator> {
 91        self.cx
 92            .read(|cx| cx.global::<Vim>().state.operator_stack.last().copied())
 93    }
 94
 95    pub fn set_state(&mut self, text: &str, mode: Mode) {
 96        self.cx.update(|cx| {
 97            Vim::update(cx, |vim, cx| {
 98                vim.switch_mode(mode, cx);
 99            })
100        });
101        self.cx.set_state(text);
102    }
103
104    pub fn assert_binding<const COUNT: usize>(
105        &mut self,
106        keystrokes: [&str; COUNT],
107        initial_state: &str,
108        initial_mode: Mode,
109        state_after: &str,
110        mode_after: Mode,
111    ) {
112        self.set_state(initial_state, initial_mode);
113        self.cx.simulate_keystrokes(keystrokes);
114        self.cx.assert_editor_state(state_after);
115        assert_eq!(self.mode(), mode_after);
116        assert_eq!(self.active_operator(), None);
117    }
118
119    pub fn binding<const COUNT: usize>(
120        mut self,
121        keystrokes: [&'static str; COUNT],
122    ) -> VimBindingTestContext<'a, COUNT> {
123        let mode = self.mode();
124        VimBindingTestContext::new(keystrokes, mode, mode, self)
125    }
126
127    pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) {
128        self.cx.update(|cx| {
129            let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned());
130            let expected_content = expected_content.map(|content| content.to_owned());
131            assert_eq!(actual_content, expected_content);
132        })
133    }
134}
135
136impl<'a> Deref for VimTestContext<'a> {
137    type Target = EditorTestContext<'a>;
138
139    fn deref(&self) -> &Self::Target {
140        &self.cx
141    }
142}
143
144impl<'a> DerefMut for VimTestContext<'a> {
145    fn deref_mut(&mut self) -> &mut Self::Target {
146        &mut self.cx
147    }
148}
149
150pub struct VimBindingTestContext<'a, const COUNT: usize> {
151    cx: VimTestContext<'a>,
152    keystrokes_under_test: [&'static str; COUNT],
153    mode_before: Mode,
154    mode_after: Mode,
155}
156
157impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> {
158    pub fn new(
159        keystrokes_under_test: [&'static str; COUNT],
160        mode_before: Mode,
161        mode_after: Mode,
162        cx: VimTestContext<'a>,
163    ) -> Self {
164        Self {
165            cx,
166            keystrokes_under_test,
167            mode_before,
168            mode_after,
169        }
170    }
171
172    pub fn binding<const NEW_COUNT: usize>(
173        self,
174        keystrokes_under_test: [&'static str; NEW_COUNT],
175    ) -> VimBindingTestContext<'a, NEW_COUNT> {
176        VimBindingTestContext {
177            keystrokes_under_test,
178            cx: self.cx,
179            mode_before: self.mode_before,
180            mode_after: self.mode_after,
181        }
182    }
183
184    pub fn mode_after(mut self, mode_after: Mode) -> Self {
185        self.mode_after = mode_after;
186        self
187    }
188
189    pub fn assert(&mut self, initial_state: &str, state_after: &str) {
190        self.cx.assert_binding(
191            self.keystrokes_under_test,
192            initial_state,
193            self.mode_before,
194            state_after,
195            self.mode_after,
196        )
197    }
198}
199
200impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> {
201    type Target = VimTestContext<'a>;
202
203    fn deref(&self) -> &Self::Target {
204        &self.cx
205    }
206}
207
208impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> {
209    fn deref_mut(&mut self) -> &mut Self::Target {
210        &mut self.cx
211    }
212}