Add whole word selection commands

pull/9904/head
chtenb 1 year ago
parent 620dfceb84
commit 07b3c49130

@ -170,6 +170,15 @@ impl Range {
self.from() <= pos && pos < self.to()
}
/// Returns equal if anchor and head are the same, and disregards old_visual_position. When
/// a single character is selected, the orientation (i.e. which is the head and which is the
/// anchor) is indistinguishable and should be disregarded.
pub fn visual_eq(&self, other: Range) -> bool {
self.anchor == other.anchor && self.head == other.head
// TODO: this does not work for graphemes like \r\n
|| self.len() == 1 && self.from() == other.from() && self.to() == other.to()
}
/// Map a range through a set of changes. Returns a new range representing
/// the same position after the changes are applied. Note that this
/// function runs in O(N) (N is number of changes) and can therefore
@ -710,6 +719,14 @@ impl Selection {
}
}
}
/// Returns true if two selections are identical as perceived by the user
pub fn visual_eq(&self, other: Selection) -> bool {
self.ranges()
.iter()
.zip(other.ranges().iter())
.all(|(&r1, &r2)| r1.visual_eq(r2))
}
}
impl<'a> IntoIterator for &'a Selection {

@ -523,6 +523,10 @@ impl MappableCommand {
surround_delete, "Surround delete",
select_textobject_around, "Select around object",
select_textobject_inner, "Select inside object",
select_next_word, "Select next whole word",
select_next_long_word, "Select next whole long word",
select_prev_word, "Select previous whole word",
select_prev_long_word, "Select previous whole long word",
goto_next_function, "Goto next function",
goto_prev_function, "Goto previous function",
goto_next_class, "Goto next type definition",
@ -5454,6 +5458,39 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
cx.on_next_key(move |cx, event| {
cx.editor.autoinfo = None;
if let Some(ch) = event.char() {
select_textobject_for_char(cx, ch, objtype, count);
}
});
let title = match objtype {
textobject::TextObject::Inside => "Match inside",
textobject::TextObject::Around => "Match around",
_ => return,
};
let help_text = [
("w", "Word"),
("W", "WORD"),
("p", "Paragraph"),
("t", "Type definition (tree-sitter)"),
("f", "Function (tree-sitter)"),
("a", "Argument/parameter (tree-sitter)"),
("c", "Comment (tree-sitter)"),
("T", "Test (tree-sitter)"),
("e", "Data structure entry (tree-sitter)"),
("m", "Closest surrounding pair (tree-sitter)"),
("g", "Change"),
(" ", "... or any character acting as a pair"),
];
cx.editor.autoinfo = Some(Info::new(title, &help_text));
}
fn select_textobject_for_char(
cx: &mut Context,
ch: char,
objtype: textobject::TextObject,
count: usize,
) {
let textobject = move |editor: &mut Editor| {
let (view, doc) = current!(editor);
let text = doc.text().slice(..);
@ -5529,30 +5566,55 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
doc.set_selection(view.id, selection);
};
cx.editor.apply_motion(textobject);
}
fn select_next_word(cx: &mut Context) {
let current_selection = get_current_selection(cx);
select_textobject_for_char(cx, 'w', textobject::TextObject::Inside, cx.count());
let new_selection = get_current_selection(cx);
if current_selection.visual_eq(new_selection) {
move_next_word_end(cx);
select_textobject_for_char(cx, 'w', textobject::TextObject::Inside, cx.count());
}
});
}
let title = match objtype {
textobject::TextObject::Inside => "Match inside",
textobject::TextObject::Around => "Match around",
_ => return,
};
let help_text = [
("w", "Word"),
("W", "WORD"),
("p", "Paragraph"),
("t", "Type definition (tree-sitter)"),
("f", "Function (tree-sitter)"),
("a", "Argument/parameter (tree-sitter)"),
("c", "Comment (tree-sitter)"),
("T", "Test (tree-sitter)"),
("e", "Data structure entry (tree-sitter)"),
("m", "Closest surrounding pair (tree-sitter)"),
("g", "Change"),
(" ", "... or any character acting as a pair"),
];
fn select_next_long_word(cx: &mut Context) {
let current_selection = get_current_selection(cx);
select_textobject_for_char(cx, 'W', textobject::TextObject::Inside, cx.count());
let new_selection = get_current_selection(cx);
if current_selection.visual_eq(new_selection) {
move_next_long_word_end(cx);
select_textobject_for_char(cx, 'W', textobject::TextObject::Inside, cx.count());
}
}
cx.editor.autoinfo = Some(Info::new(title, &help_text));
fn select_prev_word(cx: &mut Context) {
let current_selection = get_current_selection(cx);
select_textobject_for_char(cx, 'w', textobject::TextObject::Inside, cx.count());
flip_selections(cx);
let new_selection = get_current_selection(cx);
if current_selection.visual_eq(new_selection) {
move_prev_word_start(cx);
select_textobject_for_char(cx, 'w', textobject::TextObject::Inside, cx.count());
flip_selections(cx);
}
}
fn select_prev_long_word(cx: &mut Context) {
let current_selection = get_current_selection(cx);
select_textobject_for_char(cx, 'W', textobject::TextObject::Inside, cx.count());
flip_selections(cx);
let new_selection = get_current_selection(cx);
if current_selection.visual_eq(new_selection) {
move_prev_long_word_start(cx);
select_textobject_for_char(cx, 'W', textobject::TextObject::Inside, cx.count());
flip_selections(cx);
}
}
fn get_current_selection(cx: &mut Context) -> Selection {
let (view, doc) = current!(cx.editor);
doc.selection(view.id).clone()
}
fn surround_add(cx: &mut Context) {

Loading…
Cancel
Save