vim_tests.rs

  1use std::ops::Deref;
  2
  3use editor::{display_map::ToDisplayPoint, DisplayPoint};
  4use gpui::{json::json, keymap::Keystroke, ViewHandle};
  5use language::{Point, Selection};
  6use workspace::{WorkspaceHandle, WorkspaceParams};
  7
  8use crate::*;
  9
 10#[gpui::test]
 11async fn test_insert_mode(cx: &mut gpui::TestAppContext) {
 12    let mut cx = VimTestAppContext::new(cx, "").await;
 13    assert_eq!(cx.mode(), Mode::Normal);
 14    cx.simulate_keystroke("i");
 15    assert_eq!(cx.mode(), Mode::Insert);
 16    cx.simulate_keystrokes(&["T", "e", "s", "t"]);
 17    assert_eq!(cx.editor_text(), "Test".to_owned());
 18    cx.simulate_keystroke("escape");
 19    assert_eq!(cx.mode(), Mode::Normal);
 20}
 21
 22#[gpui::test]
 23async fn test_normal_hjkl(cx: &mut gpui::TestAppContext) {
 24    let mut cx = VimTestAppContext::new(cx, "Test\nTestTest\nTest").await;
 25    assert_eq!(cx.mode(), Mode::Normal);
 26    cx.simulate_keystroke("l");
 27    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(0, 1));
 28    cx.simulate_keystroke("h");
 29    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(0, 0));
 30    cx.simulate_keystroke("j");
 31    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(1, 0));
 32    cx.simulate_keystroke("k");
 33    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(0, 0));
 34
 35    cx.simulate_keystroke("j");
 36    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(1, 0));
 37
 38    // When moving left, cursor does not wrap to the previous line
 39    cx.simulate_keystroke("h");
 40    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(1, 0));
 41
 42    // When moving right, cursor does not reach the line end or wrap to the next line
 43    for _ in 0..9 {
 44        cx.simulate_keystroke("l");
 45    }
 46    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(1, 7));
 47
 48    // Goal column respects the inability to reach the end of the line
 49    cx.simulate_keystroke("k");
 50    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(0, 3));
 51    cx.simulate_keystroke("j");
 52    assert_eq!(cx.newest_selection().head(), DisplayPoint::new(1, 7));
 53}
 54
 55struct VimTestAppContext<'a> {
 56    cx: &'a mut gpui::TestAppContext,
 57    window_id: usize,
 58    editor: ViewHandle<Editor>,
 59}
 60
 61impl<'a> VimTestAppContext<'a> {
 62    async fn new(
 63        cx: &'a mut gpui::TestAppContext,
 64        initial_editor_text: &str,
 65    ) -> VimTestAppContext<'a> {
 66        cx.update(|cx| {
 67            editor::init(cx);
 68            crate::init(cx);
 69        });
 70        let params = cx.update(WorkspaceParams::test);
 71        params
 72            .fs
 73            .as_fake()
 74            .insert_tree(
 75                "/root",
 76                json!({ "dir": { "test.txt": initial_editor_text } }),
 77            )
 78            .await;
 79
 80        let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
 81        params
 82            .project
 83            .update(cx, |project, cx| {
 84                project.find_or_create_local_worktree("/root", true, cx)
 85            })
 86            .await
 87            .unwrap();
 88        cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
 89            .await;
 90
 91        let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
 92        let item = workspace
 93            .update(cx, |workspace, cx| workspace.open_path(file, cx))
 94            .await
 95            .expect("Could not open test file");
 96
 97        let editor = cx.update(|cx| {
 98            item.act_as::<Editor>(cx)
 99                .expect("Opened test file wasn't an editor")
100        });
101        editor.update(cx, |_, cx| cx.focus_self());
102
103        Self {
104            cx,
105            window_id,
106            editor,
107        }
108    }
109
110    fn newest_selection(&mut self) -> Selection<DisplayPoint> {
111        self.editor.update(self.cx, |editor, cx| {
112            let snapshot = editor.snapshot(cx);
113            editor
114                .newest_selection::<Point>(cx)
115                .map(|point| point.to_display_point(&snapshot.display_snapshot))
116        })
117    }
118
119    fn mode(&mut self) -> Mode {
120        self.cx.update(|cx| cx.global::<VimState>().mode)
121    }
122
123    fn editor_text(&mut self) -> String {
124        self.editor
125            .update(self.cx, |editor, cx| editor.snapshot(cx).text())
126    }
127
128    fn simulate_keystroke(&mut self, keystroke_text: &str) {
129        let keystroke = Keystroke::parse(keystroke_text).unwrap();
130        let input = if keystroke.modified() {
131            None
132        } else {
133            Some(keystroke.key.clone())
134        };
135        self.cx
136            .dispatch_keystroke(self.window_id, keystroke, input, false);
137    }
138
139    fn simulate_keystrokes(&mut self, keystroke_texts: &[&str]) {
140        for keystroke_text in keystroke_texts.into_iter() {
141            self.simulate_keystroke(keystroke_text);
142        }
143    }
144}
145
146impl<'a> Deref for VimTestAppContext<'a> {
147    type Target = gpui::TestAppContext;
148
149    fn deref(&self) -> &Self::Target {
150        self.cx
151    }
152}