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_before(selection.start.to_offset(&map, Bias::Left)),
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 }
67
68 pub fn jump(&mut self, text: Arc<str>, line: bool, cx: &mut ViewContext<Self>) {
69 self.pop_operator(cx);
70
71 let anchors = match &*text {
72 "{" | "}" => self.update_editor(cx, |_, editor, cx| {
73 let (map, selections) = editor.selections.all_display(cx);
74 selections
75 .into_iter()
76 .map(|selection| {
77 let point = if &*text == "{" {
78 movement::start_of_paragraph(&map, selection.head(), 1)
79 } else {
80 movement::end_of_paragraph(&map, selection.head(), 1)
81 };
82 map.buffer_snapshot
83 .anchor_before(point.to_offset(&map, Bias::Left))
84 })
85 .collect::<Vec<Anchor>>()
86 }),
87 "." => self.change_list.last().cloned(),
88 _ => self.marks.get(&*text).cloned(),
89 };
90
91 let Some(anchors) = anchors else { return };
92
93 let is_active_operator = self.active_operator().is_some();
94 if is_active_operator {
95 if let Some(anchor) = anchors.last() {
96 self.motion(
97 Motion::Jump {
98 anchor: *anchor,
99 line,
100 },
101 cx,
102 )
103 }
104 } else {
105 self.update_editor(cx, |_, editor, cx| {
106 let map = editor.snapshot(cx);
107 let mut ranges: Vec<Range<Anchor>> = Vec::new();
108 for mut anchor in anchors {
109 if line {
110 let mut point = anchor.to_display_point(&map.display_snapshot);
111 point = motion::first_non_whitespace(&map.display_snapshot, false, point);
112 anchor = map
113 .display_snapshot
114 .buffer_snapshot
115 .anchor_before(point.to_point(&map.display_snapshot));
116 }
117 if ranges.last() != Some(&(anchor..anchor)) {
118 ranges.push(anchor..anchor);
119 }
120 }
121 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
122 s.select_anchor_ranges(ranges)
123 })
124 });
125 }
126 }
127}
128
129pub fn jump_motion(
130 map: &DisplaySnapshot,
131 anchor: Anchor,
132 line: bool,
133) -> (DisplayPoint, SelectionGoal) {
134 let mut point = anchor.to_display_point(map);
135 if line {
136 point = motion::first_non_whitespace(map, false, point)
137 }
138
139 (point, SelectionGoal::None)
140}