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::WindowContext;
10use language::SelectionGoal;
11
12use crate::{
13 motion::{self, Motion},
14 Vim,
15};
16
17pub fn create_mark(vim: &mut Vim, text: Arc<str>, tail: bool, cx: &mut WindowContext) {
18 let Some(anchors) = vim.update_active_editor(cx, |_, editor, _| {
19 editor
20 .selections
21 .disjoint_anchors()
22 .iter()
23 .map(|s| if tail { s.tail() } else { s.head() })
24 .collect::<Vec<_>>()
25 }) else {
26 return;
27 };
28 vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
29 vim.clear_operator(cx);
30}
31
32pub fn create_mark_after(vim: &mut Vim, text: Arc<str>, cx: &mut WindowContext) {
33 let Some(anchors) = vim.update_active_editor(cx, |_, editor, cx| {
34 let (map, selections) = editor.selections.all_display(cx);
35 selections
36 .into_iter()
37 .map(|selection| {
38 let point = movement::saturating_right(&map, selection.tail());
39 map.buffer_snapshot
40 .anchor_before(point.to_offset(&map, Bias::Left))
41 })
42 .collect::<Vec<_>>()
43 }) else {
44 return;
45 };
46
47 vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
48 vim.clear_operator(cx);
49}
50
51pub fn create_mark_before(vim: &mut Vim, text: Arc<str>, cx: &mut WindowContext) {
52 let Some(anchors) = vim.update_active_editor(cx, |_, editor, cx| {
53 let (map, selections) = editor.selections.all_display(cx);
54 selections
55 .into_iter()
56 .map(|selection| {
57 let point = movement::saturating_left(&map, selection.head());
58 map.buffer_snapshot
59 .anchor_before(point.to_offset(&map, Bias::Left))
60 })
61 .collect::<Vec<_>>()
62 }) else {
63 return;
64 };
65
66 vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
67 vim.clear_operator(cx);
68}
69
70pub fn jump(text: Arc<str>, line: bool, cx: &mut WindowContext) {
71 let anchors = match &*text {
72 "{" | "}" => Vim::update(cx, |vim, cx| {
73 vim.update_active_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 }),
89 _ => Vim::read(cx).state().marks.get(&*text).cloned(),
90 };
91
92 Vim::update(cx, |vim, cx| {
93 vim.pop_operator(cx);
94 });
95
96 let Some(anchors) = anchors else { return };
97
98 let is_active_operator = Vim::read(cx).state().active_operator().is_some();
99 if is_active_operator {
100 if let Some(anchor) = anchors.last() {
101 motion::motion(
102 Motion::Jump {
103 anchor: *anchor,
104 line,
105 },
106 cx,
107 )
108 }
109 return;
110 } else {
111 Vim::update(cx, |vim, cx| {
112 vim.update_active_editor(cx, |_, editor, cx| {
113 let map = editor.snapshot(cx);
114 let mut ranges: Vec<Range<Anchor>> = Vec::new();
115 for mut anchor in anchors {
116 if line {
117 let mut point = anchor.to_display_point(&map.display_snapshot);
118 point = motion::first_non_whitespace(&map.display_snapshot, false, point);
119 anchor = map
120 .display_snapshot
121 .buffer_snapshot
122 .anchor_before(point.to_point(&map.display_snapshot));
123 }
124 if ranges.last() != Some(&(anchor..anchor)) {
125 ranges.push(anchor..anchor);
126 }
127 }
128 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
129 s.select_anchor_ranges(ranges)
130 })
131 });
132 })
133 }
134}
135
136pub fn jump_motion(
137 map: &DisplaySnapshot,
138 anchor: Anchor,
139 line: bool,
140) -> (DisplayPoint, SelectionGoal) {
141 let mut point = anchor.to_display_point(map);
142 if line {
143 point = motion::first_non_whitespace(map, false, point)
144 }
145
146 (point, SelectionGoal::None)
147}