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