@@ -1,12 +1,14 @@
1−use std::iter::{self, from_fn};
1+use std::iter;
22
33use ropey::iter::Chars;
44
55use crate::{
66 chars::{categorize_char, char_is_line_ending, CharCategory},
77 coords_at_pos,
8− graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary},
9− line_ending::{get_line_ending, line_end_char_index},
8+ graphemes::{
9+ next_grapheme_boundary, nth_next_grapheme_boundary, nth_prev_grapheme_boundary,
10+ prev_grapheme_boundary,
11+ },
1012 pos_at_coords, Position, Range, RopeSlice,
1113};
1214
@@ -29,25 +31,16 @@ pub fn move_horizontally(
2931 count: usize,
3032 behaviour: Movement,
3133) -> Range {
32− let pos = range.head;
33− let line = slice.char_to_line(pos);
34− // TODO: we can optimize clamping by passing in RopeSlice limited to current line. that way
35− // we stop calculating past start/end of line.
36− let pos = match dir {
37− Direction::Backward => {
38− let start = slice.line_to_char(line);
39− nth_prev_grapheme_boundary(slice, pos, count).max(start)
40− }
41− Direction::Forward => {
42− let end_char_idx = line_end_char_index(&slice, line);
43− nth_next_grapheme_boundary(slice, pos, count).min(end_char_idx)
44− }
45− };
46− let anchor = match behaviour {
47− Movement::Extend => range.anchor,
48− Movement::Move => pos,
34+ let pos = range.cursor(slice);
35+
36+ // Compute the new position.
37+ let new_pos = match dir {
38+ Direction::Forward => nth_next_grapheme_boundary(slice, pos, count),
39+ Direction::Backward => nth_prev_grapheme_boundary(slice, pos, count),
4940 };
50− Range::new(anchor, pos)
41+
42+ // Compute the final new range.
43+ range.put_cursor(slice, new_pos, behaviour == Movement::Extend)
5144}
5245
5346pub fn move_vertically(
@@ -57,36 +50,28 @@ pub fn move_vertically(
5750 count: usize,
5851 behaviour: Movement,
5952) -> Range {
60− let Position { row, col } = coords_at_pos(slice, range.head);
53+ let pos = range.cursor(slice);
6154
55+ // Compute the current position's 2d coordinates.
56+ let Position { row, col } = coords_at_pos(slice, pos);
6257 let horiz = range.horiz.unwrap_or(col as u32);
6358
64− let new_line = match dir {
59+ // Compute the new position.
60+ let new_row = match dir {
61+ Direction::Forward => (row + count).min(slice.len_lines().saturating_sub(1)),
6562 Direction::Backward => row.saturating_sub(count),
66− Direction::Forward => std::cmp::min(
67− row.saturating_add(count),
68− slice.len_lines().saturating_sub(2),
69− ),
7063 };
64+ let new_col = col.max(horiz as usize);
65+ let new_pos = pos_at_coords(slice, Position::new(new_row, new_col), true);
7166
72− // Length of the line sans line-ending.
73− let new_line_len = {
74− let line = slice.line(new_line);
75− line.len_chars() - get_line_ending(&line).map(|le| le.len_chars()).unwrap_or(0)
76− };
77−
78− let new_col = std::cmp::min(horiz as usize, new_line_len);
79−
80− let pos = pos_at_coords(slice, Position::new(new_line, new_col));
81−
82− let anchor = match behaviour {
83− Movement::Extend => range.anchor,
84− Movement::Move => pos,
85− };
67+ // Special-case to avoid moving to the end of the last non-empty line.
68+ if behaviour == Movement::Extend && slice.line(new_row).len_chars() == 0 {
69+ return range;
70+ }
8671
87− let mut range = Range::new(anchor, pos);
88− range.horiz = Some(horiz);
89− range
72+ let mut new_range = range.put_cursor(slice, new_pos, behaviour == Movement::Extend);
73+ new_range.horiz = Some(horiz);
74+ new_range
9075}
9176
9277pub fn move_next_word_start(slice: RopeSlice, range: Range, count: usize) -> Range {
@@ -118,8 +103,41 @@ pub fn move_prev_word_end(slice: RopeSlice, range: Range, count: usize) -> Range
118103}
119104
120105fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTarget) -> Range {
121− (0..count).fold(range, |range, _| {
122− slice.chars_at(range.head).range_to_target(target, range)
106+ let is_prev = matches!(
107+ target,
108+ WordMotionTarget::PrevWordStart
109+ | WordMotionTarget::PrevLongWordStart
110+ | WordMotionTarget::PrevWordEnd
111+ );
112+
113+ // Special-case early-out.
114+ if (is_prev && range.head == 0) || (!is_prev && range.head == slice.len_chars()) {
115+ return range;
116+ }
117+
118+ // Prepare the range appropriately based on the target movement
119+ // direction. This is addressing two things at once:
120+ //
121+ // 1. Block-cursor semantics.
122+ // 2. The anchor position being irrelevant to the output result.
123+ #[allow(clippy::collapsible_else_if)] // Makes the structure clearer in this case.
124+ let start_range = if is_prev {
125+ if range.anchor < range.head {
126+ Range::new(range.head, prev_grapheme_boundary(slice, range.head))
127+ } else {
128+ Range::new(next_grapheme_boundary(slice, range.head), range.head)
129+ }
130+ } else {
131+ if range.anchor < range.head {
132+ Range::new(prev_grapheme_boundary(slice, range.head), range.head)
133+ } else {
134+ Range::new(range.head, next_grapheme_boundary(slice, range.head))
135+ }
136+ };
137+
138+ // Do the main work.
139+ (0..count).fold(start_range, |r, _| {
140+ slice.chars_at(r.head).range_to_target(target, r)
123141 })
124142}
125143
@@ -176,79 +194,75 @@ pub trait CharHelpers {
176194 fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range;
177195}
178196
179−enum WordMotionPhase {
180− Start,
181− SkipNewlines,
182− ReachTarget,
183−}
184−
185197impl CharHelpers for Chars<'_> {
198+ /// Note: this only changes the anchor of the range if the head is effectively
199+ /// starting on a boundary (either directly or after skipping newline characters).
200+ /// Any other changes to the anchor should be handled by the calling code.
186201 fn range_to_target(&mut self, target: WordMotionTarget, origin: Range) -> Range {
187− // Characters are iterated forward or backwards depending on the motion direction.
188− let characters: Box<dyn Iterator<Item = char>> = match target {
202+ let is_prev = matches!(
203+ target,
189204 WordMotionTarget::PrevWordStart
190− | WordMotionTarget::PrevLongWordStart
191− | WordMotionTarget::PrevWordEnd => {
205+ | WordMotionTarget::PrevLongWordStart
206+ | WordMotionTarget::PrevWordEnd
207+ );
208+
209+ // Reverse the iterator if needed for the motion direction.
210+ if is_prev {
211+ self.reverse();
212+ }
213+
214+ // Function to advance index in the appropriate motion direction.
215+ let advance: &dyn Fn(&mut usize) = if is_prev {
216+ &|idx| *idx = idx.saturating_sub(1)
217+ } else {
218+ &|idx| *idx += 1
219+ };
220+
221+ // Initialize state variables.
222+ let mut anchor = origin.anchor;
223+ let mut head = origin.head;
224+ let mut prev_ch = {
225+ let ch = self.prev();
226+ if ch.is_some() {
192227 self.next();
193− Box::new(from_fn(|| self.prev()))
194228 }
195− _ => Box::new(self),
229+ ch
196230 };
197231
198− // Index advancement also depends on the direction.
199− let advance: &dyn Fn(&mut usize) = match target {
200− WordMotionTarget::PrevWordStart
201− | WordMotionTarget::PrevLongWordStart
202− | WordMotionTarget::PrevWordEnd => &|u| *u = u.saturating_sub(1),
203− _ => &|u| *u += 1,
204− };
232+ // Skip any initial newline characters.
233+ while let Some(ch) = self.next() {
234+ if char_is_line_ending(ch) {
235+ prev_ch = Some(ch);
236+ advance(&mut head);
237+ } else {
238+ self.prev();
239+ break;
240+ }
241+ }
242+ if prev_ch.map(char_is_line_ending).unwrap_or(false) {
243+ anchor = head;
244+ }
205245
206− let mut characters = characters.peekable();
207− let mut phase = WordMotionPhase::Start;
208− let mut head = origin.head;
209− let mut anchor: Option<usize> = None;
210− let is_boundary =
211− |a: char, b: Option<char>| categorize_char(a) != categorize_char(b.unwrap_or(a));
212− while let Some(peek) = characters.peek().copied() {
213− phase = match phase {
214− WordMotionPhase::Start => {
215− characters.next();
216− if characters.peek().is_none() {
217− break; // We're at the end, so there's nothing to do.
218− }
219− // Anchor may remain here if the head wasn't at a boundary
220− if !is_boundary(peek, characters.peek().copied()) && !char_is_line_ending(peek)
221− {
222− anchor = Some(head);
223− }
224− // First character is always skipped by the head
225− advance(&mut head);
226− WordMotionPhase::SkipNewlines
227− }
228− WordMotionPhase::SkipNewlines => {
229− if char_is_line_ending(peek) {
230− characters.next();
231− if characters.peek().is_some() {
232− advance(&mut head);
233− }
234− WordMotionPhase::SkipNewlines
235− } else {
236− WordMotionPhase::ReachTarget
237− }
238− }
239− WordMotionPhase::ReachTarget => {
240− characters.next();
241− anchor = anchor.or(Some(head));
242− if reached_target(target, peek, characters.peek()) {
243− break;
244− } else {
245− advance(&mut head);
246− }
247− WordMotionPhase::ReachTarget
246+ // Find our target position(s).
247+ let head_start = head;
248+ while let Some(next_ch) = self.next() {
249+ if prev_ch.is_none() || reached_target(target, prev_ch.unwrap(), next_ch) {
250+ if head == head_start {
251+ anchor = head;
252+ } else {
253+ break;
248254 }
249255 }
256+ prev_ch = Some(next_ch);
257+ advance(&mut head);
258+ }
259+
260+ // Un-reverse the iterator if needed.
261+ if is_prev {
262+ self.reverse();
250263 }
251− Range::new(anchor.unwrap_or(origin.anchor), head)
264+
265+ Range::new(anchor, head)
252266 }
253267}
254268
@@ -265,28 +279,23 @@ fn is_long_word_boundary(a: char, b: char) -> bool {
265279 }
266280}
267281
268−fn reached_target(target: WordMotionTarget, peek: char, next_peek: Option<&char>) -> bool {
269− let next_peek = match next_peek {
270− Some(next_peek) => next_peek,
271− None => return true,
272− };
273−
282+fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> bool {
274283 match target {
275284 WordMotionTarget::NextWordStart | WordMotionTarget::PrevWordEnd => {
276− is_word_boundary(peek, *next_peek)
277− && (char_is_line_ending(*next_peek) || !next_peek.is_whitespace())
285+ is_word_boundary(prev_ch, next_ch)
286+ && (char_is_line_ending(next_ch) || !next_ch.is_whitespace())
278287 }
279288 WordMotionTarget::NextWordEnd | WordMotionTarget::PrevWordStart => {
280− is_word_boundary(peek, *next_peek)
281− && (!peek.is_whitespace() || char_is_line_ending(*next_peek))
289+ is_word_boundary(prev_ch, next_ch)
290+ && (!prev_ch.is_whitespace() || char_is_line_ending(next_ch))
282291 }
283292 WordMotionTarget::NextLongWordStart => {
284− is_long_word_boundary(peek, *next_peek)
285− && (char_is_line_ending(*next_peek) || !next_peek.is_whitespace())
293+ is_long_word_boundary(prev_ch, next_ch)
294+ && (char_is_line_ending(next_ch) || !next_ch.is_whitespace())
286295 }
287296 WordMotionTarget::NextLongWordEnd | WordMotionTarget::PrevLongWordStart => {
288− is_long_word_boundary(peek, *next_peek)
289− && (!peek.is_whitespace() || char_is_line_ending(*next_peek))
297+ is_long_word_boundary(prev_ch, next_ch)
298+ && (!prev_ch.is_whitespace() || char_is_line_ending(next_ch))
290299 }
291300 }
292301}
@@ -317,7 +326,7 @@ mod test {
317326 fn test_vertical_move() {
318327 let text = Rope::from("abcd\nefg\nwrs");
319328 let slice = text.slice(..);
320− let pos = pos_at_coords(slice, (0, 4).into());
329+ let pos = pos_at_coords(slice, (0, 4).into(), true);
321330
322331 let range = Range::new(pos, pos);
323332 assert_eq!(
@@ -330,10 +339,10 @@ mod test {
330339 }
331340
332341 #[test]
333− fn horizontal_moves_through_single_line_in_single_line_text() {
342+ fn horizontal_moves_through_single_line_text() {
334343 let text = Rope::from(SINGLE_LINE_SAMPLE);
335344 let slice = text.slice(..);
336− let position = pos_at_coords(slice, (0, 0).into());
345+ let position = pos_at_coords(slice, (0, 0).into(), true);
337346
338347 let mut range = Range::point(position);
339348
@@ -353,23 +362,23 @@ mod test {
353362 }
354363
355364 #[test]
356− fn horizontal_moves_through_single_line_in_multiline_text() {
365+ fn horizontal_moves_through_multiline_text() {
357366 let text = Rope::from(MULTILINE_SAMPLE);
358367 let slice = text.slice(..);
359− let position = pos_at_coords(slice, (0, 0).into());
368+ let position = pos_at_coords(slice, (0, 0).into(), true);
360369
361370 let mut range = Range::point(position);
362371
363372 let moves_and_expected_coordinates = IntoIter::new([
364− ((Direction::Forward, 1usize), (0, 1)), // M|ultiline\n
365− ((Direction::Forward, 2usize), (0, 3)), // Mul|tiline\n
366− ((Direction::Backward, 6usize), (0, 0)), // |Multiline\n
367− ((Direction::Backward, 999usize), (0, 0)), // |Multiline\n
368− ((Direction::Forward, 3usize), (0, 3)), // Mul|tiline\n
369− ((Direction::Forward, 0usize), (0, 3)), // Mul|tiline\n
370− ((Direction::Backward, 0usize), (0, 3)), // Mul|tiline\n
371− ((Direction::Forward, 999usize), (0, 9)), // Multiline|\n
372− ((Direction::Forward, 999usize), (0, 9)), // Multiline|\n
373+ ((Direction::Forward, 11usize), (1, 1)), // Multiline\nt|ext sample\n...
374+ ((Direction::Backward, 1usize), (1, 0)), // Multiline\n|text sample\n...
375+ ((Direction::Backward, 5usize), (0, 5)), // Multi|line\ntext sample\n...
376+ ((Direction::Backward, 999usize), (0, 0)), // |Multiline\ntext sample\n...
377+ ((Direction::Forward, 3usize), (0, 3)), // Mul|tiline\ntext sample\n...
378+ ((Direction::Forward, 0usize), (0, 3)), // Mul|tiline\ntext sample\n...
379+ ((Direction::Backward, 0usize), (0, 3)), // Mul|tiline\ntext sample\n...
380+ ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
381+ ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
373382 ]);
374383
375384 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
@@ -383,7 +392,7 @@ mod test {
383392 fn selection_extending_moves_in_single_line_text() {
384393 let text = Rope::from(SINGLE_LINE_SAMPLE);
385394 let slice = text.slice(..);
386− let position = pos_at_coords(slice, (0, 0).into());
395+ let position = pos_at_coords(slice, (0, 0).into(), true);
387396
388397 let mut range = Range::point(position);
389398 let original_anchor = range.anchor;
@@ -403,18 +412,19 @@ mod test {
403412 #[test]
404413 fn vertical_moves_in_single_column() {
405414 let text = Rope::from(MULTILINE_SAMPLE);
406− let slice = dbg!(&text).slice(..);
407− let position = pos_at_coords(slice, (0, 0).into());
415+ let slice = text.slice(..);
416+ let position = pos_at_coords(slice, (0, 0).into(), true);
408417 let mut range = Range::point(position);
409418 let moves_and_expected_coordinates = IntoIter::new([
410419 ((Direction::Forward, 1usize), (1, 0)),
411420 ((Direction::Forward, 2usize), (3, 0)),
421+ ((Direction::Forward, 1usize), (4, 0)),
412422 ((Direction::Backward, 999usize), (0, 0)),
413− ((Direction::Forward, 3usize), (3, 0)),
414− ((Direction::Forward, 0usize), (3, 0)),
415− ((Direction::Backward, 0usize), (3, 0)),
416− ((Direction::Forward, 5), (4, 0)),
417− ((Direction::Forward, 999usize), (4, 0)),
423+ ((Direction::Forward, 4usize), (4, 0)),
424+ ((Direction::Forward, 0usize), (4, 0)),
425+ ((Direction::Backward, 0usize), (4, 0)),
426+ ((Direction::Forward, 5), (5, 0)),
427+ ((Direction::Forward, 999usize), (5, 0)),
418428 ]);
419429
420430 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
@@ -428,7 +438,7 @@ mod test {
428438 fn vertical_moves_jumping_column() {
429439 let text = Rope::from(MULTILINE_SAMPLE);
430440 let slice = text.slice(..);
431− let position = pos_at_coords(slice, (0, 0).into());
441+ let position = pos_at_coords(slice, (0, 0).into(), true);
432442 let mut range = Range::point(position);
433443
434444 enum Axis {
@@ -446,7 +456,8 @@ mod test {
446456 ((Axis::V, Direction::Forward, 1usize), (3, 8)),
447457 // Behaviour is preserved even through long jumps
448458 ((Axis::V, Direction::Backward, 999usize), (0, 8)),
449− ((Axis::V, Direction::Forward, 999usize), (4, 8)),
459+ ((Axis::V, Direction::Forward, 4usize), (4, 8)),
460+ ((Axis::V, Direction::Forward, 999usize), (5, 0)),
450461 ]);
451462
452463 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
@@ -460,10 +471,10 @@ mod test {
460471 }
461472
462473 #[test]
463− fn multibyte_character_column_jumps() {
474+ fn multibyte_character_wide_column_jumps() {
464475 let text = Rope::from(MULTIBYTE_CHARACTER_SAMPLE);
465476 let slice = text.slice(..);
466− let position = pos_at_coords(slice, (0, 0).into());
477+ let position = pos_at_coords(slice, (0, 0).into(), true);
467478 let mut range = Range::point(position);
468479
469480 // FIXME: The behaviour captured in this test diverges from both Kakoune and Vim. These
@@ -474,10 +485,14 @@ mod test {
474485 V,
475486 }
476487 let moves_and_expected_coordinates = IntoIter::new([
477− // Places cursor at the fourth kana
488+ // Places cursor at the fourth kana.
478489 ((Axis::H, Direction::Forward, 4), (0, 4)),
479− // Descent places cursor at the fourth character.
490+ // Descent places cursor at the 4th character.
480491 ((Axis::V, Direction::Forward, 1usize), (1, 4)),
492+ // Moving back 1 character.
493+ ((Axis::H, Direction::Backward, 1usize), (1, 3)),
494+ // Jumping back up 1 line.
495+ ((Axis::V, Direction::Backward, 1usize), (0, 3)),
481496 ]);
482497
483498 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
@@ -512,42 +527,42 @@ mod test {
512527 fn test_behaviour_when_moving_to_start_of_next_words() {
513528 let tests = array::IntoIter::new([
514529 ("Basic forward motion stops at the first space",
515− vec![(1, Range::new(0, 0), Range::new(0, 5))]),
530+ vec![(1, Range::new(0, 0), Range::new(0, 6))]),
516531 (" Starting from a boundary advances the anchor",
517− vec![(1, Range::new(0, 0), Range::new(1, 9))]),
532+ vec![(1, Range::new(0, 0), Range::new(1, 10))]),
518533 ("Long whitespace gap is bridged by the head",
519− vec![(1, Range::new(0, 0), Range::new(0, 10))]),
534+ vec![(1, Range::new(0, 0), Range::new(0, 11))]),
520535 ("Previous anchor is irrelevant for forward motions",
521− vec![(1, Range::new(12, 0), Range::new(0, 8))]),
536+ vec![(1, Range::new(12, 0), Range::new(0, 9))]),
522537 (" Starting from whitespace moves to last space in sequence",
523− vec![(1, Range::new(0, 0), Range::new(0, 3))]),
538+ vec![(1, Range::new(0, 0), Range::new(0, 4))]),
524539 ("Starting from mid-word leaves anchor at start position and moves head",
525− vec![(1, Range::new(3, 3), Range::new(3, 8))]),
540+ vec![(1, Range::new(3, 3), Range::new(3, 9))]),
526541 ("Identifiers_with_underscores are considered a single word",
527− vec![(1, Range::new(0, 0), Range::new(0, 28))]),
542+ vec![(1, Range::new(0, 0), Range::new(0, 29))]),
528543 ("Jumping\n into starting whitespace selects the spaces before 'into'",
529− vec![(1, Range::new(0, 6), Range::new(8, 11))]),
544+ vec![(1, Range::new(0, 7), Range::new(8, 12))]),
530545 ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
531546 vec![
532− (1, Range::new(0, 0), Range::new(0, 11)),
533− (1, Range::new(0, 11), Range::new(12, 14)),
534− (1, Range::new(12, 14), Range::new(15, 17))
547+ (1, Range::new(0, 0), Range::new(0, 12)),
548+ (1, Range::new(0, 12), Range::new(12, 15)),
549+ (1, Range::new(12, 15), Range::new(15, 18))
535550 ]),
536551 ("... ... punctuation and spaces behave as expected",
537552 vec![
538− (1, Range::new(0, 0), Range::new(0, 5)),
539− (1, Range::new(0, 5), Range::new(6, 9)),
553+ (1, Range::new(0, 0), Range::new(0, 6)),
554+ (1, Range::new(0, 6), Range::new(6, 10)),
540555 ]),
541556 (".._.._ punctuation is not joined by underscores into a single block",
542− vec![(1, Range::new(0, 0), Range::new(0, 1))]),
557+ vec![(1, Range::new(0, 0), Range::new(0, 2))]),
543558 ("Newlines\n\nare bridged seamlessly.",
544559 vec![
545− (1, Range::new(0, 0), Range::new(0, 7)),
546− (1, Range::new(0, 7), Range::new(10, 13)),
560+ (1, Range::new(0, 0), Range::new(0, 8)),
561+ (1, Range::new(0, 8), Range::new(10, 14)),
547562 ]),
548563 ("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.",
549564 vec![
550− (1, Range::new(0, 8), Range::new(13, 15)),
565+ (1, Range::new(0, 9), Range::new(13, 16)),
551566 ]),
552567 ("A failed motion does not modify the range",
553568 vec![
@@ -555,17 +570,17 @@ mod test {
555570 ]),
556571 ("oh oh oh two character words!",
557572 vec![
558− (1, Range::new(0, 0), Range::new(0, 2)),
559− (1, Range::new(0, 2), Range::new(3, 5)),
560− (1, Range::new(0, 1), Range::new(2, 2)),
573+ (1, Range::new(0, 0), Range::new(0, 3)),
574+ (1, Range::new(0, 3), Range::new(3, 6)),
575+ (1, Range::new(0, 2), Range::new(1, 3)),
561576 ]),
562577 ("Multiple motions at once resolve correctly",
563578 vec![
564− (3, Range::new(0, 0), Range::new(17, 19)),
579+ (3, Range::new(0, 0), Range::new(17, 20)),
565580 ]),
566581 ("Excessive motions are performed partially",
567582 vec![
568− (999, Range::new(0, 0), Range::new(32, 40)),
583+ (999, Range::new(0, 0), Range::new(32, 41)),
569584 ]),
570585 ("", // Edge case of moving forward in empty string
571586 vec![
@@ -573,16 +588,16 @@ mod test {
573588 ]),
574589 ("\n\n\n\n\n", // Edge case of moving forward in all newlines
575590 vec![
576− (1, Range::new(0, 0), Range::new(0, 4)),
591+ (1, Range::new(0, 0), Range::new(5, 5)),
577592 ]),
578593 ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
579594 vec![
580− (1, Range::new(0, 0), Range::new(1, 3)),
581− (1, Range::new(1, 3), Range::new(5, 7)),
595+ (1, Range::new(0, 0), Range::new(1, 4)),
596+ (1, Range::new(1, 4), Range::new(5, 8)),
582597 ]),
583598 ("ヒーリクス multibyte characters behave as normal characters",
584599 vec![
585− (1, Range::new(0, 0), Range::new(0, 5)),
600+ (1, Range::new(0, 0), Range::new(0, 6)),
586601 ]),
587602 ]);
588603
@@ -598,40 +613,40 @@ mod test {
598613 fn test_behaviour_when_moving_to_start_of_next_long_words() {
599614 let tests = array::IntoIter::new([
600615 ("Basic forward motion stops at the first space",
601− vec![(1, Range::new(0, 0), Range::new(0, 5))]),
616+ vec![(1, Range::new(0, 0), Range::new(0, 6))]),
602617 (" Starting from a boundary advances the anchor",
603− vec![(1, Range::new(0, 0), Range::new(1, 9))]),
618+ vec![(1, Range::new(0, 0), Range::new(1, 10))]),
604619 ("Long whitespace gap is bridged by the head",
605− vec![(1, Range::new(0, 0), Range::new(0, 10))]),
620+ vec![(1, Range::new(0, 0), Range::new(0, 11))]),
606621 ("Previous anchor is irrelevant for forward motions",
607− vec![(1, Range::new(12, 0), Range::new(0, 8))]),
622+ vec![(1, Range::new(12, 0), Range::new(0, 9))]),
608623 (" Starting from whitespace moves to last space in sequence",
609− vec![(1, Range::new(0, 0), Range::new(0, 3))]),
624+ vec![(1, Range::new(0, 0), Range::new(0, 4))]),
610625 ("Starting from mid-word leaves anchor at start position and moves head",
611− vec![(1, Range::new(3, 3), Range::new(3, 8))]),
626+ vec![(1, Range::new(3, 3), Range::new(3, 9))]),
612627 ("Identifiers_with_underscores are considered a single word",
613− vec![(1, Range::new(0, 0), Range::new(0, 28))]),
628+ vec![(1, Range::new(0, 0), Range::new(0, 29))]),
614629 ("Jumping\n into starting whitespace selects the spaces before 'into'",
615− vec![(1, Range::new(0, 6), Range::new(8, 11))]),
630+ vec![(1, Range::new(0, 7), Range::new(8, 12))]),
616631 ("alphanumeric.!,and.?=punctuation are not treated any differently than alphanumerics",
617632 vec![
618− (1, Range::new(0, 0), Range::new(0, 32)),
633+ (1, Range::new(0, 0), Range::new(0, 33)),
619634 ]),
620635 ("... ... punctuation and spaces behave as expected",
621636 vec![
622− (1, Range::new(0, 0), Range::new(0, 5)),
623− (1, Range::new(0, 5), Range::new(6, 9)),
637+ (1, Range::new(0, 0), Range::new(0, 6)),
638+ (1, Range::new(0, 6), Range::new(6, 10)),
624639 ]),
625640 (".._.._ punctuation is joined by underscores into a single word, as it behaves like alphanumerics",
626− vec![(1, Range::new(0, 0), Range::new(0, 6))]),
641+ vec![(1, Range::new(0, 0), Range::new(0, 7))]),
627642 ("Newlines\n\nare bridged seamlessly.",
628643 vec![
629− (1, Range::new(0, 0), Range::new(0, 7)),
630− (1, Range::new(0, 7), Range::new(10, 13)),
644+ (1, Range::new(0, 0), Range::new(0, 8)),
645+ (1, Range::new(0, 8), Range::new(10, 14)),
631646 ]),
632647 ("Jumping\n\n\n\n\n\n from newlines to whitespace selects whitespace.",
633648 vec![
634− (1, Range::new(0, 8), Range::new(13, 15)),
649+ (1, Range::new(0, 9), Range::new(13, 16)),
635650 ]),
636651 ("A failed motion does not modify the range",
637652 vec![
@@ -639,17 +654,17 @@ mod test {
639654 ]),
640655 ("oh oh oh two character words!",
641656 vec![
642− (1, Range::new(0, 0), Range::new(0, 2)),
643− (1, Range::new(0, 2), Range::new(3, 5)),
644− (1, Range::new(0, 1), Range::new(2, 2)),
657+ (1, Range::new(0, 0), Range::new(0, 3)),
658+ (1, Range::new(0, 3), Range::new(3, 6)),
659+ (1, Range::new(0, 1), Range::new(0, 3)),
645660 ]),
646661 ("Multiple motions at once resolve correctly",
647662 vec![
648− (3, Range::new(0, 0), Range::new(17, 19)),
663+ (3, Range::new(0, 0), Range::new(17, 20)),
649664 ]),
650665 ("Excessive motions are performed partially",
651666 vec![
652− (999, Range::new(0, 0), Range::new(32, 40)),
667+ (999, Range::new(0, 0), Range::new(32, 41)),
653668 ]),
654669 ("", // Edge case of moving forward in empty string
655670 vec![
@@ -657,16 +672,16 @@ mod test {
657672 ]),
658673 ("\n\n\n\n\n", // Edge case of moving forward in all newlines
659674 vec![
660− (1, Range::new(0, 0), Range::new(0, 4)),
675+ (1, Range::new(0, 0), Range::new(5, 5)),
661676 ]),
662677 ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
663678 vec![
664− (1, Range::new(0, 0), Range::new(1, 3)),
665− (1, Range::new(1, 3), Range::new(5, 7)),
679+ (1, Range::new(0, 0), Range::new(1, 4)),
680+ (1, Range::new(1, 4), Range::new(5, 8)),
666681 ]),
667682 ("ヒー..リクス multibyte characters behave as normal characters, including their interaction with punctuation",
668683 vec![
669− (1, Range::new(0, 0), Range::new(0, 7)),
684+ (1, Range::new(0, 0), Range::new(0, 8)),
670685 ]),
671686 ]);
672687
@@ -682,44 +697,47 @@ mod test {
682697 fn test_behaviour_when_moving_to_start_of_previous_words() {
683698 let tests = array::IntoIter::new([
684699 ("Basic backward motion from the middle of a word",
685− vec![(1, Range::new(3, 3), Range::new(3, 0))]),
686− ("Starting from after boundary retreats the anchor",
687− vec![(1, Range::new(0, 8), Range::new(7, 0))]),
700+ vec![(1, Range::new(3, 3), Range::new(4, 0))]),
701+
702+ // // Why do we want this behavior? The current behavior fails this
703+ // // test, but seems better and more consistent.
704+ // ("Starting from after boundary retreats the anchor",
705+ // vec![(1, Range::new(0, 9), Range::new(8, 0))]),
706+
688707 (" Jump to start of a word preceded by whitespace",
689− vec![(1, Range::new(5, 5), Range::new(5, 4))]),
708+ vec![(1, Range::new(5, 5), Range::new(6, 4))]),
690709 (" Jump to start of line from start of word preceded by whitespace",
691− vec![(1, Range::new(4, 4), Range::new(3, 0))]),
710+ vec![(1, Range::new(4, 4), Range::new(4, 0))]),
692711 ("Previous anchor is irrelevant for backward motions",
693− vec![(1, Range::new(12, 5), Range::new(5, 0))]),
712+ vec![(1, Range::new(12, 5), Range::new(6, 0))]),
694713 (" Starting from whitespace moves to first space in sequence",
695− vec![(1, Range::new(0, 3), Range::new(3, 0))]),
714+ vec![(1, Range::new(0, 4), Range::new(4, 0))]),
696715 ("Identifiers_with_underscores are considered a single word",
697716 vec![(1, Range::new(0, 20), Range::new(20, 0))]),
698717 ("Jumping\n \nback through a newline selects whitespace",
699− vec![(1, Range::new(0, 13), Range::new(11, 8))]),
718+ vec![(1, Range::new(0, 13), Range::new(12, 8))]),
700719 ("Jumping to start of word from the end selects the word",
701− vec![(1, Range::new(6, 6), Range::new(6, 0))]),
720+ vec![(1, Range::new(6, 7), Range::new(7, 0))]),
702721 ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
703722 vec![
704− (1, Range::new(30, 30), Range::new(30, 21)),
705− (1, Range::new(30, 21), Range::new(20, 18)),
706− (1, Range::new(20, 18), Range::new(17, 15))
723+ (1, Range::new(29, 30), Range::new(30, 21)),
724+ (1, Range::new(30, 21), Range::new(21, 18)),
725+ (1, Range::new(21, 18), Range::new(18, 15))
707726 ]),
708−
709727 ("... ... punctuation and spaces behave as expected",
710728 vec![
711− (1, Range::new(0, 10), Range::new(9, 6)),
712− (1, Range::new(9, 6), Range::new(5, 0)),
729+ (1, Range::new(0, 10), Range::new(10, 6)),
730+ (1, Range::new(10, 6), Range::new(6, 0)),
713731 ]),
714732 (".._.._ punctuation is not joined by underscores into a single block",
715− vec![(1, Range::new(0, 5), Range::new(4, 3))]),
733+ vec![(1, Range::new(0, 6), Range::new(5, 3))]),
716734 ("Newlines\n\nare bridged seamlessly.",
717735 vec![
718− (1, Range::new(0, 10), Range::new(7, 0)),
736+ (1, Range::new(0, 10), Range::new(8, 0)),
719737 ]),
720738 ("Jumping \n\n\n\n\nback from within a newline group selects previous block",
721739 vec![
722− (1, Range::new(0, 13), Range::new(10, 0)),
740+ (1, Range::new(0, 13), Range::new(11, 0)),
723741 ]),
724742 ("Failed motions do not modify the range",
725743 vec![
@@ -727,11 +745,11 @@ mod test {
727745 ]),
728746 ("Multiple motions at once resolve correctly",
729747 vec![
730− (3, Range::new(18, 18), Range::new(8, 0)),
748+ (3, Range::new(18, 18), Range::new(9, 0)),
731749 ]),
732750 ("Excessive motions are performed partially",
733751 vec![
734− (999, Range::new(40, 40), Range::new(9, 0)),
752+ (999, Range::new(40, 40), Range::new(10, 0)),
735753 ]),
736754 ("", // Edge case of moving backwards in empty string
737755 vec![
@@ -739,16 +757,16 @@ mod test {
739757 ]),
740758 ("\n\n\n\n\n", // Edge case of moving backwards in all newlines
741759 vec![
742− (1, Range::new(0, 0), Range::new(0, 0)),
760+ (1, Range::new(5, 5), Range::new(0, 0)),
743761 ]),
744762 (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
745763 vec![
746− (1, Range::new(0, 7), Range::new(6, 4)),
747− (1, Range::new(6, 4), Range::new(2, 0)),
764+ (1, Range::new(0, 8), Range::new(7, 4)),
765+ (1, Range::new(7, 4), Range::new(3, 0)),
748766 ]),
749767 ("ヒーリクス multibyte characters behave as normal characters",
750768 vec![
751− (1, Range::new(0, 5), Range::new(4, 0)),
769+ (1, Range::new(0, 6), Range::new(6, 0)),
752770 ]),
753771 ]);
754772
@@ -763,72 +781,89 @@ mod test {
763781 #[test]
764782 fn test_behaviour_when_moving_to_start_of_previous_long_words() {
765783 let tests = array::IntoIter::new([
766− ("Basic backward motion from the middle of a word",
767− vec![(1, Range::new(3, 3), Range::new(3, 0))]),
768− ("Starting from after boundary retreats the anchor",
769− vec![(1, Range::new(0, 8), Range::new(7, 0))]),
770− (" Jump to start of a word preceded by whitespace",
771− vec![(1, Range::new(5, 5), Range::new(5, 4))]),
772− (" Jump to start of line from start of word preceded by whitespace",
773− vec![(1, Range::new(4, 4), Range::new(3, 0))]),
784+ (
785+ "Basic backward motion from the middle of a word",
786+ vec![(1, Range::new(3, 3), Range::new(4, 0))],
787+ ),
788+
789+ // // Why do we want this behavior? The current behavior fails this
790+ // // test, but seems better and more consistent.
791+ // ("Starting from after boundary retreats the anchor",
792+ // vec![(1, Range::new(0, 9), Range::new(8, 0))]),
793+
794+ (
795+ " Jump to start of a word preceded by whitespace",
796+ vec![(1, Range::new(5, 5), Range::new(6, 4))],
797+ ),
798+ (
799+ " Jump to start of line from start of word preceded by whitespace",
800+ vec![(1, Range::new(3, 4), Range::new(4, 0))],
801+ ),
774802 ("Previous anchor is irrelevant for backward motions",
775− vec![(1, Range::new(12, 5), Range::new(5, 0))]),
776− (" Starting from whitespace moves to first space in sequence",
777− vec![(1, Range::new(0, 3), Range::new(3, 0))]),
803+ vec![(1, Range::new(12, 5), Range::new(6, 0))]),
804+ (
805+ " Starting from whitespace moves to first space in sequence",
806+ vec![(1, Range::new(0, 4), Range::new(4, 0))],
807+ ),
778808 ("Identifiers_with_underscores are considered a single word",
779809 vec![(1, Range::new(0, 20), Range::new(20, 0))]),
780− ("Jumping\n \nback through a newline selects whitespace",
781− vec![(1, Range::new(0, 13), Range::new(11, 8))]),
782− ("Jumping to start of word from the end selects the word",
783− vec![(1, Range::new(6, 6), Range::new(6, 0))]),
784− ("alphanumeric.!,and.?=punctuation are treated exactly the same",
785− vec![
786− (1, Range::new(30, 30), Range::new(30, 0)),
787− ]),
788−
789− ("... ... punctuation and spaces behave as expected",
810+ (
811+ "Jumping\n \nback through a newline selects whitespace",
812+ vec![(1, Range::new(0, 13), Range::new(12, 8))],
813+ ),
814+ (
815+ "Jumping to start of word from the end selects the word",
816+ vec![(1, Range::new(6, 7), Range::new(7, 0))],
817+ ),
818+ (
819+ "alphanumeric.!,and.?=punctuation are treated exactly the same",
820+ vec![(1, Range::new(29, 30), Range::new(30, 0))],
821+ ),
822+ (
823+ "... ... punctuation and spaces behave as expected",
790824 vec![
791− (1, Range::new(0, 10), Range::new(9, 6)),
792− (1, Range::new(9, 6), Range::new(5, 0)),
793− ]),
825+ (1, Range::new(0, 10), Range::new(10, 6)),
826+ (1, Range::new(10, 6), Range::new(6, 0)),
827+ ],
828+ ),
794829 (".._.._ punctuation is joined by underscores into a single block",
795− vec![(1, Range::new(0, 5), Range::new(4, 0))]),
796− ("Newlines\n\nare bridged seamlessly.",
797− vec![
798− (1, Range::new(0, 10), Range::new(7, 0)),
799− ]),
800− ("Jumping \n\n\n\n\nback from within a newline group selects previous block",
801− vec![
802− (1, Range::new(0, 13), Range::new(10, 0)),
803− ]),
804− ("Failed motions do not modify the range",
805− vec![
806− (0, Range::new(3, 0), Range::new(3, 0)),
807− ]),
808− ("Multiple motions at once resolve correctly",
809− vec![
810− (3, Range::new(18, 18), Range::new(8, 0)),
811− ]),
812− ("Excessive motions are performed partially",
813− vec![
814− (999, Range::new(40, 40), Range::new(9, 0)),
815− ]),
816− ("", // Edge case of moving backwards in empty string
817− vec![
818− (1, Range::new(0, 0), Range::new(0, 0)),
819− ]),
820− ("\n\n\n\n\n", // Edge case of moving backwards in all newlines
821− vec![
822− (1, Range::new(0, 0), Range::new(0, 0)),
823− ]),
830+ vec![(1, Range::new(0, 6), Range::new(6, 0))]),
831+ (
832+ "Newlines\n\nare bridged seamlessly.",
833+ vec![(1, Range::new(0, 10), Range::new(8, 0))],
834+ ),
835+ (
836+ "Jumping \n\n\n\n\nback from within a newline group selects previous block",
837+ vec![(1, Range::new(0, 13), Range::new(11, 0))],
838+ ),
839+ (
840+ "Failed motions do not modify the range",
841+ vec![(0, Range::new(3, 0), Range::new(3, 0))],
842+ ),
843+ (
844+ "Multiple motions at once resolve correctly",
845+ vec![(3, Range::new(19, 19), Range::new(9, 0))],
846+ ),
847+ (
848+ "Excessive motions are performed partially",
849+ vec![(999, Range::new(40, 40), Range::new(10, 0))],
850+ ),
851+ (
852+ "", // Edge case of moving backwards in empty string
853+ vec![(1, Range::new(0, 0), Range::new(0, 0))],
854+ ),
855+ (
856+ "\n\n\n\n\n", // Edge case of moving backwards in all newlines
857+ vec![(1, Range::new(5, 5), Range::new(0, 0))],
858+ ),
824859 (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
825860 vec![
826− (1, Range::new(0, 7), Range::new(6, 4)),
827− (1, Range::new(6, 4), Range::new(2, 0)),
861+ (1, Range::new(0, 8), Range::new(7, 4)),
862+ (1, Range::new(7, 4), Range::new(3, 0)),
828863 ]),
829864 ("ヒーリ..クス multibyte characters behave as normal characters, including when interacting with punctuation",
830865 vec![
831− (1, Range::new(0, 7), Range::new(6, 0)),
866+ (1, Range::new(0, 8), Range::new(8, 0)),
832867 ]),
833868 ]);
834869
@@ -844,42 +879,46 @@ mod test {
844879 fn test_behaviour_when_moving_to_end_of_next_words() {
845880 let tests = array::IntoIter::new([
846881 ("Basic forward motion from the start of a word to the end of it",
847− vec![(1, Range::new(0, 0), Range::new(0, 4))]),
882+ vec![(1, Range::new(0, 0), Range::new(0, 5))]),
848883 ("Basic forward motion from the end of a word to the end of the next",
849− vec![(1, Range::new(0, 4), Range::new(5, 12))]),
884+ vec![(1, Range::new(0, 5), Range::new(5, 13))]),
850885 ("Basic forward motion from the middle of a word to the end of it",
851− vec![(1, Range::new(2, 2), Range::new(2, 4))]),
886+ vec![(1, Range::new(2, 2), Range::new(2, 5))]),
852887 (" Jumping to end of a word preceded by whitespace",
853− vec![(1, Range::new(0, 0), Range::new(0, 10))]),
854− (" Starting from a boundary advances the anchor",
855− vec![(1, Range::new(0, 0), Range::new(1, 8))]),
888+ vec![(1, Range::new(0, 0), Range::new(0, 11))]),
889+
890+ // // Why do we want this behavior? The current behavior fails this
891+ // // test, but seems better and more consistent.
892+ // (" Starting from a boundary advances the anchor",
893+ // vec![(1, Range::new(0, 0), Range::new(1, 9))]),
894+
856895 ("Previous anchor is irrelevant for end of word motion",
857− vec![(1, Range::new(12, 2), Range::new(2, 7))]),
896+ vec![(1, Range::new(12, 2), Range::new(2, 8))]),
858897 ("Identifiers_with_underscores are considered a single word",
859− vec![(1, Range::new(0, 0), Range::new(0, 27))]),
898+ vec![(1, Range::new(0, 0), Range::new(0, 28))]),
860899 ("Jumping\n into starting whitespace selects up to the end of next word",
861− vec![(1, Range::new(0, 6), Range::new(8, 15))]),
900+ vec![(1, Range::new(0, 7), Range::new(8, 16))]),
862901 ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
863902 vec![
864− (1, Range::new(0, 0), Range::new(0, 11)),
865− (1, Range::new(0, 11), Range::new(12, 14)),
866− (1, Range::new(12, 14), Range::new(15, 17))
903+ (1, Range::new(0, 0), Range::new(0, 12)),
904+ (1, Range::new(0, 12), Range::new(12, 15)),
905+ (1, Range::new(12, 15), Range::new(15, 18))
867906 ]),
868907 ("... ... punctuation and spaces behave as expected",
869908 vec![
870− (1, Range::new(0, 0), Range::new(0, 2)),
871− (1, Range::new(0, 2), Range::new(3, 8)),
909+ (1, Range::new(0, 0), Range::new(0, 3)),
910+ (1, Range::new(0, 3), Range::new(3, 9)),
872911 ]),
873912 (".._.._ punctuation is not joined by underscores into a single block",
874− vec![(1, Range::new(0, 0), Range::new(0, 1))]),
913+ vec![(1, Range::new(0, 0), Range::new(0, 2))]),
875914 ("Newlines\n\nare bridged seamlessly.",
876915 vec![
877− (1, Range::new(0, 0), Range::new(0, 7)),
878− (1, Range::new(0, 7), Range::new(10, 12)),
916+ (1, Range::new(0, 0), Range::new(0, 8)),
917+ (1, Range::new(0, 8), Range::new(10, 13)),
879918 ]),
880919 ("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.",
881920 vec![
882− (1, Range::new(0, 8), Range::new(13, 19)),
921+ (1, Range::new(0, 8), Range::new(13, 20)),
883922 ]),
884923 ("A failed motion does not modify the range",
885924 vec![
@@ -887,11 +926,11 @@ mod test {
887926 ]),
888927 ("Multiple motions at once resolve correctly",
889928 vec![
890− (3, Range::new(0, 0), Range::new(16, 18)),
929+ (3, Range::new(0, 0), Range::new(16, 19)),
891930 ]),
892931 ("Excessive motions are performed partially",
893932 vec![
894− (999, Range::new(0, 0), Range::new(31, 40)),
933+ (999, Range::new(0, 0), Range::new(31, 41)),
895934 ]),
896935 ("", // Edge case of moving forward in empty string
897936 vec![
@@ -899,16 +938,16 @@ mod test {
899938 ]),
900939 ("\n\n\n\n\n", // Edge case of moving forward in all newlines
901940 vec![
902− (1, Range::new(0, 0), Range::new(0, 4)),
941+ (1, Range::new(0, 0), Range::new(5, 5)),
903942 ]),
904943 ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
905944 vec![
906− (1, Range::new(0, 0), Range::new(1, 3)),
907− (1, Range::new(1, 3), Range::new(5, 7)),
945+ (1, Range::new(0, 0), Range::new(1, 4)),
946+ (1, Range::new(1, 4), Range::new(5, 8)),
908947 ]),
909948 ("ヒーリクス multibyte characters behave as normal characters",
910949 vec![
911− (1, Range::new(0, 0), Range::new(0, 4)),
950+ (1, Range::new(0, 0), Range::new(0, 5)),
912951 ]),
913952 ]);
914953
@@ -924,44 +963,44 @@ mod test {
924963 fn test_behaviour_when_moving_to_end_of_previous_words() {
925964 let tests = array::IntoIter::new([
926965 ("Basic backward motion from the middle of a word",
927− vec![(1, Range::new(9, 9), Range::new(9, 5))]),
966+ vec![(1, Range::new(9, 9), Range::new(10, 5))]),
928967 ("Starting from after boundary retreats the anchor",
929− vec![(1, Range::new(0, 13), Range::new(12, 8))]),
968+ vec![(1, Range::new(0, 14), Range::new(13, 8))]),
930969 ("Jump to end of a word succeeded by whitespace",
931− vec![(1, Range::new(10, 10), Range::new(10, 4))]),
970+ vec![(1, Range::new(11, 11), Range::new(11, 4))]),
932971 (" Jump to start of line from end of word preceded by whitespace",
933− vec![(1, Range::new(7, 7), Range::new(7, 0))]),
972+ vec![(1, Range::new(8, 8), Range::new(8, 0))]),
934973 ("Previous anchor is irrelevant for backward motions",
935− vec![(1, Range::new(26, 12), Range::new(12, 8))]),
974+ vec![(1, Range::new(26, 12), Range::new(13, 8))]),
936975 (" Starting from whitespace moves to first space in sequence",
937− vec![(1, Range::new(0, 3), Range::new(3, 0))]),
976+ vec![(1, Range::new(0, 4), Range::new(4, 0))]),
938977 ("Test identifiers_with_underscores are considered a single word",
939978 vec![(1, Range::new(0, 25), Range::new(25, 4))]),
940979 ("Jumping\n \nback through a newline selects whitespace",
941− vec![(1, Range::new(0, 13), Range::new(11, 8))]),
980+ vec![(1, Range::new(0, 13), Range::new(12, 8))]),
942981 ("Jumping to start of word from the end selects the whole word",
943− vec![(1, Range::new(15, 15), Range::new(15, 10))]),
982+ vec![(1, Range::new(16, 16), Range::new(16, 10))]),
944983 ("alphanumeric.!,and.?=punctuation are considered 'words' for the purposes of word motion",
945984 vec![
946− (1, Range::new(30, 30), Range::new(30, 21)),
947− (1, Range::new(30, 21), Range::new(20, 18)),
948− (1, Range::new(20, 18), Range::new(17, 15))
985+ (1, Range::new(30, 30), Range::new(31, 21)),
986+ (1, Range::new(31, 21), Range::new(21, 18)),
987+ (1, Range::new(21, 18), Range::new(18, 15))
949988 ]),
950989
951990 ("... ... punctuation and spaces behave as expected",
952991 vec![
953− (1, Range::new(0, 10), Range::new(9, 9)),
954− (1, Range::new(9, 6), Range::new(5, 3)),
992+ (1, Range::new(0, 10), Range::new(9, 3)),
993+ (1, Range::new(9, 3), Range::new(3, 0)),
955994 ]),
956995 (".._.._ punctuation is not joined by underscores into a single block",
957− vec![(1, Range::new(0, 5), Range::new(4, 3))]),
996+ vec![(1, Range::new(0, 5), Range::new(5, 3))]),
958997 ("Newlines\n\nare bridged seamlessly.",
959998 vec![
960− (1, Range::new(0, 10), Range::new(7, 0)),
999+ (1, Range::new(0, 10), Range::new(8, 0)),
9611000 ]),
9621001 ("Jumping \n\n\n\n\nback from within a newline group selects previous block",
9631002 vec![
964− (1, Range::new(0, 13), Range::new(10, 7)),
1003+ (1, Range::new(0, 13), Range::new(11, 7)),
9651004 ]),
9661005 ("Failed motions do not modify the range",
9671006 vec![
@@ -969,11 +1008,11 @@ mod test {
9691008 ]),
9701009 ("Multiple motions at once resolve correctly",
9711010 vec![
972− (3, Range::new(23, 23), Range::new(15, 8)),
1011+ (3, Range::new(24, 24), Range::new(16, 8)),
9731012 ]),
9741013 ("Excessive motions are performed partially",
9751014 vec![
976− (999, Range::new(40, 40), Range::new(8, 0)),
1015+ (999, Range::new(40, 40), Range::new(9, 0)),
9771016 ]),
9781017 ("", // Edge case of moving backwards in empty string
9791018 vec![
@@ -981,16 +1020,16 @@ mod test {
9811020 ]),
9821021 ("\n\n\n\n\n", // Edge case of moving backwards in all newlines
9831022 vec![
984− (1, Range::new(0, 0), Range::new(0, 0)),
1023+ (1, Range::new(5, 5), Range::new(0, 0)),
9851024 ]),
9861025 (" \n \nJumping back through alternated space blocks and newlines selects the space blocks",
9871026 vec![
988− (1, Range::new(0, 7), Range::new(6, 4)),
989− (1, Range::new(6, 4), Range::new(2, 0)),
1027+ (1, Range::new(0, 8), Range::new(7, 4)),
1028+ (1, Range::new(7, 4), Range::new(3, 0)),
9901029 ]),
9911030 ("Test ヒーリクス multibyte characters behave as normal characters",
9921031 vec![
993− (1, Range::new(0, 9), Range::new(9, 4)),
1032+ (1, Range::new(0, 10), Range::new(10, 4)),
9941033 ]),
9951034 ]);
9961035
@@ -1006,40 +1045,44 @@ mod test {
10061045 fn test_behaviour_when_moving_to_end_of_next_long_words() {
10071046 let tests = array::IntoIter::new([
10081047 ("Basic forward motion from the start of a word to the end of it",
1009− vec![(1, Range::new(0, 0), Range::new(0, 4))]),
1048+ vec![(1, Range::new(0, 0), Range::new(0, 5))]),
10101049 ("Basic forward motion from the end of a word to the end of the next",
1011− vec![(1, Range::new(0, 4), Range::new(5, 12))]),
1050+ vec![(1, Range::new(0, 5), Range::new(5, 13))]),
10121051 ("Basic forward motion from the middle of a word to the end of it",
1013− vec![(1, Range::new(2, 2), Range::new(2, 4))]),
1052+ vec![(1, Range::new(2, 2), Range::new(2, 5))]),
10141053 (" Jumping to end of a word preceded by whitespace",
1015− vec![(1, Range::new(0, 0), Range::new(0, 10))]),
1016− (" Starting from a boundary advances the anchor",
1017− vec![(1, Range::new(0, 0), Range::new(1, 8))]),
1054+ vec![(1, Range::new(0, 0), Range::new(0, 11))]),
1055+
1056+ // // Why do we want this behavior? The current behavior fails this
1057+ // // test, but seems better and more consistent.
1058+ // (" Starting from a boundary advances the anchor",
1059+ // vec![(1, Range::new(0, 0), Range::new(1, 9))]),
1060+
10181061 ("Previous anchor is irrelevant for end of word motion",
1019− vec![(1, Range::new(12, 2), Range::new(2, 7))]),
1062+ vec![(1, Range::new(12, 2), Range::new(2, 8))]),
10201063 ("Identifiers_with_underscores are considered a single word",
1021− vec![(1, Range::new(0, 0), Range::new(0, 27))]),
1064+ vec![(1, Range::new(0, 0), Range::new(0, 28))]),
10221065 ("Jumping\n into starting whitespace selects up to the end of next word",
1023− vec![(1, Range::new(0, 6), Range::new(8, 15))]),
1066+ vec![(1, Range::new(0, 7), Range::new(8, 16))]),
10241067 ("alphanumeric.!,and.?=punctuation are treated the same way",
10251068 vec![
1026− (1, Range::new(0, 0), Range::new(0, 31)),
1069+ (1, Range::new(0, 0), Range::new(0, 32)),
10271070 ]),
10281071 ("... ... punctuation and spaces behave as expected",
10291072 vec![
1030− (1, Range::new(0, 0), Range::new(0, 2)),
1031− (1, Range::new(0, 2), Range::new(3, 8)),
1073+ (1, Range::new(0, 0), Range::new(0, 3)),
1074+ (1, Range::new(0, 3), Range::new(3, 9)),
10321075 ]),
10331076 (".._.._ punctuation is joined by underscores into a single block",
1034− vec![(1, Range::new(0, 0), Range::new(0, 5))]),
1077+ vec![(1, Range::new(0, 0), Range::new(0, 6))]),
10351078 ("Newlines\n\nare bridged seamlessly.",
10361079 vec![
1037− (1, Range::new(0, 0), Range::new(0, 7)),
1038− (1, Range::new(0, 7), Range::new(10, 12)),
1080+ (1, Range::new(0, 0), Range::new(0, 8)),
1081+ (1, Range::new(0, 8), Range::new(10, 13)),
10391082 ]),
10401083 ("Jumping\n\n\n\n\n\n from newlines to whitespace selects to end of next word.",
10411084 vec![
1042− (1, Range::new(0, 8), Range::new(13, 19)),
1085+ (1, Range::new(0, 9), Range::new(13, 20)),
10431086 ]),
10441087 ("A failed motion does not modify the range",
10451088 vec![
@@ -1047,11 +1090,11 @@ mod test {
10471090 ]),
10481091 ("Multiple motions at once resolve correctly",
10491092 vec![
1050− (3, Range::new(0, 0), Range::new(16, 18)),
1093+ (3, Range::new(0, 0), Range::new(16, 19)),
10511094 ]),
10521095 ("Excessive motions are performed partially",
10531096 vec![
1054− (999, Range::new(0, 0), Range::new(31, 40)),
1097+ (999, Range::new(0, 0), Range::new(31, 41)),
10551098 ]),
10561099 ("", // Edge case of moving forward in empty string
10571100 vec![
@@ -1059,16 +1102,16 @@ mod test {
10591102 ]),
10601103 ("\n\n\n\n\n", // Edge case of moving forward in all newlines
10611104 vec![
1062− (1, Range::new(0, 0), Range::new(0, 4)),
1105+ (1, Range::new(0, 0), Range::new(5, 5)),
10631106 ]),
10641107 ("\n \n \n Jumping through alternated space blocks and newlines selects the space blocks",
10651108 vec![
1066− (1, Range::new(0, 0), Range::new(1, 3)),
1067− (1, Range::new(1, 3), Range::new(5, 7)),
1109+ (1, Range::new(0, 0), Range::new(1, 4)),
1110+ (1, Range::new(1, 4), Range::new(5, 8)),
10681111 ]),
10691112 ("ヒーリ..クス multibyte characters behave as normal characters, including when they interact with punctuation",
10701113 vec![
1071− (1, Range::new(0, 0), Range::new(0, 6)),
1114+ (1, Range::new(0, 0), Range::new(0, 7)),
10721115 ]),
10731116 ]);
10741117