From 78189dd9c1aecd1760cc1baf6e2e81d8abbca48c Mon Sep 17 00:00:00 2001 From: A-Walrus <58790821+A-Walrus@users.noreply.github.com> Date: Wed, 31 Aug 2022 04:42:58 +0300 Subject: [PATCH] Fix extra selection with regex anchors (^,$) (#3598) Also added a bunch of tests to ensure correct behaviour --- helix-core/src/selection.rs | 78 ++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 59bd736e6..3463c1d3f 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -659,7 +659,13 @@ pub fn select_on_matches( let start = text.byte_to_char(start_byte + mat.start()); let end = text.byte_to_char(start_byte + mat.end()); - result.push(Range::new(start, end)); + + let range = Range::new(start, end); + // Make sure the match is not right outside of the selection. + // These invalid matches can come from using RegEx anchors like `^`, `$` + if range != Range::point(sel.to()) { + result.push(range); + } } } @@ -929,6 +935,76 @@ mod test { assert_eq!(Range::new(6, 5).min_width_1(s), Range::new(6, 5)); } + #[test] + fn test_select_on_matches() { + use crate::regex::{Regex, RegexBuilder}; + + let r = Rope::from_str("Nobody expects the Spanish inquisition"); + let s = r.slice(..); + + let selection = Selection::single(0, r.len_chars()); + assert_eq!( + select_on_matches(s, &selection, &Regex::new(r"[A-Z][a-z]*").unwrap()), + Some(Selection::new( + smallvec![Range::new(0, 6), Range::new(19, 26)], + 0 + )) + ); + + let r = Rope::from_str("This\nString\n\ncontains multiple\nlines"); + let s = r.slice(..); + + let start_of_line = RegexBuilder::new(r"^").multi_line(true).build().unwrap(); + let end_of_line = RegexBuilder::new(r"$").multi_line(true).build().unwrap(); + + // line without ending + assert_eq!( + select_on_matches(s, &Selection::single(0, 4), &start_of_line), + Some(Selection::single(0, 0)) + ); + assert_eq!( + select_on_matches(s, &Selection::single(0, 4), &end_of_line), + None + ); + // line with ending + assert_eq!( + select_on_matches(s, &Selection::single(0, 5), &start_of_line), + Some(Selection::single(0, 0)) + ); + assert_eq!( + select_on_matches(s, &Selection::single(0, 5), &end_of_line), + Some(Selection::single(4, 4)) + ); + // line with start of next line + assert_eq!( + select_on_matches(s, &Selection::single(0, 6), &start_of_line), + Some(Selection::new( + smallvec![Range::point(0), Range::point(5)], + 0 + )) + ); + assert_eq!( + select_on_matches(s, &Selection::single(0, 6), &end_of_line), + Some(Selection::single(4, 4)) + ); + + // multiple lines + assert_eq!( + select_on_matches( + s, + &Selection::single(0, s.len_chars()), + &RegexBuilder::new(r"^[a-z ]*$") + .multi_line(true) + .build() + .unwrap() + ), + Some(Selection::new( + smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)], + 0 + )) + ); + } + #[test] fn test_line_range() { let r = Rope::from_str("\r\nHi\r\nthere!");