diff options
| author | tslil <tslil@posteo.de> | 2025-10-17 20:35:53 +0100 |
|---|---|---|
| committer | tslil <tslil@posteo.de> | 2025-10-17 23:48:42 +0100 |
| commit | a775b8f56e5422e47c40927328baf3045c500a32 (patch) | |
| tree | fc003854fb4bc7e3b0cb815034540536a5fd286e /rprt-engine/src/selection.rs | |
| parent | 1376a23bda5e6fe94148cb57ca1447907c6bb21e (diff) | |
Starting on RIIR, first steps have been whelming
Diffstat (limited to 'rprt-engine/src/selection.rs')
| -rw-r--r-- | rprt-engine/src/selection.rs | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/rprt-engine/src/selection.rs b/rprt-engine/src/selection.rs new file mode 100644 index 0000000..9494544 --- /dev/null +++ b/rprt-engine/src/selection.rs @@ -0,0 +1,288 @@ +use crate::buffer::BufferID; +use either::Either; +use std::collections::HashMap; +use std::collections::HashSet; +use std::fmt::Display; + +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] +pub enum Rank { + Zero, + One, + Two, + Three, +} + +impl Display for Rank { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Rank::Zero => write!(f, "Rank 0"), + Rank::One => write!(f, "Rank 1"), + Rank::Two => write!(f, "Rank 2"), + Rank::Three => write!(f, "Rank 3"), + } + } +} + +pub enum SelectionError { + InvalidPromotion { from: Rank, to: Rank }, +} + +impl std::fmt::Display for SelectionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SelectionError::InvalidPromotion { from, to } => { + write!(f, "Cannot change selection of {} to {}", from, to) + } + } + } +} + +#[derive(Debug, Clone)] +pub struct Interval { + pub start: usize, + pub end: usize, + capture_groups: Vec<(usize, usize)>, +} + +impl Interval { + pub fn is_disjoint(&self, other: &Interval) -> bool { + self.start >= other.end || self.end <= other.start + } +} + +#[derive(Debug, Clone)] +pub enum Selection { + Position { + pos: usize, + buffer: BufferID, + }, + Range { + buffer: BufferID, + interval: Interval, + }, + Ranges { + buffer: BufferID, + ranges: Vec<Interval>, + }, + MultiRanges { + multi_ranges: HashMap<BufferID, Vec<Interval>>, + }, +} + +impl Selection { + pub fn empty() -> Self { + Self::MultiRanges { + multi_ranges: HashMap::new(), + } + } + + pub fn ranges_disjoint(ranges: &[Interval]) -> bool { + if ranges.len() < 1 { + return true; + } + + let mut sorted_by_start: Vec<&Interval> = ranges.iter().collect::<Vec<_>>(); + sorted_by_start.sort_by_key(|i| i.start); + sorted_by_start.windows(2).all(|w| w[0].is_disjoint(&w[1])) + } + + pub fn is_disjoint(&self) -> bool { + match self { + Self::Position { .. } => true, + Self::Range { .. } => true, + Self::Ranges { ranges, .. } => Self::ranges_disjoint(ranges), + Self::MultiRanges { multi_ranges } => { + for (_, intervals) in multi_ranges { + if !Self::ranges_disjoint(intervals) { + return false; + } + } + true + } + } + } + + pub fn rank(&self) -> Rank { + match self { + Self::Position { .. } => Rank::Zero, + Self::Range { .. } => Rank::One, + Self::Ranges { .. } => Rank::Two, + Self::MultiRanges { .. } => Rank::Three, + } + } + + pub fn promote(&self, to_rank: Rank) -> Result<Self, SelectionError> { + use Rank::*; + + match (self, to_rank) { + (Self::Position { .. }, Zero) => Ok(self.clone()), + (Self::Range { .. }, One) => Ok(self.clone()), + (Self::Ranges { .. }, Two) => Ok(self.clone()), + (Self::MultiRanges { .. }, Three) => Ok(self.clone()), + + // Position + (Self::Position { pos, buffer }, One) => Ok(Self::Range { + interval: Interval { + start: pos + 0, + end: pos + 1, + capture_groups: Vec::new(), + }, + buffer: *buffer, + }), + (Self::Position { pos, buffer }, Two) => Ok(Self::Ranges { + ranges: vec![Interval { + start: pos + 0, + end: pos + 1, + capture_groups: Vec::new(), + }], + buffer: *buffer, + }), + (Self::Position { pos, buffer }, Three) => { + let mut map = HashMap::new(); + map.insert( + *buffer, + vec![Interval { + start: pos + 0, + end: pos + 1, + capture_groups: Vec::new(), + }], + ); + Ok(Self::MultiRanges { multi_ranges: map }) + } + + // Range promotions + (Self::Range { interval, buffer }, Two) => Ok(Self::Ranges { + ranges: vec![interval.clone()], + buffer: *buffer, + }), + (Self::Range { interval, buffer }, Three) => { + let mut map = HashMap::new(); + map.insert(*buffer, vec![interval.clone()]); + Ok(Self::MultiRanges { multi_ranges: map }) + } + + // Ranges promotion + (Self::Ranges { ranges, buffer }, Three) => { + let mut map = HashMap::new(); + map.insert(*buffer, ranges.clone()); + Ok(Self::MultiRanges { multi_ranges: map }) + } + + // all other cases + (sel, target) => Err(SelectionError::InvalidPromotion { + from: sel.rank(), + to: target, + }), + } + } + + pub fn buffers(&self) -> Vec<BufferID> { + match self { + Self::Position { buffer, .. } => vec![*buffer], + Self::Range { buffer, .. } => vec![*buffer], + Self::Ranges { buffer, .. } => vec![*buffer], + Self::MultiRanges { multi_ranges } => multi_ranges.keys().copied().collect(), + } + } + + pub fn union(selections: Vec<Self>) -> Result<Self, SelectionError> { + if selections.is_empty() { + return Ok(Self::empty()); + } + + let all_buffers: HashSet<BufferID> = selections.iter().flat_map(|s| s.buffers()).collect(); + + if all_buffers.is_empty() { + return Ok(Self::empty()); + } + + let max_rank = if all_buffers.len() > 1 { + Rank::Three + } else { + selections + .iter() + .map(|s| s.rank()) + .max() + .unwrap_or(Rank::Two) + .max(Rank::Two) // At least rank 2 + }; + + let promoted: Result<Vec<Self>, SelectionError> = + selections.iter().map(|s| s.promote(max_rank)).collect(); + let promoted = promoted?; + + match max_rank { + Rank::Two => { + let mut all_intervals = Vec::new(); + let buffer = *all_buffers.iter().next().unwrap(); + + for sel in &promoted { + match sel { + Self::Ranges { ranges, .. } => all_intervals.extend(ranges.clone()), + _ => unreachable!("promote() to Rank::Two should always produce Ranges"), + } + } + + Ok(Self::Ranges { + ranges: all_intervals, + buffer, + }) + } + Rank::Three => { + let mut buffer_intervals: HashMap<BufferID, Vec<Interval>> = + all_buffers.iter().map(|&b| (b, Vec::new())).collect(); + + for sel in &promoted { + match sel { + Self::MultiRanges { multi_ranges } => { + for (&buffer, intervals) in multi_ranges { + buffer_intervals + .entry(buffer) + .or_insert_with(Vec::new) + .extend(intervals.clone()); + } + } + _ => unreachable!( + "promote() to Rank::Three should always produce MultiRanges" + ), + } + } + + Ok(Self::MultiRanges { + multi_ranges: buffer_intervals, + }) + } + _ => unreachable!(), + } + } + + pub fn broadcast<E>( + &self, + fn_rank_zero: &impl Fn(&BufferID, &usize) -> Result<Self, E>, + fn_rank_one: &impl Fn(&BufferID, &Interval) -> Result<Self, E>, + ) -> Result<Self, Either<E, SelectionError>> { + let do_rank_one = |b: &usize, rs: &Vec<Interval>| { + rs.iter() + .map(|int| fn_rank_one(b, int).map_err(Either::Left)) + .collect::<Result<_, _>>() + }; + + match self { + Self::Position { buffer, pos } => fn_rank_zero(buffer, pos).map_err(Either::Left), + Self::Range { buffer, interval } => fn_rank_one(buffer, interval).map_err(Either::Left), + Self::Ranges { buffer, ranges } => { + let results = do_rank_one(buffer, ranges)?; + Self::union(results).map_err(Either::Right) + } + Self::MultiRanges { multi_ranges } => { + let all_ok: Vec<Vec<Self>> = multi_ranges + .iter() + .map(|(buffer, ranges)| do_rank_one(buffer, ranges)) + .collect::<Result<_, _>>()?; + // It would seem that Rust has no built in monadic flatten, or in general cannot lift things to operate on Result... :( + let results: Vec<Self> = all_ok.into_iter().flatten().collect(); + Self::union(results).map_err(Either::Right) + } + } + } +} |
