on master help sign in
Switch to a cleaner range-head moving abstraction.

Also fix a bunch of bugs related to it.

commitf96b8b769b3c7457935b5c02db870af97036f7b6 authorNathan Vegdahl <cessen@cessen.com> dateSat 24 Jul 2021, 07:44 (5 years ago) parent2072349
5 files changed+111−84
helix-core/src/movement.rs+17−18 helix-core/src/search.rs+3−23 helix-core/src/selection.rs+12−8 helix-core/src/surround.rs+6−7 helix-term/src/commands.rs+73−28
helix-core/src/movement.rs+17−18
@@ -42,20 +42,18 @@ pub fn move_horizontally(
4242 };
4343
4444 // Compute the new position.
45− let mut new_pos = if dir == Direction::Backward {
45+ let new_pos = if dir == Direction::Backward {
4646 nth_prev_grapheme_boundary(slice, pos, count)
4747 } else {
4848 nth_next_grapheme_boundary(slice, pos, count)
4949 };
5050
51− // Shift forward one grapheme if needed, for the
52− // visual 1-width cursor.
53− if behaviour == Extend && new_pos >= range.anchor {
54− new_pos = next_grapheme_boundary(slice, new_pos);
55− };
56−
5751 // Compute the final new range.
58− range.put(slice, new_pos, behaviour == Extend)
52+ if behaviour == Extend {
53+ range.move_head(slice, new_pos, true)
54+ } else {
55+ Range::point(new_pos)
56+ }
5957}
6058
6159pub fn move_vertically(
@@ -78,14 +76,17 @@ pub fn move_vertically(
7876 let horiz = range.horiz.unwrap_or(col as u32);
7977
8078 // Compute the new position.
81− let new_pos = {
79+ let (new_pos, new_row) = {
8280 let new_row = if dir == Direction::Backward {
8381 row.saturating_sub(count)
8482 } else {
8583 (row + count).min(slice.len_lines().saturating_sub(1))
8684 };
8785 let new_col = col.max(horiz as usize);
88− pos_at_coords(slice, Position::new(new_row, new_col), true)
86+ (
87+ pos_at_coords(slice, Position::new(new_row, new_col), true),
88+ new_row,
89+ )
8990 };
9091
9192 // Compute the new range according to the type of movement.
@@ -97,15 +98,13 @@ pub fn move_vertically(
9798 },
9899
99100 Movement::Extend => {
100− let new_head = if new_pos >= range.anchor {
101− next_grapheme_boundary(slice, new_pos)
101+ if slice.line(new_row).len_chars() > 0 {
102+ let mut new_range = range.move_head(slice, new_pos, true);
103+ new_range.horiz = Some(horiz);
104+ new_range
102105 } else {
103− new_pos
104− };
105−
106− let mut new_range = range.put(slice, new_head, true);
107− new_range.horiz = Some(horiz);
108− new_range
106+ range
107+ }
109108 }
110109 }
111110}
helix-core/src/search.rs+3−23
@@ -1,12 +1,6 @@
11use crate::RopeSlice;
22
3−pub fn find_nth_next(
4− text: RopeSlice,
5− ch: char,
6− mut pos: usize,
7− n: usize,
8− inclusive: bool,
9−) -> Option<usize> {
3+pub fn find_nth_next(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
104 if pos >= text.len_chars() || n == 0 {
115 return None;
126 }
@@ -25,20 +19,10 @@ pub fn find_nth_next(
2519 }
2620 }
2721
28− if !inclusive {
29− pos -= 1;
30− }
31−
32− Some(pos)
22+ Some(pos - 1)
3323}
3424
35−pub fn find_nth_prev(
36− text: RopeSlice,
37− ch: char,
38− mut pos: usize,
39− n: usize,
40− inclusive: bool,
41−) -> Option<usize> {
25+pub fn find_nth_prev(text: RopeSlice, ch: char, mut pos: usize, n: usize) -> Option<usize> {
4226 if pos == 0 || n == 0 {
4327 return None;
4428 }
@@ -57,9 +41,5 @@ pub fn find_nth_prev(
5741 }
5842 }
5943
60− if !inclusive {
61− pos += 1;
62− }
63−
6444 Some(pos)
6545}
helix-core/src/selection.rs+12−8
@@ -248,18 +248,18 @@ impl Range {
248248 }
249249 }
250250
251− /// Moves the `Range` to `char_idx`. If `extend == true`, then only the head
252− /// is moved to `char_idx`, and the anchor is adjusted only as needed to
253− /// preserve 1-width range semantics.
251+ /// Moves the head of the `Range` to `char_idx`, adjusting the anchor
252+ /// as needed to preserve 1-width range semantics.
253+ ///
254+ /// `block_cursor` specifies whether it should treat `char_idx` as a block
255+ /// cursor position or as a range-end position.
254256 ///
255257 /// This method assumes that the range and `char_idx` are already properly
256258 /// grapheme-aligned.
257259 #[must_use]
258260 #[inline]
259− pub fn put(self, text: RopeSlice, char_idx: usize, extend: bool) -> Range {
260− let anchor = if !extend {
261− char_idx
262− } else if self.head >= self.anchor && char_idx < self.anchor {
261+ pub fn move_head(self, text: RopeSlice, char_idx: usize, block_cursor: bool) -> Range {
262+ let anchor = if self.head >= self.anchor && char_idx < self.anchor {
263263 next_grapheme_boundary(text, self.anchor)
264264 } else if self.head < self.anchor && char_idx >= self.anchor {
265265 prev_grapheme_boundary(text, self.anchor)
@@ -267,7 +267,11 @@ impl Range {
267267 self.anchor
268268 };
269269
270− Range::new(anchor, char_idx)
270+ if block_cursor && anchor <= char_idx {
271+ Range::new(anchor, next_grapheme_boundary(text, char_idx))
272+ } else {
273+ Range::new(anchor, char_idx)
274+ }
271275 }
272276
273277 // groupAt
helix-core/src/surround.rs+6−7
@@ -49,19 +49,18 @@ pub fn find_nth_pairs_pos(
4949 if Some(open) == text.get_char(pos) {
5050 // Special case: cursor is directly on a matching char.
5151 match pos {
52− 0 => Some((pos, search::find_nth_next(text, close, pos + 1, n, true)?)),
53− _ if (pos + 1) == text.len_chars() => Some((
54− search::find_nth_prev(text, open, pos, n, true)?,
55− text.len_chars(),
56− )),
52+ 0 => Some((pos, search::find_nth_next(text, close, pos + 1, n)? + 1)),
53+ _ if (pos + 1) == text.len_chars() => {
54+ Some((search::find_nth_prev(text, open, pos, n)?, text.len_chars()))
55+ }
5756 // We return no match because there's no way to know which
5857 // side of the char we should be searching on.
5958 _ => None,
6059 }
6160 } else {
6261 Some((
63− search::find_nth_prev(text, open, pos, n, true)?,
64− search::find_nth_next(text, close, pos, n, true)?,
62+ search::find_nth_prev(text, open, pos, n)?,
63+ search::find_nth_next(text, close, pos, n)? + 1,
6564 ))
6665 }
6766 } else {
helix-term/src/commands.rs+73−28
@@ -373,15 +373,16 @@ fn goto_line_end(cx: &mut Context) {
373373
374374 let selection = doc.selection(view.id).clone().transform(|range| {
375375 let line = range.head_line(text);
376+ let line_start = text.line_to_char(line);
376377
377− let mut pos = line_end_char_index(&text, line);
378− if doc.mode != Mode::Select {
379− pos = graphemes::prev_grapheme_boundary(text, pos);
380− }
381−
382− pos = range.head.max(pos).max(text.line_to_char(line));
378+ let pos = graphemes::prev_grapheme_boundary(text, line_end_char_index(&text, line))
379+ .max(line_start);
383380
384− range.put(text, pos, doc.mode == Mode::Select)
381+ if doc.mode == Mode::Select {
382+ range.move_head(text, pos, true)
383+ } else {
384+ Range::point(pos)
385+ }
385386 });
386387 doc.set_selection(view.id, selection);
387388}
@@ -392,12 +393,13 @@ fn goto_line_end_newline(cx: &mut Context) {
392393
393394 let selection = doc.selection(view.id).clone().transform(|range| {
394395 let line = range.head_line(text);
396+ let pos = line_end_char_index(&text, line);
395397
396− let mut pos = text.line_to_char((line + 1).min(text.len_lines()));
397− if doc.mode != Mode::Select {
398− pos = graphemes::prev_grapheme_boundary(text, pos);
398+ if doc.mode == Mode::Select {
399+ range.move_head(text, pos, true)
400+ } else {
401+ Range::point(pos)
399402 }
400− range.put(text, pos, doc.mode == Mode::Select)
401403 });
402404 doc.set_selection(view.id, selection);
403405}
@@ -411,7 +413,11 @@ fn goto_line_start(cx: &mut Context) {
411413
412414 // adjust to start of the line
413415 let pos = text.line_to_char(line);
414− range.put(text, pos, doc.mode == Mode::Select)
416+ if doc.mode == Mode::Select {
417+ range.move_head(text, pos, true)
418+ } else {
419+ Range::point(pos)
420+ }
415421 });
416422 doc.set_selection(view.id, selection);
417423}
@@ -425,7 +431,11 @@ fn goto_first_nonwhitespace(cx: &mut Context) {
425431
426432 if let Some(pos) = find_first_non_whitespace_char(text.line(line)) {
427433 let pos = pos + text.line_to_char(line);
428− range.put(text, pos, doc.mode == Mode::Select)
434+ if doc.mode == Mode::Select {
435+ range.move_head(text, pos, true)
436+ } else {
437+ Range::point(pos)
438+ }
429439 } else {
430440 range
431441 }
@@ -569,8 +579,8 @@ fn extend_next_word_start(cx: &mut Context) {
569579 .min_width_1(text)
570580 .transform(|range| {
571581 let word = movement::move_next_word_start(text, range, count);
572− let pos = word.head;
573− range.put(text, pos, true)
582+ let pos = graphemes::prev_grapheme_boundary(text, word.head);
583+ range.move_head(text, pos, true)
574584 });
575585 doc.set_selection(view.id, selection);
576586}
@@ -587,7 +597,7 @@ fn extend_prev_word_start(cx: &mut Context) {
587597 .transform(|range| {
588598 let word = movement::move_prev_word_start(text, range, count);
589599 let pos = word.head;
590− range.put(text, pos, true)
600+ range.move_head(text, pos, true)
591601 });
592602 doc.set_selection(view.id, selection);
593603}
@@ -603,8 +613,8 @@ fn extend_next_word_end(cx: &mut Context) {
603613 .min_width_1(text)
604614 .transform(|range| {
605615 let word = movement::move_next_word_end(text, range, count);
606− let pos = word.head;
607− range.put(text, pos, true)
616+ let pos = graphemes::prev_grapheme_boundary(text, word.head);
617+ range.move_head(text, pos, true)
608618 });
609619 doc.set_selection(view.id, selection);
610620}
@@ -652,11 +662,17 @@ where
652662 let text = doc.text().slice(..);
653663
654664 let selection = doc.selection(view.id).clone().transform(|range| {
665+ let range = if range.anchor < range.head {
666+ // For 1-width cursor semantics.
667+ Range::new(range.anchor, range.head - 1)
668+ } else {
669+ range
670+ };
655671 search_fn(text, ch, range.head, count, inclusive).map_or(range, |pos| {
656672 if extend {
657− range.put(text, pos, true)
673+ range.move_head(text, pos, true)
658674 } else {
659− range.put(text, pos.saturating_sub(1), false)
675+ Range::point(pos)
660676 }
661677 })
662678 });
@@ -664,10 +680,39 @@ where
664680 })
665681}
666682
683+fn find_next_char_impl(
684+ text: RopeSlice,
685+ ch: char,
686+ pos: usize,
687+ n: usize,
688+ inclusive: bool,
689+) -> Option<usize> {
690+ let pos = (pos + 1).min(text.len_chars());
691+ if inclusive {
692+ search::find_nth_next(text, ch, pos, n)
693+ } else {
694+ search::find_nth_next(text, ch, pos, n).map(|n| n.saturating_sub(1))
695+ }
696+}
697+
698+fn find_prev_char_impl(
699+ text: RopeSlice,
700+ ch: char,
701+ pos: usize,
702+ n: usize,
703+ inclusive: bool,
704+) -> Option<usize> {
705+ if inclusive {
706+ search::find_nth_prev(text, ch, pos, n)
707+ } else {
708+ search::find_nth_prev(text, ch, pos, n).map(|n| (n + 1).min(text.len_chars()))
709+ }
710+}
711+
667712fn find_till_char(cx: &mut Context) {
668713 find_char_impl(
669714 cx,
670− search::find_nth_next,
715+ find_next_char_impl,
671716 false, /* inclusive */
672717 false, /* extend */
673718 )
@@ -676,7 +721,7 @@ fn find_till_char(cx: &mut Context) {
676721fn find_next_char(cx: &mut Context) {
677722 find_char_impl(
678723 cx,
679− search::find_nth_next,
724+ find_next_char_impl,
680725 true, /* inclusive */
681726 false, /* extend */
682727 )
@@ -685,7 +730,7 @@ fn find_next_char(cx: &mut Context) {
685730fn extend_till_char(cx: &mut Context) {
686731 find_char_impl(
687732 cx,
688− search::find_nth_next,
733+ find_next_char_impl,
689734 false, /* inclusive */
690735 true, /* extend */
691736 )
@@ -694,7 +739,7 @@ fn extend_till_char(cx: &mut Context) {
694739fn extend_next_char(cx: &mut Context) {
695740 find_char_impl(
696741 cx,
697− search::find_nth_next,
742+ find_next_char_impl,
698743 true, /* inclusive */
699744 true, /* extend */
700745 )
@@ -703,7 +748,7 @@ fn extend_next_char(cx: &mut Context) {
703748fn till_prev_char(cx: &mut Context) {
704749 find_char_impl(
705750 cx,
706− search::find_nth_prev,
751+ find_prev_char_impl,
707752 false, /* inclusive */
708753 false, /* extend */
709754 )
@@ -712,7 +757,7 @@ fn till_prev_char(cx: &mut Context) {
712757fn find_prev_char(cx: &mut Context) {
713758 find_char_impl(
714759 cx,
715− search::find_nth_prev,
760+ find_prev_char_impl,
716761 true, /* inclusive */
717762 false, /* extend */
718763 )
@@ -721,7 +766,7 @@ fn find_prev_char(cx: &mut Context) {
721766fn extend_till_prev_char(cx: &mut Context) {
722767 find_char_impl(
723768 cx,
724− search::find_nth_prev,
769+ find_prev_char_impl,
725770 false, /* inclusive */
726771 true, /* extend */
727772 )
@@ -730,7 +775,7 @@ fn extend_till_prev_char(cx: &mut Context) {
730775fn extend_prev_char(cx: &mut Context) {
731776 find_char_impl(
732777 cx,
733− search::find_nth_prev,
778+ find_prev_char_impl,
734779 true, /* inclusive */
735780 true, /* extend */
736781 )