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