Add subtract function

This commit is contained in:
Austen Adler 2022-07-15 18:50:40 -04:00
parent 4923a86b5f
commit d69cec4d73
5 changed files with 200 additions and 63 deletions

View File

@ -91,9 +91,9 @@ pub fn get_selections_with_desc() -> Result<Vec<SelectionWithDesc>, KakError> {
/// # Errors
///
/// Will return `Err` if command fifo could not be opened, read from, or written to
pub fn set_selections<'a, I, S: 'a + ?Sized>(selections: I) -> Result<(), KakError>
pub fn set_selections<'a, I, S: 'a>(selections: I) -> Result<(), KakError>
where
I: IntoIterator<Item = &'a S>,
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut selections_iter = selections.into_iter().peekable();

View File

@ -15,7 +15,7 @@ pub struct SelectionWithSubselections {
pub subselections: Vec<SelectionWithDesc>,
}
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Debug)]
pub struct SelectionDesc {
pub left: AnchorPosition,
pub right: AnchorPosition,
@ -47,6 +47,76 @@ impl SelectionDesc {
sorted_b.left >= sorted_a.left && sorted_b.right <= sorted_a.right
}
#[must_use]
pub fn subtract(&self, b: &Self) -> Vec<Self> {
// let sorted_self = self.sort();
// let sorted_b = b.sort();
// My left is contained in b
let left_contained = b.contains(&SelectionDesc {
left: self.left,
right: self.left,
});
// My right is contained in b
let right_contained = b.contains(&SelectionDesc {
left: self.right,
right: self.right,
});
// b is contaned in self
let b_contained = self.contains(b);
match (left_contained, right_contained, b_contained) {
(true, true, _) => {
// self is contained by b
vec![]
}
(false, false, false) => {
// There is no intersection
// TODO: Why can't I clone myself?
vec![self.clone()]
}
(false, false, true) => {
// B is contained and it does not intersect with left or right
vec![
Self {
left: self.left,
right: AnchorPosition {
row: b.left.row,
col: b.left.col.saturating_sub(1),
},
},
Self {
left: AnchorPosition {
row: b.right.row,
col: b.right.col.saturating_add(1),
},
right: self.right,
},
]
}
(true, false, _) => {
// Only self's left is contained
vec![Self {
left: AnchorPosition {
row: b.right.row,
col: b.right.col.saturating_add(1),
},
right: self.right,
}]
}
(false, true, _) => {
// Only self's right is contained
vec![Self {
left: self.left,
right: AnchorPosition {
row: b.left.row,
col: b.left.col.saturating_sub(1),
},
}]
}
}
}
}
impl AsRef<SelectionDesc> for SelectionDesc {
@ -75,7 +145,7 @@ impl FromStr for SelectionDesc {
}
}
#[derive(PartialOrd, PartialEq, Clone, Eq, Ord, Debug)]
#[derive(PartialOrd, PartialEq, Copy, Clone, Eq, Ord, Debug)]
pub struct AnchorPosition {
pub row: usize,
pub col: usize,
@ -454,6 +524,18 @@ impl FromStr for Register {
#[cfg(test)]
mod test {
use super::*;
// Selection desc creator
macro_rules! sd {
($b:expr, $d:expr) => {{
sd!(1,$b,1,$d)
}};
($a:expr, $b:expr,$c:expr,$d:expr) => {{
SelectionDesc {
left: AnchorPosition { row: $a, col: $b },
right: AnchorPosition { row: $c, col: $d },
}
}};
}
const SD: SelectionDesc = SelectionDesc {
left: AnchorPosition { row: 18, col: 9 },
right: AnchorPosition { row: 10, col: 1 },
@ -461,39 +543,104 @@ mod test {
#[test]
fn test_anchor_position() {
// Check parsing
assert_eq!(SelectionDesc::from_str("18.9,10.1").unwrap(), SD);
assert_eq!(sd!(18,9,10,1), SD);
// Check if multiple parsed ones match
assert_eq!(
SelectionDesc::from_str("18.9,10.1").unwrap(),
SelectionDesc::from_str("18.9,10.1").unwrap()
sd!(18,9,10,1),
sd!(18,9,10,1)
);
}
#[test]
fn test_sort() {
// Check if sorting works
assert_eq!(SD.sort(), SelectionDesc::from_str("10.1,18.9").unwrap());
assert_eq!(SD.sort(), sd!(10,1,18,9));
assert_eq!(SD.sort(), SD.sort().sort());
}
#[test]
fn test_contains() {
assert!(SD.contains(&SD));
assert!(SD.contains(&SelectionDesc::from_str("17.9,10.1").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("18.8,10.1").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("18.9,11.1").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("18.9,10.2").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("10.1,17.9").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("10.1,18.8").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("11.1,18.9").unwrap()));
assert!(SD.contains(&SelectionDesc::from_str("10.2,18.9").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("19.9,10.1").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("18.10,10.1").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("18.9,9.1").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("18.9,10.0").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("10.1,19.9").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("10.1,18.10").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("9.1,18.9").unwrap()));
assert!(!SD.contains(&SelectionDesc::from_str("10.0,18.9").unwrap()));
assert!(SD.contains(&sd!(17,9,10,1)));
assert!(SD.contains(&sd!(18,8,10,1)));
assert!(SD.contains(&sd!(18,9,11,1)));
assert!(SD.contains(&sd!(18,9,10,2)));
assert!(SD.contains(&sd!(10,1,17,9)));
assert!(SD.contains(&sd!(10,1,18,8)));
assert!(SD.contains(&sd!(11,1,18,9)));
assert!(SD.contains(&sd!(10,2,18,9)));
assert!(!SD.contains(&sd!(19,9,10,1)));
assert!(!SD.contains(&sd!(18,10,10,1)));
assert!(!SD.contains(&sd!(18,9,9,1)));
assert!(!SD.contains(&sd!(18,9,10,0)));
assert!(!SD.contains(&sd!(10,1,19,9)));
assert!(!SD.contains(&sd!(10,1,18,10)));
assert!(!SD.contains(&sd!(9,1,18,9)));
assert!(!SD.contains(&sd!(10,0,18,9)));
}
#[test]
fn test_subtract() {
// Testing a-b
// 01234567
// a: ^_^
// b: ^____^
assert_eq!(sd!(1, 3).subtract(&sd!(0, 5)), vec![]);
// 01234567
// a: ^__^
// b: ^____^
assert_eq!(sd!(0, 3).subtract(&sd!(0, 5)), vec![]);
// 01234567
// a: ^___^
// b: ^___^
assert_eq!(sd!(1, 5).subtract(&sd!(1, 5)), vec![]);
// 01234567
// a: ^_____^
// b: ^____^
assert_eq!(sd!(0, 6).subtract(&sd!(0, 5)), vec![sd!(6, 6)]);
// 01234567
// a: ^____^
// b: ^____^
assert_eq!(sd!(1, 6).subtract(&sd!(0, 5)), vec![sd!(6, 6)]);
// 01234567
// a: ^____^
// b: ^____^
assert_eq!(sd!(0, 5).subtract(&sd!(1, 6)), vec![sd!(0, 0)]);
// 01234567
// a: ^______^
// b: ^____^
assert_eq!(sd!(0, 7).subtract(&sd!(1, 6)), vec![sd!(0, 0), sd!(7, 7)]);
// 01234567
// a: ^
// b: ^____^
assert_eq!(sd!(3, 3).subtract(&sd!(0, 5)), vec![]);
// 01234567
// a: ^
// b: ^____^
assert_eq!(sd!(0, 0).subtract(&sd!(0, 5)), vec![]);
// 01234567
// a: ^
// b: ^____^
assert_eq!(sd!(0, 0).subtract(&sd!(1, 6)), vec![sd!(0, 0)]);
// 01234567
// a: ^
// b: ^____^
assert_eq!(sd!(5, 5).subtract(&sd!(0, 5)), vec![]);
// 01234567
// a: ^
// b: ^____^
assert_eq!(sd!(6, 6).subtract(&sd!(0, 5)), vec![sd!(6, 6)]);
}
}

View File

@ -5,11 +5,11 @@ use std::cmp::{max, min};
#[derive(clap::StructOpt, Debug)]
pub struct Options {
// /// Bounding box mode, which selects the largest box to contain everything
// #[clap(short, long, help = "Select the bonding box of all selections")]
// bounding_box: bool,
// /// Allow selecting trailing newlines
// #[clap(short, long, help = "Allow selecting trailing newlines")]
// preserve_newlines: bool,
// #[clap(short, long, help = "Select the bonding box of all selections")]
// bounding_box: bool,
// /// Allow selecting trailing newlines
// #[clap(short, long, help = "Allow selecting trailing newlines")]
// preserve_newlines: bool,
}
pub fn box_(options: &Options) -> Result<String, KakError> {

View File

@ -10,6 +10,7 @@
mod box_;
mod errors;
// mod invert;
mod math_eval;
mod set;
mod shuf;

View File

@ -1,44 +1,33 @@
use evalexpr::{eval, Value};
use kakplugin::{get_selections, open_command_fifo, KakError};
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
use std::io::Write;
// TODO: Context for log() and others
#[derive(clap::StructOpt, Debug)]
pub struct Options;
pub fn math_eval(_options: &Options) -> Result<String, KakError> {
let selections = get_selections()?;
let mut f = open_command_fifo()?;
write!(f, "reg '\"'")?;
let mut err = None;
let mut err_count: usize = 0;
for i in selections.iter().map(|s| {
// TODO: Do all of these need to be strings?
match eval(s) {
Ok(Value::Float(f)) => Some(f.to_string()),
Ok(Value::Int(f)) => Some(f.to_string()),
// TODO: Should this be none?
Ok(_) => None,
Err(e) => {
eprintln!("Error: {:?}", e);
if err.is_none() {
err = Some(e);
err_count = err_count.saturating_add(1);
}
None
}
}
}) {
// TODO: String allocation?
let new_selection = i.map(|s| s.replace('\'', "''"));
// .unwrap_or_else(|| "".to_string());
write!(f, " '{}'", new_selection.as_deref().unwrap_or(""))?;
}
write!(f, " ; exec R;")?;
f.flush()?;
let selections = get_selections()?;
Ok(format!("MathEval {} selections", selections.len()))
set_selections(selections.iter().map(|s| match eval(s) {
Ok(Value::Float(f)) => f.to_string(),
Ok(Value::Int(f)) => f.to_string(),
Ok(_) => String::from(""),
Err(e) => {
eprintln!("Error: {:?}", e);
err_count = err_count.saturating_add(1);
// Set the selection to empty
String::from("")
}
}))?;
Ok(if err_count == 0 {
format!("Processed {} selections", selections.len())
} else {
format!(
"Processed {} selections ({} errors)",
selections.len().saturating_sub(err_count),
err_count
)
})
}