1use crate::{Vim, motion::Motion, object::Object, state::Mode};
2use collections::HashMap;
3use editor::{Bias, Editor, RewrapOptions, display_map::ToDisplayPoint, scroll::Autoscroll};
4use gpui::{Context, Window, actions};
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::take_forced_motion(cx);
14 vim.store_visual_marks(window, cx);
15 vim.update_editor(window, cx, |vim, editor, window, cx| {
16 editor.transact(window, cx, |editor, window, cx| {
17 let mut positions = vim.save_selection_starts(editor, cx);
18 editor.rewrap_impl(
19 RewrapOptions {
20 override_language_settings: true,
21 ..Default::default()
22 },
23 cx,
24 );
25 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
26 s.move_with(|map, selection| {
27 if let Some(anchor) = positions.remove(&selection.id) {
28 let mut point = anchor.to_display_point(map);
29 *point.column_mut() = 0;
30 selection.collapse_to(point, SelectionGoal::None);
31 }
32 });
33 });
34 });
35 });
36 if vim.mode.is_visual() {
37 vim.switch_mode(Mode::Normal, true, window, cx)
38 }
39 });
40}
41
42impl Vim {
43 pub(crate) fn rewrap_motion(
44 &mut self,
45 motion: Motion,
46 times: Option<usize>,
47 forced_motion: bool,
48 window: &mut Window,
49 cx: &mut Context<Self>,
50 ) {
51 self.stop_recording(cx);
52 self.update_editor(window, cx, |_, editor, window, cx| {
53 let text_layout_details = editor.text_layout_details(window);
54 editor.transact(window, cx, |editor, window, cx| {
55 let mut selection_starts: HashMap<_, _> = Default::default();
56 editor.change_selections(None, window, cx, |s| {
57 s.move_with(|map, selection| {
58 let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
59 selection_starts.insert(selection.id, anchor);
60 motion.expand_selection(
61 map,
62 selection,
63 times,
64 &text_layout_details,
65 forced_motion,
66 );
67 });
68 });
69 editor.rewrap_impl(
70 RewrapOptions {
71 override_language_settings: true,
72 ..Default::default()
73 },
74 cx,
75 );
76 editor.change_selections(None, window, cx, |s| {
77 s.move_with(|map, selection| {
78 let anchor = selection_starts.remove(&selection.id).unwrap();
79 let mut point = anchor.to_display_point(map);
80 *point.column_mut() = 0;
81 selection.collapse_to(point, SelectionGoal::None);
82 });
83 });
84 });
85 });
86 }
87
88 pub(crate) fn rewrap_object(
89 &mut self,
90 object: Object,
91 around: bool,
92 window: &mut Window,
93 cx: &mut Context<Self>,
94 ) {
95 self.stop_recording(cx);
96 self.update_editor(window, cx, |_, editor, window, cx| {
97 editor.transact(window, cx, |editor, window, cx| {
98 let mut original_positions: HashMap<_, _> = Default::default();
99 editor.change_selections(None, window, cx, |s| {
100 s.move_with(|map, selection| {
101 let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
102 original_positions.insert(selection.id, anchor);
103 object.expand_selection(map, selection, around);
104 });
105 });
106 editor.rewrap_impl(
107 RewrapOptions {
108 override_language_settings: true,
109 ..Default::default()
110 },
111 cx,
112 );
113 editor.change_selections(None, window, cx, |s| {
114 s.move_with(|map, selection| {
115 let anchor = original_positions.remove(&selection.id).unwrap();
116 let mut point = anchor.to_display_point(map);
117 *point.column_mut() = 0;
118 selection.collapse_to(point, SelectionGoal::None);
119 });
120 });
121 });
122 });
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use crate::test::NeovimBackedTestContext;
129
130 #[gpui::test]
131 async fn test_indent_gv(cx: &mut gpui::TestAppContext) {
132 let mut cx = NeovimBackedTestContext::new(cx).await;
133 cx.set_neovim_option("shiftwidth=4").await;
134
135 cx.set_shared_state("ˇhello\nworld\n").await;
136 cx.simulate_shared_keystrokes("v j > g v").await;
137 cx.shared_state()
138 .await
139 .assert_eq("« hello\n ˇ» world\n");
140 }
141}