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