diff options
Diffstat (limited to 'rprt-engine/src/selection.rs')
| -rw-r--r-- | rprt-engine/src/selection.rs | 138 |
1 files changed, 68 insertions, 70 deletions
diff --git a/rprt-engine/src/selection.rs b/rprt-engine/src/selection.rs index a3259b8..45a895b 100644 --- a/rprt-engine/src/selection.rs +++ b/rprt-engine/src/selection.rs @@ -1,4 +1,5 @@ use crate::buffer::BufferID; +use crate::state::EditorState; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Display; @@ -69,26 +70,55 @@ pub enum Selection { }, } -// rust is ... sigh. I have to make a wrapper class and worse still _other -// people_ have to deal with my wrapper class! -#[derive(Error, Debug)] -pub enum VectoriseError<E> -where - E: std::error::Error + 'static, -{ - #[error("An error occurred during processing: {0}")] - ProcessingError(E), - - #[error("A selection error occurred: {0}")] - SelectionError(SelectionError), -} +/// A type that knows how to act on the rank-0 (scalar) and rank-1 (interval) +/// elements of a [`Selection`], and so can be vectorised over one. +pub trait Vectorisable<P, E> { + /// Action on a rank-0 element. + fn rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result<Selection, E>; -impl<E> From<E> for VectoriseError<E> -where - E: std::error::Error + 'static, -{ - fn from(err: E) -> Self { - VectoriseError::ProcessingError(err) + /// Action on a rank-1 element. + fn rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result<Selection, E>; + + /// Vectorise `sel`: apply `rank0`/`rank1` to every element and union + /// the results. + fn vectorise(&self, param: &P, es: &EditorState, sel: &Selection) -> Result<Selection, E> { + // Single rank-0/rank-1 elements pass through as-is (no rank + // promotion); multi-element selections are unioned. + match sel { + Selection::Scalar { buffer_id, pos } => Self::rank0(self, param, es, *buffer_id, *pos), + Selection::Vector { + buffer_id, + interval, + } => Self::rank1(self, param, es, *buffer_id, interval), + Selection::Vectors { buffer_id, ranges } => { + let mut selections = Vec::with_capacity(ranges.len()); + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + Ok(Selection::union(selections)) + } + Selection::MultiVectors { multi_ranges } => { + let mut selections = Vec::new(); + for (buffer_id, ranges) in multi_ranges { + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + } + Ok(Selection::union(selections)) + } + } } } @@ -210,15 +240,20 @@ impl Selection { } } - pub fn union(selections: Vec<Self>) -> Result<Self, SelectionError> { + /// Union the given selections into a single selection. + /// + /// The target rank is the maximum rank needed to hold all inputs (rank 3 + /// if they span multiple buffers), to which every input can be promoted, + /// so this operation cannot fail. + pub fn union(selections: Vec<Self>) -> Self { if selections.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let all_buffers: HashSet<BufferID> = selections.iter().flat_map(|s| s.buffers()).collect(); if all_buffers.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let max_rank = if all_buffers.len() > 1 { @@ -232,9 +267,13 @@ impl Selection { .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?; + let promoted: Vec<Self> = selections + .iter() + .map(|s| { + s.promote(max_rank) + .expect("union promotes to the max rank present, which cannot fail") + }) + .collect(); match max_rank { Rank::Two => { @@ -248,10 +287,10 @@ impl Selection { } } - Ok(Self::Vectors { + Self::Vectors { ranges: all_intervals, buffer_id, - }) + } } Rank::Three => { let mut buffer_intervals: HashMap<BufferID, Vec<Interval>> = @@ -273,52 +312,11 @@ impl Selection { } } - Ok(Self::MultiVectors { + Self::MultiVectors { multi_ranges: buffer_intervals, - }) + } } _ => unreachable!(), } } - - pub fn vectorise<S, E>( - &self, - state: &S, - fn_rank_zero: impl Fn(&S, BufferID, usize) -> Result<Selection, E> + 'static, - fn_rank_one: impl Fn(&S, BufferID, &Interval) -> Result<Selection, E> + 'static, - ) -> Result<Selection, VectoriseError<E>> - where - E: std::error::Error + 'static, - { - // look at this mess! - use VectoriseError::{ProcessingError, SelectionError}; - let do_rank_one = |b: usize, rs: &Vec<Interval>| { - rs.iter() - .map(|int| fn_rank_one(state, b, int).map_err(ProcessingError)) - .collect::<Result<_, _>>() - }; - - match self { - &Self::Scalar { buffer_id, pos } => { - fn_rank_zero(state, buffer_id, pos).map_err(ProcessingError) - } - Self::Vector { - interval, - buffer_id, - } => fn_rank_one(state, *buffer_id, interval).map_err(VectoriseError::ProcessingError), - Self::Vectors { buffer_id, ranges } => { - let results = do_rank_one(*buffer_id, ranges)?; - Self::union(results).map_err(SelectionError) - } - Self::MultiVectors { multi_ranges } => { - let all_ok: Vec<Vec<Self>> = multi_ranges - .iter() - .map(|(buffer_id, ranges)| do_rank_one(*buffer_id, 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(SelectionError) - } - } - } } |
