1use std::ops::{Deref, DerefMut};
2
3use editor::test::{
4 editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
5};
6use futures::Future;
7use gpui::{Context, View, VisualContext};
8use lsp::request;
9use search::BufferSearchBar;
10
11use crate::{state::Operator, *};
12
13pub struct VimTestContext<'a> {
14 cx: EditorLspTestContext<'a>,
15}
16
17impl<'a> VimTestContext<'a> {
18 pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
19 cx.update(|cx| {
20 search::init(cx);
21 let settings = SettingsStore::test(cx);
22 cx.set_global(settings);
23 command_palette::init(cx);
24 crate::init(cx);
25 });
26 let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
27 Self::new_with_lsp(lsp, enabled)
28 }
29
30 pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> {
31 cx.update(|cx| {
32 search::init(cx);
33 let settings = SettingsStore::test(cx);
34 cx.set_global(settings);
35 command_palette::init(cx);
36 crate::init(cx);
37 });
38 Self::new_with_lsp(
39 EditorLspTestContext::new_typescript(Default::default(), cx).await,
40 true,
41 )
42 }
43
44 pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> VimTestContext<'a> {
45 cx.update(|cx| {
46 cx.update_global(|store: &mut SettingsStore, cx| {
47 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
48 });
49 settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
50 settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
51 });
52
53 // Setup search toolbars and keypress hook
54 cx.update_workspace(|workspace, cx| {
55 observe_keystrokes(cx);
56 workspace.active_pane().update(cx, |pane, cx| {
57 pane.toolbar().update(cx, |toolbar, cx| {
58 let buffer_search_bar = cx.build_view(BufferSearchBar::new);
59 toolbar.add_item(buffer_search_bar, cx);
60 // todo!();
61 // let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
62 // toolbar.add_item(project_search_bar, cx);
63 })
64 });
65 workspace.status_bar().update(cx, |status_bar, cx| {
66 let vim_mode_indicator = cx.build_view(ModeIndicator::new);
67 status_bar.add_right_item(vim_mode_indicator, cx);
68 });
69 });
70
71 Self { cx }
72 }
73
74 pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
75 where
76 T: 'static,
77 F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
78 {
79 let window = self.window.clone();
80 self.update_window(window, move |_, cx| view.update(cx, update))
81 .unwrap()
82 }
83
84 pub fn workspace<F, T>(&mut self, update: F) -> T
85 where
86 F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
87 {
88 self.cx.update_workspace(update)
89 }
90
91 pub fn enable_vim(&mut self) {
92 self.cx.update(|cx| {
93 cx.update_global(|store: &mut SettingsStore, cx| {
94 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(true));
95 });
96 })
97 }
98
99 pub fn disable_vim(&mut self) {
100 self.cx.update(|cx| {
101 cx.update_global(|store: &mut SettingsStore, cx| {
102 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(false));
103 });
104 })
105 }
106
107 pub fn mode(&mut self) -> Mode {
108 self.cx.read(|cx| cx.global::<Vim>().state().mode)
109 }
110
111 pub fn active_operator(&mut self) -> Option<Operator> {
112 self.cx
113 .read(|cx| cx.global::<Vim>().state().operator_stack.last().copied())
114 }
115
116 pub fn set_state(&mut self, text: &str, mode: Mode) {
117 let window = self.window;
118 self.cx.set_state(text);
119 self.update_window(window, |_, cx| {
120 Vim::update(cx, |vim, cx| {
121 vim.switch_mode(mode, true, cx);
122 })
123 });
124 self.cx.cx.cx.run_until_parked();
125 }
126
127 #[track_caller]
128 pub fn assert_state(&mut self, text: &str, mode: Mode) {
129 self.assert_editor_state(text);
130 assert_eq!(self.mode(), mode, "{}", self.assertion_context());
131 }
132
133 pub fn assert_binding<const COUNT: usize>(
134 &mut self,
135 keystrokes: [&str; COUNT],
136 initial_state: &str,
137 initial_mode: Mode,
138 state_after: &str,
139 mode_after: Mode,
140 ) {
141 self.set_state(initial_state, initial_mode);
142 self.cx.simulate_keystrokes(keystrokes);
143 self.cx.assert_editor_state(state_after);
144 assert_eq!(self.mode(), mode_after, "{}", self.assertion_context());
145 assert_eq!(self.active_operator(), None, "{}", self.assertion_context());
146 }
147
148 pub fn handle_request<T, F, Fut>(
149 &self,
150 handler: F,
151 ) -> futures::channel::mpsc::UnboundedReceiver<()>
152 where
153 T: 'static + request::Request,
154 T::Params: 'static + Send,
155 F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
156 Fut: 'static + Send + Future<Output = Result<T::Result>>,
157 {
158 self.cx.handle_request::<T, F, Fut>(handler)
159 }
160}
161
162impl<'a> Deref for VimTestContext<'a> {
163 type Target = EditorTestContext<'a>;
164
165 fn deref(&self) -> &Self::Target {
166 &self.cx
167 }
168}
169
170impl<'a> DerefMut for VimTestContext<'a> {
171 fn deref_mut(&mut self) -> &mut Self::Target {
172 &mut self.cx
173 }
174}