1use crate::Vim;
2use editor::{
3 display_map::ToDisplayPoint,
4 scroll::{scroll_amount::ScrollAmount, VERTICAL_SCROLL_MARGIN},
5 DisplayPoint, Editor,
6};
7use gpui::{actions, AppContext, ViewContext};
8use language::Bias;
9use workspace::Workspace;
10
11actions!(LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown,);
12
13pub fn init(cx: &mut AppContext) {
14 // todo!()
15 // cx.add_action(|_: &mut Workspace, _: &LineDown, cx| {
16 // scroll(cx, false, |c| ScrollAmount::Line(c.unwrap_or(1.)))
17 // });
18 // cx.add_action(|_: &mut Workspace, _: &LineUp, cx| {
19 // scroll(cx, false, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
20 // });
21 // cx.add_action(|_: &mut Workspace, _: &PageDown, cx| {
22 // scroll(cx, false, |c| ScrollAmount::Page(c.unwrap_or(1.)))
23 // });
24 // cx.add_action(|_: &mut Workspace, _: &PageUp, cx| {
25 // scroll(cx, false, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
26 // });
27 // cx.add_action(|_: &mut Workspace, _: &ScrollDown, cx| {
28 // scroll(cx, true, |c| {
29 // if let Some(c) = c {
30 // ScrollAmount::Line(c)
31 // } else {
32 // ScrollAmount::Page(0.5)
33 // }
34 // })
35 // });
36 // cx.add_action(|_: &mut Workspace, _: &ScrollUp, cx| {
37 // scroll(cx, true, |c| {
38 // if let Some(c) = c {
39 // ScrollAmount::Line(-c)
40 // } else {
41 // ScrollAmount::Page(-0.5)
42 // }
43 // })
44 // });
45}
46
47fn scroll(
48 cx: &mut ViewContext<Workspace>,
49 move_cursor: bool,
50 by: fn(c: Option<f32>) -> ScrollAmount,
51) {
52 Vim::update(cx, |vim, cx| {
53 let amount = by(vim.take_count(cx).map(|c| c as f32));
54 vim.update_active_editor(cx, |editor, cx| {
55 scroll_editor(editor, move_cursor, &amount, cx)
56 });
57 })
58}
59
60fn scroll_editor(
61 editor: &mut Editor,
62 preserve_cursor_position: bool,
63 amount: &ScrollAmount,
64 cx: &mut ViewContext<Editor>,
65) {
66 let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
67 let old_top_anchor = editor.scroll_manager.anchor().anchor;
68
69 editor.scroll_screen(amount, cx);
70 if should_move_cursor {
71 let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
72 visible_rows as u32
73 } else {
74 return;
75 };
76
77 let top_anchor = editor.scroll_manager.anchor().anchor;
78
79 editor.change_selections(None, cx, |s| {
80 s.move_with(|map, selection| {
81 let mut head = selection.head();
82 let top = top_anchor.to_display_point(map);
83
84 if preserve_cursor_position {
85 let old_top = old_top_anchor.to_display_point(map);
86 let new_row = top.row() + selection.head().row() - old_top.row();
87 head = map.clip_point(DisplayPoint::new(new_row, head.column()), Bias::Left)
88 }
89 let min_row = top.row() + VERTICAL_SCROLL_MARGIN as u32;
90 let max_row = top.row() + visible_rows - VERTICAL_SCROLL_MARGIN as u32 - 1;
91
92 let new_head = if head.row() < min_row {
93 map.clip_point(DisplayPoint::new(min_row, head.column()), Bias::Left)
94 } else if head.row() > max_row {
95 map.clip_point(DisplayPoint::new(max_row, head.column()), Bias::Left)
96 } else {
97 head
98 };
99 if selection.is_empty() {
100 selection.collapse_to(new_head, selection.goal)
101 } else {
102 selection.set_head(new_head, selection.goal)
103 };
104 })
105 });
106 }
107}
108
109// #[cfg(test)]
110// mod test {
111// use crate::{
112// state::Mode,
113// test::{NeovimBackedTestContext, VimTestContext},
114// };
115// use gpui::geometry::vector::vec2f;
116// use indoc::indoc;
117// use language::Point;
118
119// #[gpui::test]
120// async fn test_scroll(cx: &mut gpui::TestAppContext) {
121// let mut cx = VimTestContext::new(cx, true).await;
122
123// let window = cx.window;
124// let line_height = cx.editor(|editor, cx| editor.style().text.line_height(cx.font_cache()));
125// window.simulate_resize(vec2f(1000., 8.0 * line_height - 1.0), &mut cx);
126
127// cx.set_state(
128// indoc!(
129// "Λone
130// two
131// three
132// four
133// five
134// six
135// seven
136// eight
137// nine
138// ten
139// eleven
140// twelve
141// "
142// ),
143// Mode::Normal,
144// );
145
146// cx.update_editor(|editor, cx| {
147// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
148// });
149// cx.simulate_keystrokes(["ctrl-e"]);
150// cx.update_editor(|editor, cx| {
151// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.))
152// });
153// cx.simulate_keystrokes(["2", "ctrl-e"]);
154// cx.update_editor(|editor, cx| {
155// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.))
156// });
157// cx.simulate_keystrokes(["ctrl-y"]);
158// cx.update_editor(|editor, cx| {
159// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.))
160// });
161
162// // does not select in normal mode
163// cx.simulate_keystrokes(["g", "g"]);
164// cx.update_editor(|editor, cx| {
165// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
166// });
167// cx.simulate_keystrokes(["ctrl-d"]);
168// cx.update_editor(|editor, cx| {
169// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
170// assert_eq!(
171// editor.selections.newest(cx).range(),
172// Point::new(6, 0)..Point::new(6, 0)
173// )
174// });
175
176// // does select in visual mode
177// cx.simulate_keystrokes(["g", "g"]);
178// cx.update_editor(|editor, cx| {
179// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
180// });
181// cx.simulate_keystrokes(["v", "ctrl-d"]);
182// cx.update_editor(|editor, cx| {
183// assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
184// assert_eq!(
185// editor.selections.newest(cx).range(),
186// Point::new(0, 0)..Point::new(6, 1)
187// )
188// });
189// }
190// #[gpui::test]
191// async fn test_ctrl_d_u(cx: &mut gpui::TestAppContext) {
192// let mut cx = NeovimBackedTestContext::new(cx).await;
193
194// cx.set_scroll_height(10).await;
195
196// pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String {
197// let mut text = String::new();
198// for row in 0..rows {
199// let c: char = (start_char as u32 + row as u32) as u8 as char;
200// let mut line = c.to_string().repeat(cols);
201// if row < rows - 1 {
202// line.push('\n');
203// }
204// text += &line;
205// }
206// text
207// }
208// let content = "Λ".to_owned() + &sample_text(26, 2, 'a');
209// cx.set_shared_state(&content).await;
210
211// // skip over the scrolloff at the top
212// // test ctrl-d
213// cx.simulate_shared_keystrokes(["4", "j", "ctrl-d"]).await;
214// cx.assert_state_matches().await;
215// cx.simulate_shared_keystrokes(["ctrl-d"]).await;
216// cx.assert_state_matches().await;
217// cx.simulate_shared_keystrokes(["g", "g", "ctrl-d"]).await;
218// cx.assert_state_matches().await;
219
220// // test ctrl-u
221// cx.simulate_shared_keystrokes(["ctrl-u"]).await;
222// cx.assert_state_matches().await;
223// cx.simulate_shared_keystrokes(["ctrl-d", "ctrl-d", "4", "j", "ctrl-u", "ctrl-u"])
224// .await;
225// cx.assert_state_matches().await;
226// }
227// }