aboutsummaryrefslogtreecommitdiff
path: root/helix-core/src/selection.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core/src/selection.rs')
-rw-r--r--helix-core/src/selection.rs76
1 files changed, 74 insertions, 2 deletions
diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs
index f934f74d..2251c77f 100644
--- a/helix-core/src/selection.rs
+++ b/helix-core/src/selection.rs
@@ -2,8 +2,9 @@
//! single selection range.
//!
//! All positioning is done via `char` offsets into the buffer.
-use crate::{Assoc, ChangeSet};
+use crate::{Assoc, ChangeSet, Rope, RopeSlice};
use smallvec::{smallvec, SmallVec};
+use std::borrow::Cow;
#[inline]
fn abs_difference(x: usize, y: usize) -> usize {
@@ -22,7 +23,7 @@ pub struct Range {
pub anchor: usize,
/// The head of the range, moved when extending.
pub head: usize,
-}
+} // TODO: might be cheaper to store normalized as from/to and an inverted flag
impl Range {
pub fn new(anchor: usize, head: usize) -> Self {
@@ -106,6 +107,11 @@ impl Range {
}
// groupAt
+
+ #[inline]
+ pub fn fragment<'a>(&'a self, text: &'a RopeSlice) -> Cow<'a, str> {
+ Cow::from(text.slice(self.from()..self.to()))
+ }
}
/// A selection consists of one or more selection ranges.
@@ -239,10 +245,50 @@ impl Selection {
self.primary_index,
)
}
+
+ pub fn fragments<'a>(&'a self, text: &'a RopeSlice) -> impl Iterator<Item = Cow<str>> + 'a {
+ self.ranges.iter().map(move |range| range.fragment(text))
+ }
}
// TODO: checkSelection -> check if valid for doc length
+// TODO: support to split on capture #N instead of whole match
+pub fn split_on_matches(
+ text: &RopeSlice,
+ selections: &Selection,
+ regex: &crate::regex::Regex,
+) -> Selection {
+ let mut result = SmallVec::with_capacity(selections.ranges().len());
+
+ for sel in selections.ranges() {
+ // TODO: can't avoid occasional allocations since Regex can't operate on chunks yet
+ let fragment = sel.fragment(&text);
+
+ let mut sel_start = sel.from();
+ let sel_end = sel.to();
+
+ let mut start_byte = text.char_to_byte(sel_start);
+
+ let mut start = sel_start;
+
+ for mat in regex.find_iter(&fragment) {
+ // TODO: retain range direction
+
+ let end = text.byte_to_char(start_byte + mat.start());
+ result.push(Range::new(start, end - 1));
+ start = text.byte_to_char(start_byte + mat.end());
+ }
+
+ if start <= sel_end {
+ result.push(Range::new(start, sel_end));
+ }
+ }
+
+ // TODO: figure out a new primary index
+ Selection::new(result, 0)
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -312,4 +358,30 @@ mod test {
assert_eq!(range.contains(6), false);
}
+ #[test]
+ fn test_split_on_matches() {
+ use crate::regex::Regex;
+
+ let text = Rope::from("abcd efg wrs xyz 123 456");
+
+ let selections = Selection::new(smallvec![Range::new(0, 8), Range::new(10, 19),], 0);
+
+ let result = split_on_matches(&text.slice(..), &selections, &Regex::new(r"\s+").unwrap());
+
+ assert_eq!(
+ result.ranges(),
+ &[
+ Range::new(0, 4),
+ Range::new(5, 8),
+ Range::new(10, 12),
+ Range::new(15, 18),
+ Range::new(19, 19),
+ ]
+ );
+
+ assert_eq!(
+ result.fragments(&text.slice(..)).collect::<Vec<_>>(),
+ &["abcd", "efg", "rs", "xyz", ""]
+ );
+ }
}