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