use std::{ops::Range, sync::Arc}; use editor::{ display_map::{DisplaySnapshot, ToDisplayPoint}, movement, scroll::Autoscroll, Anchor, Bias, DisplayPoint, }; use gpui::{Context, Window}; use language::SelectionGoal; use crate::{ motion::{self, Motion}, state::Mode, Vim, }; impl Vim { pub fn create_mark( &mut self, text: Arc, tail: bool, window: &mut Window, cx: &mut Context, ) { let Some(anchors) = self.update_editor(window, cx, |_, editor, _, _| { editor .selections .disjoint_anchors() .iter() .map(|s| if tail { s.tail() } else { s.head() }) .collect::>() }) else { return; }; self.marks.insert(text.to_string(), anchors); self.clear_operator(window, cx); } // When handling an action, you must create visual marks if you will switch to normal // mode without the default selection behavior. pub(crate) fn store_visual_marks(&mut self, window: &mut Window, cx: &mut Context) { if self.mode.is_visual() { self.create_visual_marks(self.mode, window, cx); } } pub(crate) fn create_visual_marks( &mut self, mode: Mode, window: &mut Window, cx: &mut Context, ) { let mut starts = vec![]; let mut ends = vec![]; let mut reversed = vec![]; self.update_editor(window, cx, |_, editor, _, cx| { let (map, selections) = editor.selections.all_display(cx); for selection in selections { let end = movement::saturating_left(&map, selection.end); ends.push( map.buffer_snapshot .anchor_before(end.to_offset(&map, Bias::Left)), ); starts.push( map.buffer_snapshot .anchor_before(selection.start.to_offset(&map, Bias::Left)), ); reversed.push(selection.reversed) } }); self.marks.insert("<".to_string(), starts); self.marks.insert(">".to_string(), ends); self.stored_visual_mode.replace((mode, reversed)); } pub fn jump( &mut self, text: Arc, line: bool, window: &mut Window, cx: &mut Context, ) { self.pop_operator(window, cx); let anchors = match &*text { "{" | "}" => self.update_editor(window, cx, |_, editor, _, cx| { let (map, selections) = editor.selections.all_display(cx); selections .into_iter() .map(|selection| { let point = if &*text == "{" { movement::start_of_paragraph(&map, selection.head(), 1) } else { movement::end_of_paragraph(&map, selection.head(), 1) }; map.buffer_snapshot .anchor_before(point.to_offset(&map, Bias::Left)) }) .collect::>() }), "." => self.change_list.last().cloned(), _ => self.marks.get(&*text).cloned(), }; let Some(anchors) = anchors else { return }; let is_active_operator = self.active_operator().is_some(); if is_active_operator { if let Some(anchor) = anchors.last() { self.motion( Motion::Jump { anchor: *anchor, line, }, window, cx, ) } } else { self.update_editor(window, cx, |_, editor, window, cx| { let map = editor.snapshot(window, cx); let mut ranges: Vec> = Vec::new(); for mut anchor in anchors { if line { let mut point = anchor.to_display_point(&map.display_snapshot); point = motion::first_non_whitespace(&map.display_snapshot, false, point); anchor = map .display_snapshot .buffer_snapshot .anchor_before(point.to_point(&map.display_snapshot)); } if ranges.last() != Some(&(anchor..anchor)) { ranges.push(anchor..anchor); } } editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| { s.select_anchor_ranges(ranges) }) }); } } } pub fn jump_motion( map: &DisplaySnapshot, anchor: Anchor, line: bool, ) -> (DisplayPoint, SelectionGoal) { let mut point = anchor.to_display_point(map); if line { point = motion::first_non_whitespace(map, false, point) } (point, SelectionGoal::None) }