1use crate::{motion::Motion, object::Object, state::Mode, Vim};
2use collections::HashMap;
3use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias, Editor, IsVimMode};
4use gpui::actions;
5use language::SelectionGoal;
6use ui::ViewContext;
7
8actions!(vim, [Rewrap]);
9
10pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
11 Vim::action(editor, cx, |vim, _: &Rewrap, cx| {
12 vim.record_current_action(cx);
13 Vim::take_count(cx);
14 vim.store_visual_marks(cx);
15 vim.update_editor(cx, |vim, editor, cx| {
16 editor.transact(cx, |editor, cx| {
17 let mut positions = vim.save_selection_starts(editor, cx);
18 editor.rewrap_impl(IsVimMode::Yes, cx);
19 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
20 s.move_with(|map, selection| {
21 if let Some(anchor) = positions.remove(&selection.id) {
22 let mut point = anchor.to_display_point(map);
23 *point.column_mut() = 0;
24 selection.collapse_to(point, SelectionGoal::None);
25 }
26 });
27 });
28 });
29 });
30 if vim.mode.is_visual() {
31 vim.switch_mode(Mode::Normal, true, cx)
32 }
33 });
34}
35
36impl Vim {
37 pub(crate) fn rewrap_motion(
38 &mut self,
39 motion: Motion,
40 times: Option<usize>,
41 cx: &mut ViewContext<Self>,
42 ) {
43 self.stop_recording(cx);
44 self.update_editor(cx, |_, editor, cx| {
45 let text_layout_details = editor.text_layout_details(cx);
46 editor.transact(cx, |editor, cx| {
47 let mut selection_starts: HashMap<_, _> = Default::default();
48 editor.change_selections(None, cx, |s| {
49 s.move_with(|map, selection| {
50 let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
51 selection_starts.insert(selection.id, anchor);
52 motion.expand_selection(map, selection, times, false, &text_layout_details);
53 });
54 });
55 editor.rewrap_impl(IsVimMode::Yes, cx);
56 editor.change_selections(None, cx, |s| {
57 s.move_with(|map, selection| {
58 let anchor = selection_starts.remove(&selection.id).unwrap();
59 let mut point = anchor.to_display_point(map);
60 *point.column_mut() = 0;
61 selection.collapse_to(point, SelectionGoal::None);
62 });
63 });
64 });
65 });
66 }
67
68 pub(crate) fn rewrap_object(
69 &mut self,
70 object: Object,
71 around: bool,
72 cx: &mut ViewContext<Self>,
73 ) {
74 self.stop_recording(cx);
75 self.update_editor(cx, |_, editor, cx| {
76 editor.transact(cx, |editor, cx| {
77 let mut original_positions: HashMap<_, _> = Default::default();
78 editor.change_selections(None, cx, |s| {
79 s.move_with(|map, selection| {
80 let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
81 original_positions.insert(selection.id, anchor);
82 object.expand_selection(map, selection, around);
83 });
84 });
85 editor.rewrap_impl(IsVimMode::Yes, cx);
86 editor.change_selections(None, cx, |s| {
87 s.move_with(|map, selection| {
88 let anchor = original_positions.remove(&selection.id).unwrap();
89 let mut point = anchor.to_display_point(map);
90 *point.column_mut() = 0;
91 selection.collapse_to(point, SelectionGoal::None);
92 });
93 });
94 });
95 });
96 }
97}
98
99#[cfg(test)]
100mod test {
101 use crate::test::NeovimBackedTestContext;
102
103 #[gpui::test]
104 async fn test_indent_gv(cx: &mut gpui::TestAppContext) {
105 let mut cx = NeovimBackedTestContext::new(cx).await;
106 cx.set_neovim_option("shiftwidth=4").await;
107
108 cx.set_shared_state("ˇhello\nworld\n").await;
109 cx.simulate_shared_keystrokes("v j > g v").await;
110 cx.shared_state()
111 .await
112 .assert_eq("« hello\n ˇ» world\n");
113 }
114}