mark.rs

  1use std::{ops::Range, sync::Arc};
  2
  3use editor::{
  4    display_map::{DisplaySnapshot, ToDisplayPoint},
  5    movement,
  6    scroll::Autoscroll,
  7    Anchor, Bias, DisplayPoint,
  8};
  9use gpui::{Context, Window};
 10use language::SelectionGoal;
 11
 12use crate::{
 13    motion::{self, Motion},
 14    state::Mode,
 15    Vim,
 16};
 17
 18impl Vim {
 19    pub fn create_mark(
 20        &mut self,
 21        text: Arc<str>,
 22        tail: bool,
 23        window: &mut Window,
 24        cx: &mut Context<Self>,
 25    ) {
 26        let Some(anchors) = self.update_editor(window, cx, |_, editor, _, _| {
 27            editor
 28                .selections
 29                .disjoint_anchors()
 30                .iter()
 31                .map(|s| if tail { s.tail() } else { s.head() })
 32                .collect::<Vec<_>>()
 33        }) else {
 34            return;
 35        };
 36        self.marks.insert(text.to_string(), anchors);
 37        self.clear_operator(window, cx);
 38    }
 39
 40    // When handling an action, you must create visual marks if you will switch to normal
 41    // mode without the default selection behavior.
 42    pub(crate) fn store_visual_marks(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 43        if self.mode.is_visual() {
 44            self.create_visual_marks(self.mode, window, cx);
 45        }
 46    }
 47
 48    pub(crate) fn create_visual_marks(
 49        &mut self,
 50        mode: Mode,
 51        window: &mut Window,
 52        cx: &mut Context<Self>,
 53    ) {
 54        let mut starts = vec![];
 55        let mut ends = vec![];
 56        let mut reversed = vec![];
 57
 58        self.update_editor(window, cx, |_, editor, _, cx| {
 59            let (map, selections) = editor.selections.all_display(cx);
 60            for selection in selections {
 61                let end = movement::saturating_left(&map, selection.end);
 62                ends.push(
 63                    map.buffer_snapshot
 64                        .anchor_before(end.to_offset(&map, Bias::Left)),
 65                );
 66                starts.push(
 67                    map.buffer_snapshot
 68                        .anchor_before(selection.start.to_offset(&map, Bias::Left)),
 69                );
 70                reversed.push(selection.reversed)
 71            }
 72        });
 73
 74        self.marks.insert("<".to_string(), starts);
 75        self.marks.insert(">".to_string(), ends);
 76        self.stored_visual_mode.replace((mode, reversed));
 77    }
 78
 79    pub fn jump(
 80        &mut self,
 81        text: Arc<str>,
 82        line: bool,
 83        window: &mut Window,
 84        cx: &mut Context<Self>,
 85    ) {
 86        self.pop_operator(window, cx);
 87
 88        let anchors = match &*text {
 89            "{" | "}" => self.update_editor(window, cx, |_, editor, _, cx| {
 90                let (map, selections) = editor.selections.all_display(cx);
 91                selections
 92                    .into_iter()
 93                    .map(|selection| {
 94                        let point = if &*text == "{" {
 95                            movement::start_of_paragraph(&map, selection.head(), 1)
 96                        } else {
 97                            movement::end_of_paragraph(&map, selection.head(), 1)
 98                        };
 99                        map.buffer_snapshot
100                            .anchor_before(point.to_offset(&map, Bias::Left))
101                    })
102                    .collect::<Vec<Anchor>>()
103            }),
104            "." => self.change_list.last().cloned(),
105            _ => self.marks.get(&*text).cloned(),
106        };
107
108        let Some(mut anchors) = anchors else { return };
109
110        let is_active_operator = self.active_operator().is_some();
111        if is_active_operator {
112            if let Some(anchor) = anchors.last() {
113                self.motion(
114                    Motion::Jump {
115                        anchor: *anchor,
116                        line,
117                    },
118                    window,
119                    cx,
120                )
121            }
122        } else {
123            // Save the last anchor so as to jump to it later.
124            let anchor: Option<Anchor> = anchors.last_mut().map(|anchor| *anchor);
125            let should_jump = self.mode == Mode::Visual
126                || self.mode == Mode::VisualLine
127                || self.mode == Mode::VisualBlock;
128
129            self.update_editor(window, cx, |_, editor, window, cx| {
130                let map = editor.snapshot(window, cx);
131                let mut ranges: Vec<Range<Anchor>> = Vec::new();
132                for mut anchor in anchors {
133                    if line {
134                        let mut point = anchor.to_display_point(&map.display_snapshot);
135                        point = motion::first_non_whitespace(&map.display_snapshot, false, point);
136                        anchor = map
137                            .display_snapshot
138                            .buffer_snapshot
139                            .anchor_before(point.to_point(&map.display_snapshot));
140                    }
141
142                    if ranges.last() != Some(&(anchor..anchor)) {
143                        ranges.push(anchor..anchor);
144                    }
145                }
146
147                if !should_jump {
148                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
149                        s.select_anchor_ranges(ranges)
150                    });
151                }
152            });
153
154            if should_jump {
155                if let Some(anchor) = anchor {
156                    self.motion(Motion::Jump { anchor, line }, window, cx)
157                }
158            }
159        }
160    }
161}
162
163pub fn jump_motion(
164    map: &DisplaySnapshot,
165    anchor: Anchor,
166    line: bool,
167) -> (DisplayPoint, SelectionGoal) {
168    let mut point = anchor.to_display_point(map);
169    if line {
170        point = motion::first_non_whitespace(map, false, point)
171    }
172
173    (point, SelectionGoal::None)
174}