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