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(¶ms, 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}