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}