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