aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA-Walrus2022-08-31 01:42:58 +0000
committerGitHub2022-08-31 01:42:58 +0000
commit78189dd9c1aecd1760cc1baf6e2e81d8abbca48c (patch)
treef280ba73ef3ab2bd0482b898497997823c8c5009
parent9e24f2aa81f6bbb1fecbf6c7a14a56742389444f (diff)
Fix extra selection with regex anchors (^,$) (#3598)
Also added a bunch of tests to ensure correct behaviour
-rw-r--r--helix-core/src/selection.rs78
1 files changed, 77 insertions, 1 deletions
diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs
index 59bd736e..3463c1d3 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);
+ }
}
}
@@ -930,6 +936,76 @@ mod test {
}
#[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!");
let s = r.slice(..);