vim_test_context.rs

  1use std::ops::Deref;
  2
  3use editor::{display_map::ToDisplayPoint, Bias, DisplayPoint};
  4use gpui::{json::json, keymap::Keystroke, ViewHandle};
  5use language::{Point, Selection};
  6use util::test::marked_text;
  7use workspace::{WorkspaceHandle, WorkspaceParams};
  8
  9use crate::*;
 10
 11pub struct VimTestContext<'a> {
 12    cx: &'a mut gpui::TestAppContext,
 13    window_id: usize,
 14    editor: ViewHandle<Editor>,
 15}
 16
 17impl<'a> VimTestContext<'a> {
 18    pub async fn new(
 19        cx: &'a mut gpui::TestAppContext,
 20        enabled: bool,
 21        initial_editor_text: &str,
 22    ) -> VimTestContext<'a> {
 23        cx.update(|cx| {
 24            editor::init(cx);
 25            crate::init(cx);
 26
 27            settings::KeyMapFile::load("keymaps/vim.json", cx).unwrap();
 28        });
 29
 30        let params = cx.update(WorkspaceParams::test);
 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(
 42                "/root",
 43                json!({ "dir": { "test.txt": initial_editor_text } }),
 44            )
 45            .await;
 46
 47        let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
 48        params
 49            .project
 50            .update(cx, |project, cx| {
 51                project.find_or_create_local_worktree("/root", true, cx)
 52            })
 53            .await
 54            .unwrap();
 55        cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
 56            .await;
 57
 58        let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
 59        let item = workspace
 60            .update(cx, |workspace, cx| workspace.open_path(file, cx))
 61            .await
 62            .expect("Could not open test file");
 63
 64        let editor = cx.update(|cx| {
 65            item.act_as::<Editor>(cx)
 66                .expect("Opened test file wasn't an editor")
 67        });
 68        editor.update(cx, |_, cx| cx.focus_self());
 69
 70        Self {
 71            cx,
 72            window_id,
 73            editor,
 74        }
 75    }
 76
 77    pub fn enable_vim(&mut self) {
 78        self.cx.update(|cx| {
 79            cx.update_global(|settings: &mut Settings, _| {
 80                settings.vim_mode = true;
 81            });
 82        })
 83    }
 84
 85    pub fn disable_vim(&mut self) {
 86        self.cx.update(|cx| {
 87            cx.update_global(|settings: &mut Settings, _| {
 88                settings.vim_mode = false;
 89            });
 90        })
 91    }
 92
 93    pub fn newest_selection(&mut self) -> Selection<DisplayPoint> {
 94        self.editor.update(self.cx, |editor, cx| {
 95            let snapshot = editor.snapshot(cx);
 96            editor
 97                .newest_selection::<Point>(cx)
 98                .map(|point| point.to_display_point(&snapshot.display_snapshot))
 99        })
100    }
101
102    pub fn mode(&mut self) -> Mode {
103        self.cx.update(|cx| cx.global::<VimState>().mode)
104    }
105
106    pub fn editor_text(&mut self) -> String {
107        self.editor
108            .update(self.cx, |editor, cx| editor.snapshot(cx).text())
109    }
110
111    pub fn simulate_keystroke(&mut self, keystroke_text: &str) {
112        let keystroke = Keystroke::parse(keystroke_text).unwrap();
113        let input = if keystroke.modified() {
114            None
115        } else {
116            Some(keystroke.key.clone())
117        };
118        self.cx
119            .dispatch_keystroke(self.window_id, keystroke, input, false);
120    }
121
122    pub fn simulate_keystrokes(&mut self, keystroke_texts: &[&str]) {
123        for keystroke_text in keystroke_texts.into_iter() {
124            self.simulate_keystroke(keystroke_text);
125        }
126    }
127
128    pub fn assert_newest_selection_head_offset(&mut self, expected_offset: usize) {
129        let actual_head = self.newest_selection().head();
130        let (actual_offset, expected_head) = self.editor.update(self.cx, |editor, cx| {
131            let snapshot = editor.snapshot(cx);
132            (
133                actual_head.to_offset(&snapshot, Bias::Left),
134                expected_offset.to_display_point(&snapshot),
135            )
136        });
137        let mut actual_position_text = self.editor_text();
138        let mut expected_position_text = actual_position_text.clone();
139        actual_position_text.insert(actual_offset, '|');
140        expected_position_text.insert(expected_offset, '|');
141        assert_eq!(
142            actual_head, expected_head,
143            "\nActual Position: {}\nExpected Position: {}",
144            actual_position_text, expected_position_text
145        )
146    }
147
148    pub fn assert_editor_state(&mut self, text: &str) {
149        let (unmarked_text, markers) = marked_text(&text);
150        let editor_text = self.editor_text();
151        assert_eq!(
152            editor_text, unmarked_text,
153            "Unmarked text doesn't match editor text"
154        );
155        let expected_offset = markers[0];
156        let actual_head = self.newest_selection().head();
157        let (actual_offset, expected_head) = self.editor.update(self.cx, |editor, cx| {
158            let snapshot = editor.snapshot(cx);
159            (
160                actual_head.to_offset(&snapshot, Bias::Left),
161                expected_offset.to_display_point(&snapshot),
162            )
163        });
164        let mut actual_position_text = self.editor_text();
165        let mut expected_position_text = actual_position_text.clone();
166        actual_position_text.insert(actual_offset, '|');
167        expected_position_text.insert(expected_offset, '|');
168        assert_eq!(
169            actual_head, expected_head,
170            "\nActual Position: {}\nExpected Position: {}",
171            actual_position_text, expected_position_text
172        )
173    }
174}
175
176impl<'a> Deref for VimTestContext<'a> {
177    type Target = gpui::TestAppContext;
178
179    fn deref(&self) -> &Self::Target {
180        self.cx
181    }
182}