rewrap.rs

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