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