aboutsummaryrefslogtreecommitdiff
path: root/rprt-engine/src/selection.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rprt-engine/src/selection.rs')
-rw-r--r--rprt-engine/src/selection.rs138
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)
- }
- }
- }
}