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