
Release Notes: - vim: Added support for the changelist. `g;` and `g,` to the previous/next change - vim: Added support for the `'.` mark - vim: Added support for `gi` to resume the previous insert
146 lines
4.8 KiB
Rust
146 lines
4.8 KiB
Rust
use std::{ops::Range, sync::Arc};
|
|
|
|
use editor::{
|
|
display_map::{DisplaySnapshot, ToDisplayPoint},
|
|
movement,
|
|
scroll::Autoscroll,
|
|
Anchor, Bias, DisplayPoint,
|
|
};
|
|
use gpui::WindowContext;
|
|
use language::SelectionGoal;
|
|
|
|
use crate::{
|
|
motion::{self, Motion},
|
|
Vim,
|
|
};
|
|
|
|
pub fn create_mark(vim: &mut Vim, text: Arc<str>, tail: bool, cx: &mut WindowContext) {
|
|
let Some(anchors) = vim.update_active_editor(cx, |_, editor, _| {
|
|
editor
|
|
.selections
|
|
.disjoint_anchors()
|
|
.iter()
|
|
.map(|s| if tail { s.tail() } else { s.head() })
|
|
.collect::<Vec<_>>()
|
|
}) else {
|
|
return;
|
|
};
|
|
vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
|
|
vim.clear_operator(cx);
|
|
}
|
|
|
|
pub fn create_mark_after(vim: &mut Vim, text: Arc<str>, cx: &mut WindowContext) {
|
|
let Some(anchors) = vim.update_active_editor(cx, |_, editor, cx| {
|
|
let (map, selections) = editor.selections.all_display(cx);
|
|
selections
|
|
.into_iter()
|
|
.map(|selection| {
|
|
let point = movement::saturating_right(&map, selection.tail());
|
|
map.buffer_snapshot
|
|
.anchor_before(point.to_offset(&map, Bias::Left))
|
|
})
|
|
.collect::<Vec<_>>()
|
|
}) else {
|
|
return;
|
|
};
|
|
|
|
vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
|
|
vim.clear_operator(cx);
|
|
}
|
|
|
|
pub fn create_mark_before(vim: &mut Vim, text: Arc<str>, cx: &mut WindowContext) {
|
|
let Some(anchors) = vim.update_active_editor(cx, |_, editor, cx| {
|
|
let (map, selections) = editor.selections.all_display(cx);
|
|
selections
|
|
.into_iter()
|
|
.map(|selection| {
|
|
let point = movement::saturating_left(&map, selection.head());
|
|
map.buffer_snapshot
|
|
.anchor_before(point.to_offset(&map, Bias::Left))
|
|
})
|
|
.collect::<Vec<_>>()
|
|
}) else {
|
|
return;
|
|
};
|
|
|
|
vim.update_state(|state| state.marks.insert(text.to_string(), anchors));
|
|
vim.clear_operator(cx);
|
|
}
|
|
|
|
pub fn jump(text: Arc<str>, line: bool, cx: &mut WindowContext) {
|
|
let anchors = Vim::update(cx, |vim, cx| {
|
|
vim.pop_operator(cx);
|
|
|
|
match &*text {
|
|
"{" | "}" => vim.update_active_editor(cx, |_, editor, cx| {
|
|
let (map, selections) = editor.selections.all_display(cx);
|
|
selections
|
|
.into_iter()
|
|
.map(|selection| {
|
|
let point = if &*text == "{" {
|
|
movement::start_of_paragraph(&map, selection.head(), 1)
|
|
} else {
|
|
movement::end_of_paragraph(&map, selection.head(), 1)
|
|
};
|
|
map.buffer_snapshot
|
|
.anchor_before(point.to_offset(&map, Bias::Left))
|
|
})
|
|
.collect::<Vec<Anchor>>()
|
|
}),
|
|
"." => vim.state().change_list.last().cloned(),
|
|
_ => vim.state().marks.get(&*text).cloned(),
|
|
}
|
|
});
|
|
|
|
let Some(anchors) = anchors else { return };
|
|
|
|
let is_active_operator = Vim::read(cx).state().active_operator().is_some();
|
|
if is_active_operator {
|
|
if let Some(anchor) = anchors.last() {
|
|
motion::motion(
|
|
Motion::Jump {
|
|
anchor: *anchor,
|
|
line,
|
|
},
|
|
cx,
|
|
)
|
|
}
|
|
return;
|
|
} else {
|
|
Vim::update(cx, |vim, cx| {
|
|
vim.update_active_editor(cx, |_, editor, cx| {
|
|
let map = editor.snapshot(cx);
|
|
let mut ranges: Vec<Range<Anchor>> = Vec::new();
|
|
for mut anchor in anchors {
|
|
if line {
|
|
let mut point = anchor.to_display_point(&map.display_snapshot);
|
|
point = motion::first_non_whitespace(&map.display_snapshot, false, point);
|
|
anchor = map
|
|
.display_snapshot
|
|
.buffer_snapshot
|
|
.anchor_before(point.to_point(&map.display_snapshot));
|
|
}
|
|
if ranges.last() != Some(&(anchor..anchor)) {
|
|
ranges.push(anchor..anchor);
|
|
}
|
|
}
|
|
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
|
s.select_anchor_ranges(ranges)
|
|
})
|
|
});
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn jump_motion(
|
|
map: &DisplaySnapshot,
|
|
anchor: Anchor,
|
|
line: bool,
|
|
) -> (DisplayPoint, SelectionGoal) {
|
|
let mut point = anchor.to_display_point(map);
|
|
if line {
|
|
point = motion::first_non_whitespace(map, false, point)
|
|
}
|
|
|
|
(point, SelectionGoal::None)
|
|
}
|