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