vim_test_context.rs

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