Update word selection/navigation to work with gap indexing.

Also tweaked some of the existing behavior that seemed inconsistent
and/or buggy.  It's mostly identical, just a few corner cases are
different.
pull/376/head
Nathan Vegdahl 3 years ago
parent c2fd55e168
commit 6c038bb015

@ -1,4 +1,4 @@
use std::iter::{self, from_fn}; use std::iter;
use ropey::iter::Chars; use ropey::iter::Chars;
@ -142,8 +142,41 @@ pub fn move_prev_word_end(slice: RopeSlice, range: Range, count: usize) -> Range
} }
fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTarget) -> Range { fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTarget) -> Range {
(0..count).fold(range, |range, _| { let is_prev = matches!(
slice.chars_at(range.head).range_to_target(target, range) target,
WordMotionTarget::PrevWordStart
| WordMotionTarget::PrevLongWordStart
| WordMotionTarget::PrevWordEnd
);
// Special-case early-out.
if (is_prev && range.head == 0) || (!is_prev && range.head == slice.len_chars()) {
return range;
}
// Prepare the range appropriately based on the target movement
// direction. This is addressing two things at once:
//
// 1. 1-width range sementics.
// 2. The anchor position being irrelevant to the output result.
#[allow(clippy::collapsible_else_if)] // Makes the structure clearer in this case.
let start_range = if is_prev {
if range.anchor < range.head {
Range::new(range.head, prev_grapheme_boundary(slice, range.head))
} else {
Range::new(next_grapheme_boundary(slice, range.head), range.head)
}
} else {
if range.anchor < range.head {
Range::new(prev_grapheme_boundary(slice, range.head), range.head)
} else {
Range::new(range.head, next_grapheme_boundary(slice, range.head))
}
};
// Do the main work.
(0..count).fold(start_range, |r, _| {
slice.chars_at(r.head).range_to_target(target, r)
}) })
} }
@ -200,79 +233,75 @@ pub trait CharHelpers {
fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range; fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range;
} }
enum WordMotionPhase {
Start,
SkipNewlines,
ReachTarget,
}
impl CharHelpers for Chars<'_> { impl CharHelpers for Chars<'_> {
/// Note: this only changes the anchor of the range if the head is effectively
/// starting on a boundary (either directly or after skipping newline characters).
/// Any other changes to the anchor should be handled by the calling code.
fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range { fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range {
// Characters are iterated forward or backwards depending on the motion direction. let is_prev = matches!(
let characters: Box<dyn Iterator<Item = char>> = match target { target,
WordMotionTarget::PrevWordStart WordMotionTarget::PrevWordStart
| WordMotionTarget::PrevLongWordStart | WordMotionTarget::PrevLongWordStart
| WordMotionTarget::PrevWordEnd => { | WordMotionTarget::PrevWordEnd
);
// Reverse the iterator if needed for the motion direction.
if is_prev {
self.reverse();
}
// Function to advance index in the appropriate motion direction.
let advance: &dyn Fn(&mut usize) = if is_prev {
&|idx| *idx = idx.saturating_sub(1)
} else {
&|idx| *idx += 1
};
// Initialize state variables.
let mut anchor = origin.anchor;
let mut head = origin.head;
let mut prev_ch = {
let ch = self.prev();
if ch.is_some() {
self.next(); self.next();
Box::new(from_fn(|| self.prev()))
} }
_ => Box::new(self), ch
}; };
// Index advancement also depends on the direction. // Skip any initial newline characters.
let advance: &dyn Fn(&mut usize) = match target { while let Some(ch) = self.next() {
WordMotionTarget::PrevWordStart if char_is_line_ending(ch) {
| WordMotionTarget::PrevLongWordStart prev_ch = Some(ch);
| WordMotionTarget::PrevWordEnd => &|u| *u = u.saturating_sub(1), advance(&mut head);
_ => &|u| *u += 1, } else {
}; self.prev();
break;
}
}
if prev_ch.map(char_is_line_ending).unwrap_or(false) {
anchor = head;
}
let mut characters = characters.peekable(); // Find our target position(s).
let mut phase = WordMotionPhase::Start; let head_start = head;
let mut head = origin.head; while let Some(next_ch) = self.next() {
let mut anchor: Option<usize> = None; if reached_target(target, prev_ch.unwrap_or(next_ch), next_ch) {
let is_boundary = if head == head_start {
|a: char, b: Option<char>| categorize_char(a) != categorize_char(b.unwrap_or(a)); anchor = head;
while let Some(peek) = characters.peek().copied() { } else {
phase = match phase { break;
WordMotionPhase::Start => {
characters.next();
if characters.peek().is_none() {
break; // We're at the end, so there's nothing to do.
}
// Anchor may remain here if the head wasn't at a boundary
if !is_boundary(peek, characters.peek().copied()) && !char_is_line_ending(peek)
{
anchor = Some(head);
}
// First character is always skipped by the head
advance(&mut head);
WordMotionPhase::SkipNewlines
}
WordMotionPhase::SkipNewlines => {
if char_is_line_ending(peek) {
characters.next();
if characters.peek().is_some() {
advance(&mut head);
}
WordMotionPhase::SkipNewlines
} else {
WordMotionPhase::ReachTarget
}
}
WordMotionPhase::ReachTarget => {
characters.next();
anchor = anchor.or(Some(head));
if reached_target(target, peek, characters.peek()) {
break;
} else {
advance(&mut head);
}
WordMotionPhase::ReachTarget
} }
} }
prev_ch = Some(next_ch);
advance(&mut head);
} }
Range::new(anchor.unwrap_or(origin.anchor), head)
// Un-reverse the iterator if needed.
if is_prev {
self.reverse();
}
Range::new(anchor, head)
} }
} }
@ -289,28 +318,23 @@ fn is_long_word_boundary(a: char, b: char) -> bool {
} }
} }
fn reached_target(target: WordMotionTarget, peek: char, next_peek: Option<&char>) -> bool { fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> bool {
let next_peek = match next_peek {
Some(next_peek) => next_peek,
None => return true,
};
match target { match target {
WordMotionTarget::NextWordStart | WordMotionTarget::PrevWordEnd => { WordMotionTarget::NextWordStart | WordMotionTarget::PrevWordEnd => {
is_word_boundary(peek, *next_peek) is_word_boundary(prev_ch, next_ch)
&& (char_is_line_ending(*next_peek) || !next_peek.is_whitespace()) && (char_is_line_ending(next_ch) || !next_ch.is_whitespace())
} }
WordMotionTarget::NextWordEnd | WordMotionTarget::PrevWordStart => { WordMotionTarget::NextWordEnd | WordMotionTarget::PrevWordStart => {
is_word_boundary(peek, *next_peek) is_word_boundary(prev_ch, next_ch)
&& (!peek.is_whitespace() || char_is_line_ending(*next_peek)) && (!prev_ch.is_whitespace() || char_is_line_ending(next_ch))
} }
WordMotionTarget::NextLongWordStart => { WordMotionTarget::NextLongWordStart => {
is_long_word_boundary(peek, *next_peek) is_long_word_boundary(prev_ch, next_ch)
&& (char_is_line_ending(*next_peek) || !next_peek.is_whitespace()) && (char_is_line_ending(next_ch) || !next_ch.is_whitespace())
} }
WordMotionTarget::NextLongWordEnd | WordMotionTarget::PrevLongWordStart => { WordMotionTarget::NextLongWordEnd | WordMotionTarget::PrevLongWordStart => {
is_long_word_boundary(peek, *next_peek) is_long_word_boundary(prev_ch, next_ch)
&& (!peek.is_whitespace() || char_is_line_ending(*next_peek)) && (!prev_ch.is_whitespace() || char_is_line_ending(next_ch))
} }
} }
} }
@ -538,42 +562,42 @@ mod test {
fn test_behaviour_when_moving_to_start_of_next_words() { fn test_behaviour_when_moving_to_start_of_next_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic forward motion stops at the first space", ("Basic forward motion stops at the first space",
vec![(1, Range::new(0, 0), Range::new(0, 5))]), vec![(1, Range::new(0, 0), Range::new(0, 6))]),
(" Starting from a boundary advances the anchor", (" Starting from a boundary advances the anchor",
vec![(1, Range::new(0, 0), Range::new(1, 9))]), vec![(1, Range::new(0, 0), Range::new(1, 10))]),
("Long whitespace gap is bridged by the head", ("Long whitespace gap is bridged by the head",
vec![(1, Range::new(0, 0), Range::new(0, 10))]), vec![(1, Range::new(0, 0), Range::new(0, 11))]),
("Previous anchor is irrelevant for forward motions", ("Previous anchor is irrelevant for forward motions",
vec![(1, Range::new(12, 0), Range::new(0, 8))]), vec![(1, Range::new(12, 0), Range::new(0, 9))]),
(" Starting from whitespace moves to last space in sequence", (" Starting from whitespace moves to last space in sequence",
vec![(1, Range::new(0, 0), Range::new(0, 3))]), vec![(1, Range::new(0, 0), Range::new(0, 4))]),
("Starting from mid-word leaves anchor at start position and moves head", ("Starting from mid-word leaves anchor at start position and moves head",
vec![(1, Range::new(3, 3), Range::new(3, 8))]), vec![(1, Range::new(3, 3), Range::new(3, 9))]),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 0), Range::new(0, 28))]), vec![(1, Range::new(0, 0), Range::new(0, 29))]),
("Jumping\n into starting whitespace selects the spaces before 'into'", ("Jumping\n into starting whitespace selects the spaces before 'into'",
vec![(1, Range::new(0, 6), Range::new(8, 11))]), vec![(1, Range::new(0, 7), Range::new(8, 12))]),
("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion", ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 11)), (1, Range::new(0, 0), Range::new(0, 12)),
(1, Range::new(0, 11), Range::new(12, 14)), (1, Range::new(0, 12), Range::new(12, 15)),
(1, Range::new(12, 14), Range::new(15, 17)) (1, Range::new(12, 15), Range::new(15, 18))
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 5)), (1, Range::new(0, 0), Range::new(0, 6)),
(1, Range::new(0, 5), Range::new(6, 9)), (1, Range::new(0, 6), Range::new(6, 10)),
]), ]),
(".._.._ punctuation is not joined by underscores into a single block", (".._.._ punctuation is not joined by underscores into a single block",
vec![(1, Range::new(0, 0), Range::new(0, 1))]), vec![(1, Range::new(0, 0), Range::new(0, 2))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 7)), (1, Range::new(0, 0), Range::new(0, 8)),
(1, Range::new(0, 7), Range::new(10, 13)), (1, Range::new(0, 8), Range::new(10, 14)),
]), ]),
("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.", ("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.",
vec![ vec![
(1, Range::new(0, 8), Range::new(13, 15)), (1, Range::new(0, 9), Range::new(13, 16)),
]), ]),
("A failed motion does not modify the range", ("A failed motion does not modify the range",
vec![ vec![
@ -581,17 +605,17 @@ mod test {
]), ]),
("oh oh oh two character words!", ("oh oh oh two character words!",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 2)), (1, Range::new(0, 0), Range::new(0, 3)),
(1, Range::new(0, 2), Range::new(3, 5)), (1, Range::new(0, 3), Range::new(3, 6)),
(1, Range::new(0, 1), Range::new(2, 2)), (1, Range::new(0, 2), Range::new(1, 3)),
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(0, 0), Range::new(17, 19)), (3, Range::new(0, 0), Range::new(17, 20)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(0, 0), Range::new(32, 40)), (999, Range::new(0, 0), Range::new(32, 41)),
]), ]),
("", // Edge case of moving forward in empty string ("", // Edge case of moving forward in empty string
vec![ vec![
@ -599,16 +623,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving forward in all newlines ("\n\n\n\n\n", // Edge case of moving forward in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 4)), (1, Range::new(0, 0), Range::new(5, 5)),
]), ]),
("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks", ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 0), Range::new(1, 3)), (1, Range::new(0, 0), Range::new(1, 4)),
(1, Range::new(1, 3), Range::new(5, 7)), (1, Range::new(1, 4), Range::new(5, 8)),
]), ]),
("ヒーリクス multibyte characters behave as normal characters", ("ヒーリクス multibyte characters behave as normal characters",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 5)), (1, Range::new(0, 0), Range::new(0, 6)),
]), ]),
]); ]);
@ -624,40 +648,40 @@ mod test {
fn test_behaviour_when_moving_to_start_of_next_long_words() { fn test_behaviour_when_moving_to_start_of_next_long_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic forward motion stops at the first space", ("Basic forward motion stops at the first space",
vec![(1, Range::new(0, 0), Range::new(0, 5))]), vec![(1, Range::new(0, 0), Range::new(0, 6))]),
(" Starting from a boundary advances the anchor", (" Starting from a boundary advances the anchor",
vec![(1, Range::new(0, 0), Range::new(1, 9))]), vec![(1, Range::new(0, 0), Range::new(1, 10))]),
("Long whitespace gap is bridged by the head", ("Long whitespace gap is bridged by the head",
vec![(1, Range::new(0, 0), Range::new(0, 10))]), vec![(1, Range::new(0, 0), Range::new(0, 11))]),
("Previous anchor is irrelevant for forward motions", ("Previous anchor is irrelevant for forward motions",
vec![(1, Range::new(12, 0), Range::new(0, 8))]), vec![(1, Range::new(12, 0), Range::new(0, 9))]),
(" Starting from whitespace moves to last space in sequence", (" Starting from whitespace moves to last space in sequence",
vec![(1, Range::new(0, 0), Range::new(0, 3))]), vec![(1, Range::new(0, 0), Range::new(0, 4))]),
("Starting from mid-word leaves anchor at start position and moves head", ("Starting from mid-word leaves anchor at start position and moves head",
vec![(1, Range::new(3, 3), Range::new(3, 8))]), vec![(1, Range::new(3, 3), Range::new(3, 9))]),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 0), Range::new(0, 28))]), vec![(1, Range::new(0, 0), Range::new(0, 29))]),
("Jumping\n into starting whitespace selects the spaces before 'into'", ("Jumping\n into starting whitespace selects the spaces before 'into'",
vec![(1, Range::new(0, 6), Range::new(8, 11))]), vec![(1, Range::new(0, 7), Range::new(8, 12))]),
("alphanumeric.!,and.?=punctuation are not treated any differently than alphanumerics", ("alphanumeric.!,and.?=punctuation are not treated any differently than alphanumerics",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 32)), (1, Range::new(0, 0), Range::new(0, 33)),
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 5)), (1, Range::new(0, 0), Range::new(0, 6)),
(1, Range::new(0, 5), Range::new(6, 9)), (1, Range::new(0, 6), Range::new(6, 10)),
]), ]),
(".._.._ punctuation is joined by underscores into a single word, as it behaves like alphanumerics", (".._.._ punctuation is joined by underscores into a single word, as it behaves like alphanumerics",
vec![(1, Range::new(0, 0), Range::new(0, 6))]), vec![(1, Range::new(0, 0), Range::new(0, 7))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 7)), (1, Range::new(0, 0), Range::new(0, 8)),
(1, Range::new(0, 7), Range::new(10, 13)), (1, Range::new(0, 8), Range::new(10, 14)),
]), ]),
("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.", ("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.",
vec![ vec![
(1, Range::new(0, 8), Range::new(13, 15)), (1, Range::new(0, 9), Range::new(13, 16)),
]), ]),
("A failed motion does not modify the range", ("A failed motion does not modify the range",
vec![ vec![
@ -665,17 +689,17 @@ mod test {
]), ]),
("oh oh oh two character words!", ("oh oh oh two character words!",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 2)), (1, Range::new(0, 0), Range::new(0, 3)),
(1, Range::new(0, 2), Range::new(3, 5)), (1, Range::new(0, 3), Range::new(3, 6)),
(1, Range::new(0, 1), Range::new(2, 2)), (1, Range::new(0, 1), Range::new(0, 3)),
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(0, 0), Range::new(17, 19)), (3, Range::new(0, 0), Range::new(17, 20)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(0, 0), Range::new(32, 40)), (999, Range::new(0, 0), Range::new(32, 41)),
]), ]),
("", // Edge case of moving forward in empty string ("", // Edge case of moving forward in empty string
vec![ vec![
@ -683,16 +707,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving forward in all newlines ("\n\n\n\n\n", // Edge case of moving forward in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 4)), (1, Range::new(0, 0), Range::new(5, 5)),
]), ]),
("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks", ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 0), Range::new(1, 3)), (1, Range::new(0, 0), Range::new(1, 4)),
(1, Range::new(1, 3), Range::new(5, 7)), (1, Range::new(1, 4), Range::new(5, 8)),
]), ]),
("ヒー..リクス multibyte characters behave as normal characters, including their interaction with punctuation", ("ヒー..リクス multibyte characters behave as normal characters, including their interaction with punctuation",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 7)), (1, Range::new(0, 0), Range::new(0, 8)),
]), ]),
]); ]);
@ -708,44 +732,47 @@ mod test {
fn test_behaviour_when_moving_to_start_of_previous_words() { fn test_behaviour_when_moving_to_start_of_previous_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic backward motion from the middle of a word", ("Basic backward motion from the middle of a word",
vec![(1, Range::new(3, 3), Range::new(3, 0))]), vec![(1, Range::new(3, 3), Range::new(4, 0))]),
("Starting from after boundary retreats the anchor",
vec![(1, Range::new(0, 8), Range::new(7, 0))]), // // Why do we want this behavior? The current behavior fails this
// // test, but seems better and more consistent.
// ("Starting from after boundary retreats the anchor",
// vec![(1, Range::new(0, 9), Range::new(8, 0))]),
(" Jump to start of a word preceded by whitespace", (" Jump to start of a word preceded by whitespace",
vec![(1, Range::new(5, 5), Range::new(5, 4))]), vec![(1, Range::new(5, 5), Range::new(6, 4))]),
(" Jump to start of line from start of word preceded by whitespace", (" Jump to start of line from start of word preceded by whitespace",
vec![(1, Range::new(4, 4), Range::new(3, 0))]), vec![(1, Range::new(4, 4), Range::new(4, 0))]),
("Previous anchor is irrelevant for backward motions", ("Previous anchor is irrelevant for backward motions",
vec![(1, Range::new(12, 5), Range::new(5, 0))]), vec![(1, Range::new(12, 5), Range::new(6, 0))]),
(" Starting from whitespace moves to first space in sequence", (" Starting from whitespace moves to first space in sequence",
vec![(1, Range::new(0, 3), Range::new(3, 0))]), vec![(1, Range::new(0, 4), Range::new(4, 0))]),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 20), Range::new(20, 0))]), vec![(1, Range::new(0, 20), Range::new(20, 0))]),
("Jumping\n \nback through a newline selects whitespace", ("Jumping\n \nback through a newline selects whitespace",
vec![(1, Range::new(0, 13), Range::new(11, 8))]), vec![(1, Range::new(0, 13), Range::new(12, 8))]),
("Jumping to start of word from the end selects the word", ("Jumping to start of word from the end selects the word",
vec![(1, Range::new(6, 6), Range::new(6, 0))]), vec![(1, Range::new(6, 7), Range::new(7, 0))]),
("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion", ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
vec![ vec![
(1, Range::new(30, 30), Range::new(30, 21)), (1, Range::new(29, 30), Range::new(30, 21)),
(1, Range::new(30, 21), Range::new(20, 18)), (1, Range::new(30, 21), Range::new(21, 18)),
(1, Range::new(20, 18), Range::new(17, 15)) (1, Range::new(21, 18), Range::new(18, 15))
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 10), Range::new(9, 6)), (1, Range::new(0, 10), Range::new(10, 6)),
(1, Range::new(9, 6), Range::new(5, 0)), (1, Range::new(10, 6), Range::new(6, 0)),
]), ]),
(".._.._ punctuation is not joined by underscores into a single block", (".._.._ punctuation is not joined by underscores into a single block",
vec![(1, Range::new(0, 5), Range::new(4, 3))]), vec![(1, Range::new(0, 6), Range::new(5, 3))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 10), Range::new(7, 0)), (1, Range::new(0, 10), Range::new(8, 0)),
]), ]),
("Jumping \n\n\n\n\nback from within a newline group selects previous block", ("Jumping \n\n\n\n\nback from within a newline group selects previous block",
vec![ vec![
(1, Range::new(0, 13), Range::new(10, 0)), (1, Range::new(0, 13), Range::new(11, 0)),
]), ]),
("Failed motions do not modify the range", ("Failed motions do not modify the range",
vec![ vec![
@ -753,11 +780,11 @@ mod test {
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(18, 18), Range::new(8, 0)), (3, Range::new(18, 18), Range::new(9, 0)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(40, 40), Range::new(9, 0)), (999, Range::new(40, 40), Range::new(10, 0)),
]), ]),
("", // Edge case of moving backwards in empty string ("", // Edge case of moving backwards in empty string
vec![ vec![
@ -765,16 +792,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving backwards in all newlines ("\n\n\n\n\n", // Edge case of moving backwards in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 0)), (1, Range::new(5, 5), Range::new(0, 0)),
]), ]),
(" \n \nJumping back through alternated space blocks and newlines selects the space blocks", (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 7), Range::new(6, 4)), (1, Range::new(0, 8), Range::new(7, 4)),
(1, Range::new(6, 4), Range::new(2, 0)), (1, Range::new(7, 4), Range::new(3, 0)),
]), ]),
("ヒーリクス multibyte characters behave as normal characters", ("ヒーリクス multibyte characters behave as normal characters",
vec![ vec![
(1, Range::new(0, 5), Range::new(4, 0)), (1, Range::new(0, 6), Range::new(6, 0)),
]), ]),
]); ]);
@ -789,72 +816,89 @@ mod test {
#[test] #[test]
fn test_behaviour_when_moving_to_start_of_previous_long_words() { fn test_behaviour_when_moving_to_start_of_previous_long_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic backward motion from the middle of a word", (
vec![(1, Range::new(3, 3), Range::new(3, 0))]), "Basic backward motion from the middle of a word",
("Starting from after boundary retreats the anchor", vec![(1, Range::new(3, 3), Range::new(4, 0))],
vec![(1, Range::new(0, 8), Range::new(7, 0))]), ),
(" Jump to start of a word preceded by whitespace",
vec![(1, Range::new(5, 5), Range::new(5, 4))]), // // Why do we want this behavior? The current behavior fails this
(" Jump to start of line from start of word preceded by whitespace", // // test, but seems better and more consistent.
vec![(1, Range::new(4, 4), Range::new(3, 0))]), // ("Starting from after boundary retreats the anchor",
// vec![(1, Range::new(0, 9), Range::new(8, 0))]),
(
" Jump to start of a word preceded by whitespace",
vec![(1, Range::new(5, 5), Range::new(6, 4))],
),
(
" Jump to start of line from start of word preceded by whitespace",
vec![(1, Range::new(3, 4), Range::new(4, 0))],
),
("Previous anchor is irrelevant for backward motions", ("Previous anchor is irrelevant for backward motions",
vec![(1, Range::new(12, 5), Range::new(5, 0))]), vec![(1, Range::new(12, 5), Range::new(6, 0))]),
(" Starting from whitespace moves to first space in sequence", (
vec![(1, Range::new(0, 3), Range::new(3, 0))]), " Starting from whitespace moves to first space in sequence",
vec![(1, Range::new(0, 4), Range::new(4, 0))],
),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 20), Range::new(20, 0))]), vec![(1, Range::new(0, 20), Range::new(20, 0))]),
("Jumping\n \nback through a newline selects whitespace", (
vec![(1, Range::new(0, 13), Range::new(11, 8))]), "Jumping\n \nback through a newline selects whitespace",
("Jumping to start of word from the end selects the word", vec![(1, Range::new(0, 13), Range::new(12, 8))],
vec![(1, Range::new(6, 6), Range::new(6, 0))]), ),
("alphanumeric.!,and.?=punctuation are treated exactly the same", (
vec![ "Jumping to start of word from the end selects the word",
(1, Range::new(30, 30), Range::new(30, 0)), vec![(1, Range::new(6, 7), Range::new(7, 0))],
]), ),
(
("... ... punctuation and spaces behave as expected", "alphanumeric.!,and.?=punctuation are treated exactly the same",
vec![(1, Range::new(29, 30), Range::new(30, 0))],
),
(
"... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 10), Range::new(9, 6)), (1, Range::new(0, 10), Range::new(10, 6)),
(1, Range::new(9, 6), Range::new(5, 0)), (1, Range::new(10, 6), Range::new(6, 0)),
]), ],
),
(".._.._ punctuation is joined by underscores into a single block", (".._.._ punctuation is joined by underscores into a single block",
vec![(1, Range::new(0, 5), Range::new(4, 0))]), vec![(1, Range::new(0, 6), Range::new(6, 0))]),
("Newlines\n\nare bridged seamlessly.", (
vec![ "Newlines\n\nare bridged seamlessly.",
(1, Range::new(0, 10), Range::new(7, 0)), vec![(1, Range::new(0, 10), Range::new(8, 0))],
]), ),
("Jumping \n\n\n\n\nback from within a newline group selects previous block", (
vec![ "Jumping \n\n\n\n\nback from within a newline group selects previous block",
(1, Range::new(0, 13), Range::new(10, 0)), vec![(1, Range::new(0, 13), Range::new(11, 0))],
]), ),
("Failed motions do not modify the range", (
vec![ "Failed motions do not modify the range",
(0, Range::new(3, 0), Range::new(3, 0)), vec![(0, Range::new(3, 0), Range::new(3, 0))],
]), ),
("Multiple motions at once resolve correctly", (
vec![ "Multiple motions at once resolve correctly",
(3, Range::new(18, 18), Range::new(8, 0)), vec![(3, Range::new(19, 19), Range::new(9, 0))],
]), ),
("Excessive motions are performed partially", (
vec![ "Excessive motions are performed partially",
(999, Range::new(40, 40), Range::new(9, 0)), vec![(999, Range::new(40, 40), Range::new(10, 0))],
]), ),
("", // Edge case of moving backwards in empty string (
vec![ "", // Edge case of moving backwards in empty string
(1, Range::new(0, 0), Range::new(0, 0)), vec![(1, Range::new(0, 0), Range::new(0, 0))],
]), ),
("\n\n\n\n\n", // Edge case of moving backwards in all newlines (
vec![ "\n\n\n\n\n", // Edge case of moving backwards in all newlines
(1, Range::new(0, 0), Range::new(0, 0)), vec![(1, Range::new(5, 5), Range::new(0, 0))],
]), ),
(" \n \nJumping back through alternated space blocks and newlines selects the space blocks", (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 7), Range::new(6, 4)), (1, Range::new(0, 8), Range::new(7, 4)),
(1, Range::new(6, 4), Range::new(2, 0)), (1, Range::new(7, 4), Range::new(3, 0)),
]), ]),
("ヒーリ..クス multibyte characters behave as normal characters, including when interacting with punctuation", ("ヒーリ..クス multibyte characters behave as normal characters, including when interacting with punctuation",
vec![ vec![
(1, Range::new(0, 7), Range::new(6, 0)), (1, Range::new(0, 8), Range::new(8, 0)),
]), ]),
]); ]);
@ -870,42 +914,46 @@ mod test {
fn test_behaviour_when_moving_to_end_of_next_words() { fn test_behaviour_when_moving_to_end_of_next_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic forward motion from the start of a word to the end of it", ("Basic forward motion from the start of a word to the end of it",
vec![(1, Range::new(0, 0), Range::new(0, 4))]), vec![(1, Range::new(0, 0), Range::new(0, 5))]),
("Basic forward motion from the end of a word to the end of the next", ("Basic forward motion from the end of a word to the end of the next",
vec![(1, Range::new(0, 4), Range::new(5, 12))]), vec![(1, Range::new(0, 5), Range::new(5, 13))]),
("Basic forward motion from the middle of a word to the end of it", ("Basic forward motion from the middle of a word to the end of it",
vec![(1, Range::new(2, 2), Range::new(2, 4))]), vec![(1, Range::new(2, 2), Range::new(2, 5))]),
(" Jumping to end of a word preceded by whitespace", (" Jumping to end of a word preceded by whitespace",
vec![(1, Range::new(0, 0), Range::new(0, 10))]), vec![(1, Range::new(0, 0), Range::new(0, 11))]),
(" Starting from a boundary advances the anchor",
vec![(1, Range::new(0, 0), Range::new(1, 8))]), // // Why do we want this behavior? The current behavior fails this
// // test, but seems better and more consistent.
// (" Starting from a boundary advances the anchor",
// vec![(1, Range::new(0, 0), Range::new(1, 9))]),
("Previous anchor is irrelevant for end of word motion", ("Previous anchor is irrelevant for end of word motion",
vec![(1, Range::new(12, 2), Range::new(2, 7))]), vec![(1, Range::new(12, 2), Range::new(2, 8))]),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 0), Range::new(0, 27))]), vec![(1, Range::new(0, 0), Range::new(0, 28))]),
("Jumping\n into starting whitespace selects up to the end of next word", ("Jumping\n into starting whitespace selects up to the end of next word",
vec![(1, Range::new(0, 6), Range::new(8, 15))]), vec![(1, Range::new(0, 7), Range::new(8, 16))]),
("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion", ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 11)), (1, Range::new(0, 0), Range::new(0, 12)),
(1, Range::new(0, 11), Range::new(12, 14)), (1, Range::new(0, 12), Range::new(12, 15)),
(1, Range::new(12, 14), Range::new(15, 17)) (1, Range::new(12, 15), Range::new(15, 18))
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 2)), (1, Range::new(0, 0), Range::new(0, 3)),
(1, Range::new(0, 2), Range::new(3, 8)), (1, Range::new(0, 3), Range::new(3, 9)),
]), ]),
(".._.._ punctuation is not joined by underscores into a single block", (".._.._ punctuation is not joined by underscores into a single block",
vec![(1, Range::new(0, 0), Range::new(0, 1))]), vec![(1, Range::new(0, 0), Range::new(0, 2))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 7)), (1, Range::new(0, 0), Range::new(0, 8)),
(1, Range::new(0, 7), Range::new(10, 12)), (1, Range::new(0, 8), Range::new(10, 13)),
]), ]),
("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.", ("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.",
vec![ vec![
(1, Range::new(0, 8), Range::new(13, 19)), (1, Range::new(0, 8), Range::new(13, 20)),
]), ]),
("A failed motion does not modify the range", ("A failed motion does not modify the range",
vec![ vec![
@ -913,11 +961,11 @@ mod test {
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(0, 0), Range::new(16, 18)), (3, Range::new(0, 0), Range::new(16, 19)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(0, 0), Range::new(31, 40)), (999, Range::new(0, 0), Range::new(31, 41)),
]), ]),
("", // Edge case of moving forward in empty string ("", // Edge case of moving forward in empty string
vec![ vec![
@ -925,16 +973,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving forward in all newlines ("\n\n\n\n\n", // Edge case of moving forward in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 4)), (1, Range::new(0, 0), Range::new(5, 5)),
]), ]),
("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks", ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 0), Range::new(1, 3)), (1, Range::new(0, 0), Range::new(1, 4)),
(1, Range::new(1, 3), Range::new(5, 7)), (1, Range::new(1, 4), Range::new(5, 8)),
]), ]),
("ヒーリクス multibyte characters behave as normal characters", ("ヒーリクス multibyte characters behave as normal characters",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 4)), (1, Range::new(0, 0), Range::new(0, 5)),
]), ]),
]); ]);
@ -950,44 +998,44 @@ mod test {
fn test_behaviour_when_moving_to_end_of_previous_words() { fn test_behaviour_when_moving_to_end_of_previous_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic backward motion from the middle of a word", ("Basic backward motion from the middle of a word",
vec![(1, Range::new(9, 9), Range::new(9, 5))]), vec![(1, Range::new(9, 9), Range::new(10, 5))]),
("Starting from after boundary retreats the anchor", ("Starting from after boundary retreats the anchor",
vec![(1, Range::new(0, 13), Range::new(12, 8))]), vec![(1, Range::new(0, 14), Range::new(13, 8))]),
("Jump to end of a word succeeded by whitespace", ("Jump to end of a word succeeded by whitespace",
vec![(1, Range::new(10, 10), Range::new(10, 4))]), vec![(1, Range::new(11, 11), Range::new(11, 4))]),
(" Jump to start of line from end of word preceded by whitespace", (" Jump to start of line from end of word preceded by whitespace",
vec![(1, Range::new(7, 7), Range::new(7, 0))]), vec![(1, Range::new(8, 8), Range::new(8, 0))]),
("Previous anchor is irrelevant for backward motions", ("Previous anchor is irrelevant for backward motions",
vec![(1, Range::new(26, 12), Range::new(12, 8))]), vec![(1, Range::new(26, 12), Range::new(13, 8))]),
(" Starting from whitespace moves to first space in sequence", (" Starting from whitespace moves to first space in sequence",
vec![(1, Range::new(0, 3), Range::new(3, 0))]), vec![(1, Range::new(0, 4), Range::new(4, 0))]),
("Test identifiers_with_underscores are considered a single word", ("Test identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 25), Range::new(25, 4))]), vec![(1, Range::new(0, 25), Range::new(25, 4))]),
("Jumping\n \nback through a newline selects whitespace", ("Jumping\n \nback through a newline selects whitespace",
vec![(1, Range::new(0, 13), Range::new(11, 8))]), vec![(1, Range::new(0, 13), Range::new(12, 8))]),
("Jumping to start of word from the end selects the whole word", ("Jumping to start of word from the end selects the whole word",
vec![(1, Range::new(15, 15), Range::new(15, 10))]), vec![(1, Range::new(16, 16), Range::new(16, 10))]),
("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion", ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
vec![ vec![
(1, Range::new(30, 30), Range::new(30, 21)), (1, Range::new(30, 30), Range::new(31, 21)),
(1, Range::new(30, 21), Range::new(20, 18)), (1, Range::new(31, 21), Range::new(21, 18)),
(1, Range::new(20, 18), Range::new(17, 15)) (1, Range::new(21, 18), Range::new(18, 15))
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 10), Range::new(9, 9)), (1, Range::new(0, 10), Range::new(9, 3)),
(1, Range::new(9, 6), Range::new(5, 3)), (1, Range::new(9, 3), Range::new(3, 0)),
]), ]),
(".._.._ punctuation is not joined by underscores into a single block", (".._.._ punctuation is not joined by underscores into a single block",
vec![(1, Range::new(0, 5), Range::new(4, 3))]), vec![(1, Range::new(0, 5), Range::new(5, 3))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 10), Range::new(7, 0)), (1, Range::new(0, 10), Range::new(8, 0)),
]), ]),
("Jumping \n\n\n\n\nback from within a newline group selects previous block", ("Jumping \n\n\n\n\nback from within a newline group selects previous block",
vec![ vec![
(1, Range::new(0, 13), Range::new(10, 7)), (1, Range::new(0, 13), Range::new(11, 7)),
]), ]),
("Failed motions do not modify the range", ("Failed motions do not modify the range",
vec![ vec![
@ -995,11 +1043,11 @@ mod test {
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(23, 23), Range::new(15, 8)), (3, Range::new(24, 24), Range::new(16, 8)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(40, 40), Range::new(8, 0)), (999, Range::new(40, 40), Range::new(9, 0)),
]), ]),
("", // Edge case of moving backwards in empty string ("", // Edge case of moving backwards in empty string
vec![ vec![
@ -1007,16 +1055,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving backwards in all newlines ("\n\n\n\n\n", // Edge case of moving backwards in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 0)), (1, Range::new(5, 5), Range::new(0, 0)),
]), ]),
(" \n \nJumping back through alternated space blocks and newlines selects the space blocks", (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 7), Range::new(6, 4)), (1, Range::new(0, 8), Range::new(7, 4)),
(1, Range::new(6, 4), Range::new(2, 0)), (1, Range::new(7, 4), Range::new(3, 0)),
]), ]),
("Test ヒーリクス multibyte characters behave as normal characters", ("Test ヒーリクス multibyte characters behave as normal characters",
vec![ vec![
(1, Range::new(0, 9), Range::new(9, 4)), (1, Range::new(0, 10), Range::new(10, 4)),
]), ]),
]); ]);
@ -1032,40 +1080,44 @@ mod test {
fn test_behaviour_when_moving_to_end_of_next_long_words() { fn test_behaviour_when_moving_to_end_of_next_long_words() {
let tests = array::IntoIter::new([ let tests = array::IntoIter::new([
("Basic forward motion from the start of a word to the end of it", ("Basic forward motion from the start of a word to the end of it",
vec![(1, Range::new(0, 0), Range::new(0, 4))]), vec![(1, Range::new(0, 0), Range::new(0, 5))]),
("Basic forward motion from the end of a word to the end of the next", ("Basic forward motion from the end of a word to the end of the next",
vec![(1, Range::new(0, 4), Range::new(5, 12))]), vec![(1, Range::new(0, 5), Range::new(5, 13))]),
("Basic forward motion from the middle of a word to the end of it", ("Basic forward motion from the middle of a word to the end of it",
vec![(1, Range::new(2, 2), Range::new(2, 4))]), vec![(1, Range::new(2, 2), Range::new(2, 5))]),
(" Jumping to end of a word preceded by whitespace", (" Jumping to end of a word preceded by whitespace",
vec![(1, Range::new(0, 0), Range::new(0, 10))]), vec![(1, Range::new(0, 0), Range::new(0, 11))]),
(" Starting from a boundary advances the anchor",
vec![(1, Range::new(0, 0), Range::new(1, 8))]), // // Why do we want this behavior? The current behavior fails this
// // test, but seems better and more consistent.
// (" Starting from a boundary advances the anchor",
// vec![(1, Range::new(0, 0), Range::new(1, 9))]),
("Previous anchor is irrelevant for end of word motion", ("Previous anchor is irrelevant for end of word motion",
vec![(1, Range::new(12, 2), Range::new(2, 7))]), vec![(1, Range::new(12, 2), Range::new(2, 8))]),
("Identifiers_with_underscores are considered a single word", ("Identifiers_with_underscores are considered a single word",
vec![(1, Range::new(0, 0), Range::new(0, 27))]), vec![(1, Range::new(0, 0), Range::new(0, 28))]),
("Jumping\n into starting whitespace selects up to the end of next word", ("Jumping\n into starting whitespace selects up to the end of next word",
vec![(1, Range::new(0, 6), Range::new(8, 15))]), vec![(1, Range::new(0, 7), Range::new(8, 16))]),
("alphanumeric.!,and.?=punctuation are treated the same way", ("alphanumeric.!,and.?=punctuation are treated the same way",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 31)), (1, Range::new(0, 0), Range::new(0, 32)),
]), ]),
("... ... punctuation and spaces behave as expected", ("... ... punctuation and spaces behave as expected",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 2)), (1, Range::new(0, 0), Range::new(0, 3)),
(1, Range::new(0, 2), Range::new(3, 8)), (1, Range::new(0, 3), Range::new(3, 9)),
]), ]),
(".._.._ punctuation is joined by underscores into a single block", (".._.._ punctuation is joined by underscores into a single block",
vec![(1, Range::new(0, 0), Range::new(0, 5))]), vec![(1, Range::new(0, 0), Range::new(0, 6))]),
("Newlines\n\nare bridged seamlessly.", ("Newlines\n\nare bridged seamlessly.",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 7)), (1, Range::new(0, 0), Range::new(0, 8)),
(1, Range::new(0, 7), Range::new(10, 12)), (1, Range::new(0, 8), Range::new(10, 13)),
]), ]),
("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.", ("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.",
vec![ vec![
(1, Range::new(0, 8), Range::new(13, 19)), (1, Range::new(0, 9), Range::new(13, 20)),
]), ]),
("A failed motion does not modify the range", ("A failed motion does not modify the range",
vec![ vec![
@ -1073,11 +1125,11 @@ mod test {
]), ]),
("Multiple motions at once resolve correctly", ("Multiple motions at once resolve correctly",
vec![ vec![
(3, Range::new(0, 0), Range::new(16, 18)), (3, Range::new(0, 0), Range::new(16, 19)),
]), ]),
("Excessive motions are performed partially", ("Excessive motions are performed partially",
vec![ vec![
(999, Range::new(0, 0), Range::new(31, 40)), (999, Range::new(0, 0), Range::new(31, 41)),
]), ]),
("", // Edge case of moving forward in empty string ("", // Edge case of moving forward in empty string
vec![ vec![
@ -1085,16 +1137,16 @@ mod test {
]), ]),
("\n\n\n\n\n", // Edge case of moving forward in all newlines ("\n\n\n\n\n", // Edge case of moving forward in all newlines
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 4)), (1, Range::new(0, 0), Range::new(5, 5)),
]), ]),
("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks", ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
vec![ vec![
(1, Range::new(0, 0), Range::new(1, 3)), (1, Range::new(0, 0), Range::new(1, 4)),
(1, Range::new(1, 3), Range::new(5, 7)), (1, Range::new(1, 4), Range::new(5, 8)),
]), ]),
("ヒーリ..クス multibyte characters behave as normal characters, including when they interact with punctuation", ("ヒーリ..クス multibyte characters behave as normal characters, including when they interact with punctuation",
vec![ vec![
(1, Range::new(0, 0), Range::new(0, 6)), (1, Range::new(0, 0), Range::new(0, 7)),
]), ]),
]); ]);

@ -498,6 +498,7 @@ fn move_next_word_start(cx: &mut Context) {
view.id, view.id,
doc.selection(view.id) doc.selection(view.id)
.clone() .clone()
.min_width_1(doc.text().slice(..))
.transform(|range| movement::move_next_word_start(doc.text().slice(..), range, count)), .transform(|range| movement::move_next_word_start(doc.text().slice(..), range, count)),
); );
} }
@ -509,6 +510,7 @@ fn move_prev_word_start(cx: &mut Context) {
view.id, view.id,
doc.selection(view.id) doc.selection(view.id)
.clone() .clone()
.min_width_1(doc.text().slice(..))
.transform(|range| movement::move_prev_word_start(doc.text().slice(..), range, count)), .transform(|range| movement::move_prev_word_start(doc.text().slice(..), range, count)),
); );
} }
@ -520,6 +522,7 @@ fn move_next_word_end(cx: &mut Context) {
view.id, view.id,
doc.selection(view.id) doc.selection(view.id)
.clone() .clone()
.min_width_1(doc.text().slice(..))
.transform(|range| movement::move_next_word_end(doc.text().slice(..), range, count)), .transform(|range| movement::move_next_word_end(doc.text().slice(..), range, count)),
); );
} }
@ -529,9 +532,12 @@ fn move_next_long_word_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
movement::move_next_long_word_start(doc.text().slice(..), range, count) .clone()
}), .min_width_1(doc.text().slice(..))
.transform(|range| {
movement::move_next_long_word_start(doc.text().slice(..), range, count)
}),
); );
} }
@ -540,9 +546,12 @@ fn move_prev_long_word_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
movement::move_prev_long_word_start(doc.text().slice(..), range, count) .clone()
}), .min_width_1(doc.text().slice(..))
.transform(|range| {
movement::move_prev_long_word_start(doc.text().slice(..), range, count)
}),
); );
} }
@ -551,9 +560,12 @@ fn move_next_long_word_end(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
movement::move_next_long_word_end(doc.text().slice(..), range, count) .clone()
}), .min_width_1(doc.text().slice(..))
.transform(|range| {
movement::move_next_long_word_end(doc.text().slice(..), range, count)
}),
); );
} }
@ -574,12 +586,15 @@ fn extend_next_word_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
let text = doc.text().slice(..); .clone()
let word = movement::move_next_word_start(text, range, count); .min_width_1(doc.text().slice(..))
let pos = word.head; .transform(|range| {
range.put(text, pos, true) let text = doc.text().slice(..);
}), let word = movement::move_next_word_start(text, range, count);
let pos = word.head;
range.put(text, pos, true)
}),
); );
} }
@ -588,12 +603,15 @@ fn extend_prev_word_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
let text = doc.text().slice(..); .clone()
let word = movement::move_prev_word_start(text, range, count); .min_width_1(doc.text().slice(..))
let pos = word.head; .transform(|range| {
range.put(text, pos, true) let text = doc.text().slice(..);
}), let word = movement::move_prev_word_start(text, range, count);
let pos = word.head;
range.put(text, pos, true)
}),
); );
} }
@ -602,12 +620,15 @@ fn extend_next_word_end(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.set_selection( doc.set_selection(
view.id, view.id,
doc.selection(view.id).clone().transform(|range| { doc.selection(view.id)
let text = doc.text().slice(..); .clone()
let word = movement::move_next_word_end(text, range, count); .min_width_1(doc.text().slice(..))
let pos = word.head; .transform(|range| {
range.put(text, pos, true) let text = doc.text().slice(..);
}), let word = movement::move_next_word_end(text, range, count);
let pos = word.head;
range.put(text, pos, true)
}),
); );
} }

Loading…
Cancel
Save