w, b, e: Match kakoune's behavior in selecting by default.

I initially preferred only moving the cursor, but selecting the whole
word is a lot nicer for things like wd (instead of vwd).
imgbot
Blaž Hrastnik 3 years ago
parent 16350399ac
commit 59a0fc7b59

@ -61,99 +61,110 @@ pub fn move_vertically(
range range
} }
pub fn move_next_word_start(slice: RopeSlice, mut pos: usize, count: usize) -> usize { pub fn move_next_word_start(slice: RopeSlice, mut begin: usize, count: usize) -> Option<Range> {
let mut end = begin;
for _ in 0..count { for _ in 0..count {
if pos + 1 == slice.len_chars() { if begin + 1 == slice.len_chars() {
return pos; return None;
} }
let mut ch = slice.char(pos); let mut ch = slice.char(begin);
let next = slice.char(pos + 1); let next = slice.char(begin + 1);
// if we're at the end of a word, or on whitespce right before new one // if we're at the end of a word, or on whitespce right before new one
if categorize(ch) != categorize(next) { if categorize(ch) != categorize(next) {
pos += 1; begin += 1;
ch = next;
} }
// return if not skip while?
skip_over_next(slice, &mut begin, |ch| ch == '\n');
ch = slice.char(begin);
end = begin + 1;
if is_word(ch) { if is_word(ch) {
skip_over_next(slice, &mut pos, is_word); skip_over_next(slice, &mut end, is_word);
} else if ch.is_ascii_punctuation() { } else if ch.is_ascii_punctuation() {
skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation()); skip_over_next(slice, &mut end, |ch| ch.is_ascii_punctuation());
} }
// TODO: don't include newline? skip_over_next(slice, &mut end, is_horiz_blank);
skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace());
} }
pos Some(Range::new(begin, end - 1))
} }
pub fn move_prev_word_start(slice: RopeSlice, mut pos: usize, count: usize) -> usize { pub fn move_prev_word_start(slice: RopeSlice, mut begin: usize, count: usize) -> Option<Range> {
let mut with_end = false;
let mut end = begin;
for _ in 0..count { for _ in 0..count {
if pos == 0 { if begin == 0 {
return pos; return None;
} }
let ch = slice.char(pos); let ch = slice.char(begin);
let prev = slice.char(pos - 1); let prev = slice.char(begin - 1);
if categorize(ch) != categorize(prev) { if categorize(ch) != categorize(prev) {
pos -= 1; begin -= 1;
} }
// match (category c1, category c2) => { // return if not skip while?
// if c1 != c2 { skip_over_prev(slice, &mut begin, |ch| ch == '\n');
// }
// }
// TODO: skip while eol end = begin;
// TODO: don't include newline? with_end = skip_over_prev(slice, &mut end, is_horiz_blank);
skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_whitespace());
// refetch // refetch
let ch = slice.char(pos); let ch = slice.char(end);
if is_word(ch) { if is_word(ch) {
skip_over_prev(slice, &mut pos, is_word); with_end = skip_over_prev(slice, &mut end, is_word);
} else if ch.is_ascii_punctuation() { } else if ch.is_ascii_punctuation() {
skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_punctuation()); with_end = skip_over_prev(slice, &mut end, |ch| ch.is_ascii_punctuation());
} }
pos = pos.saturating_add(1)
} }
pos // we want to include begin
Some(Range::new(begin + 1, if with_end { end } else { end + 1 }))
} }
pub fn move_next_word_end(slice: RopeSlice, mut pos: usize, count: usize) -> usize { pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> Option<Range> {
let mut end = begin;
for _ in 0..count { for _ in 0..count {
if pos + 1 == slice.len_chars() { if begin + 1 == slice.len_chars() {
return pos; return None;
} }
let ch = slice.char(pos); let ch = slice.char(begin);
let next = slice.char(pos + 1); let next = slice.char(begin + 1);
if categorize(ch) != categorize(next) { if categorize(ch) != categorize(next) {
pos += 1; begin += 1;
} }
// TODO: don't include newline? // return if not skip while?
skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace()); skip_over_next(slice, &mut begin, |ch| ch == '\n');
end = begin;
skip_over_next(slice, &mut end, is_horiz_blank);
// refetch // refetch
let ch = slice.char(pos); let ch = slice.char(end);
if is_word(ch) { if is_word(ch) {
skip_over_next(slice, &mut pos, is_word); skip_over_next(slice, &mut end, is_word);
} else if ch.is_ascii_punctuation() { } else if ch.is_ascii_punctuation() {
skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation()); skip_over_next(slice, &mut end, |ch| ch.is_ascii_punctuation());
} }
pos -= 1
} }
pos Some(Range::new(begin, end - 1))
} }
// ---- util ------------ // ---- util ------------
@ -164,6 +175,10 @@ fn is_word(ch: char) -> bool {
ch.is_alphanumeric() || ch == '_' ch.is_alphanumeric() || ch == '_'
} }
fn is_horiz_blank(ch: char) -> bool {
matches!(ch, ' ' | '\t')
}
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
enum Category { enum Category {
Whitespace, Whitespace,
@ -201,7 +216,8 @@ where
} }
#[inline] #[inline]
pub fn skip_over_prev<F>(slice: RopeSlice, pos: &mut usize, fun: F) /// Returns true if the final pos matches the predicate.
pub fn skip_over_prev<F>(slice: RopeSlice, pos: &mut usize, fun: F) -> bool
where where
F: Fn(char) -> bool, F: Fn(char) -> bool,
{ {
@ -214,6 +230,7 @@ where
} }
*pos = pos.saturating_sub(1); *pos = pos.saturating_sub(1);
} }
return fun(slice.char(*pos));
} }
#[cfg(test)] #[cfg(test)]

@ -218,8 +218,7 @@ pub fn move_next_word_start(cx: &mut Context) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|range| { let selection = doc.selection(view.id).transform(|range| {
let pos = movement::move_next_word_start(text, range.head, count); movement::move_next_word_start(text, range.head, count).unwrap_or(range)
Range::new(pos, pos)
}); });
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
@ -231,8 +230,7 @@ pub fn move_prev_word_start(cx: &mut Context) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|range| { let selection = doc.selection(view.id).transform(|range| {
let pos = movement::move_prev_word_start(text, range.head, count); movement::move_prev_word_start(text, range.head, count).unwrap_or(range)
Range::new(pos, pos)
}); });
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
@ -243,10 +241,9 @@ pub fn move_next_word_end(cx: &mut Context) {
let (view, doc) = cx.current(); let (view, doc) = cx.current();
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|range| { let selection = doc
let pos = movement::move_next_word_end(text, range.head, count); .selection(view.id)
Range::new(pos, pos) .transform(|range| movement::move_next_word_end(text, range.head, count).unwrap_or(range));
});
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
} }
@ -271,7 +268,8 @@ pub fn extend_next_word_start(cx: &mut Context) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|mut range| { let selection = doc.selection(view.id).transform(|mut range| {
let pos = movement::move_next_word_start(text, range.head, count); let word = movement::move_next_word_start(text, range.head, count).unwrap_or(range);
let pos = word.head;
Range::new(range.anchor, pos) Range::new(range.anchor, pos)
}); });
@ -284,7 +282,8 @@ pub fn extend_prev_word_start(cx: &mut Context) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|mut range| { let selection = doc.selection(view.id).transform(|mut range| {
let pos = movement::move_prev_word_start(text, range.head, count); let word = movement::move_prev_word_start(text, range.head, count).unwrap_or(range);
let pos = word.head;
Range::new(range.anchor, pos) Range::new(range.anchor, pos)
}); });
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
@ -296,7 +295,8 @@ pub fn extend_next_word_end(cx: &mut Context) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
let selection = doc.selection(view.id).transform(|mut range| { let selection = doc.selection(view.id).transform(|mut range| {
let pos = movement::move_next_word_end(text, range.head, count); let word = movement::move_next_word_end(text, range.head, count).unwrap_or(range);
let pos = word.head;
Range::new(range.anchor, pos) Range::new(range.anchor, pos)
}); });

Loading…
Cancel
Save