Initial visual mode

This commit is contained in:
Keith Simmons 2022-05-03 10:29:57 -07:00
parent 5e2e859413
commit 37c921f972
13 changed files with 621 additions and 113 deletions

View file

@ -1,19 +1,62 @@
pub enum SetEqError<T> {
LeftMissing(T),
RightMissing(T),
}
impl<T> SetEqError<T> {
pub fn map<R, F: FnOnce(T) -> R>(self, update: F) -> SetEqError<R> {
match self {
SetEqError::LeftMissing(missing) => SetEqError::LeftMissing(update(missing)),
SetEqError::RightMissing(missing) => SetEqError::RightMissing(update(missing)),
}
}
}
#[macro_export]
macro_rules! assert_set_eq {
macro_rules! set_eq {
($left:expr,$right:expr) => {{
use util::test::*;
let left = $left;
let right = $right;
for left_value in left.iter() {
if !right.contains(left_value) {
panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nright does not contain {:?}", left, right, left_value);
let mut result = Ok(());
for right_value in right.iter() {
if !left.contains(right_value) {
result = Err(SetEqError::LeftMissing(right_value.clone()));
break;
}
}
for right_value in right.iter() {
if !left.contains(right_value) {
panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nleft does not contain {:?}", left, right, right_value);
if result.is_ok() {
for left_value in left.iter() {
if !right.contains(left_value) {
result = Err(SetEqError::RightMissing(left_value.clone()));
}
}
}
result
}};
}
#[macro_export]
macro_rules! assert_set_eq {
($left:expr,$right:expr) => {{
use util::test::*;
use util::set_eq;
let left = $left;
let right = $right;
match set_eq!(left, right) {
Err(SetEqError::LeftMissing(missing)) => {
panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nright does not contain {:?}", left, right, missing);
},
Err(SetEqError::RightMissing(missing)) => {
panic!("assertion failed: `(left == right)`\n left: {:?}\nright: {:?}\nleft does not contain {:?}", left, right, missing);
},
_ => {}
}
}};
}

View file

@ -21,22 +21,44 @@ pub fn marked_text_by(
pub fn marked_text(marked_text: &str) -> (String, Vec<usize>) {
let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['|']);
(unmarked_text, markers.remove(&'|').unwrap_or_else(Vec::new))
(unmarked_text, markers.remove(&'|').unwrap_or_default())
}
pub fn marked_text_ranges_by(
marked_text: &str,
delimiters: Vec<(char, char)>,
) -> (String, HashMap<(char, char), Vec<Range<usize>>>) {
let all_markers = delimiters
.iter()
.flat_map(|(start, end)| [*start, *end])
.collect();
let (unmarked_text, mut markers) = marked_text_by(marked_text, all_markers);
let range_lookup = delimiters
.into_iter()
.map(|(start_marker, end_marker)| {
let starts = markers.remove(&start_marker).unwrap_or_default();
let ends = markers.remove(&end_marker).unwrap_or_default();
assert_eq!(starts.len(), ends.len(), "marked ranges are unbalanced");
let ranges = starts
.into_iter()
.zip(ends)
.map(|(start, end)| {
assert!(end >= start, "marked ranges must be disjoint");
start..end
})
.collect::<Vec<Range<usize>>>();
((start_marker, end_marker), ranges)
})
.collect();
(unmarked_text, range_lookup)
}
pub fn marked_text_ranges(marked_text: &str) -> (String, Vec<Range<usize>>) {
let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['[', ']']);
let opens = markers.remove(&'[').unwrap_or_default();
let closes = markers.remove(&']').unwrap_or_default();
assert_eq!(opens.len(), closes.len(), "marked ranges are unbalanced");
let ranges = opens
.into_iter()
.zip(closes)
.map(|(open, close)| {
assert!(close >= open, "marked ranges must be disjoint");
open..close
})
.collect();
(unmarked_text, ranges)
let (unmarked_text, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']')]);
(
unmarked_text,
ranges.remove(&('[', ']')).unwrap_or_else(Vec::new),
)
}