Start working on new features

This commit is contained in:
Austen Adler 2022-08-09 17:45:46 -04:00
parent 0aacd16f50
commit 8a3a77df55
2 changed files with 286 additions and 42 deletions

View File

@ -1,6 +1,10 @@
use crate::KakError; use crate::KakError;
use core::fmt::{Display, Formatter}; use core::fmt::{Display, Formatter};
use std::{fmt, str::FromStr}; use std::{
cmp::{max, min},
fmt,
str::FromStr,
};
pub type Selection = String; pub type Selection = String;
@ -23,6 +27,50 @@ pub struct SelectionWithSubselections {
pub subselections: Vec<SelectionWithDesc>, pub subselections: Vec<SelectionWithDesc>,
} }
/// A selection desc that spans only one row
///
/// This type is required when doing operations that involve multiple lines, but logic cannot exist to see if, for example, 2 selection descs with row:1 col:1-10 and row:2 col:0-1 is adjacent
#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Debug)]
pub struct RowSelectionDesc {
pub row: usize,
pub left_col: usize,
pub right_col: usize,
}
// impl RowSelectionDesc {
// pub fn
// }
impl TryFrom<SelectionDesc> for RowSelectionDesc {
type Error = KakError;
fn try_from(sd: SelectionDesc) -> Result<Self, Self::Error> {
if sd.left.row == sd.right.row {
Ok(Self {
row: sd.left.row,
left_col: sd.left.col,
right_col: sd.right.col,
})
} else {
Err(KakError::MultiRowSelectionNotSupported)
}
}
}
impl Into<SelectionDesc> for RowSelectionDesc {
fn into(self) -> SelectionDesc {
SelectionDesc {
left: AnchorPosition {
row: self.row,
col: self.left_col,
},
right: AnchorPosition {
row: self.row,
col: self.right_col,
},
}
}
}
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Debug)] #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Debug)]
pub struct SelectionDesc { pub struct SelectionDesc {
pub left: AnchorPosition, pub left: AnchorPosition,
@ -48,37 +96,77 @@ impl SelectionDesc {
} }
#[must_use] #[must_use]
pub fn contains(&self, b: &Self) -> bool { pub fn contains<ISD>(&self, other: ISD) -> bool
where
ISD: Into<Self>,
{
// Cursor and anchor can be flipped. Set a.0 to be leftmost cursor // Cursor and anchor can be flipped. Set a.0 to be leftmost cursor
let sorted_a = self.sort(); let (a, b) = (self.sort(), other.into().sort());
let sorted_b = b.sort();
sorted_b.left >= sorted_a.left && sorted_b.right <= sorted_a.right b.left >= a.left && b.right <= a.right
}
#[must_use]
pub fn intersect(&self, other: &Self) -> Option<Self> {
// Set a and b to the leftmost and rightmost selection
let (a, b) = (min(self, other).sort(), max(self, other).sort());
if a.contains(&b.left)
|| a.contains(&b.right)
|| b.contains(&a.left)
|| b.contains(&a.right)
{
// Some(Self {})
None
} else {
None
}
}
#[must_use]
pub fn partial_union(&self, other: &Self) -> Option<Self> {
// Set a and b to the leftmost and rightmost selection
let (a, b) = (min(self, other).sort(), max(self, other).sort());
eprintln!("Partial union args: a: {a:#?}, b: {b:#?}");
eprintln!(
"Checking if {} contains {}: {}",
a,
b.left,
a.contains(&b.left)
);
eprintln!(
"Checking if {} contains {}: {}",
b,
a.right,
b.contains(&a.right)
);
// Either the left side of b is contained b a, or
// This will not work when the right side of a is the end of line and the left side of b is beginning of line
// This is because selection descs do not know when a selection desc is at the end of a line
if a.contains(&b.left) || b.contains(&a.right)
// If b's left is one col off from a's right
// || (a.right.row == b_left.row && a.right.col == b.left.col.saturating_sub(1))
// Or b's right is
// || (a.left.row == b_left.row && a.left.col == b.right.col.saturating_sub(1))
{
Some(SelectionDesc {
left: min(a.left, b.left),
right: max(a.right, b.right),
})
} else {
None
}
} }
#[must_use] #[must_use]
pub fn subtract(&self, b: &Self) -> MaybeSplit<Self> { pub fn subtract(&self, b: &Self) -> MaybeSplit<Self> {
// let sorted_self = self.sort(); let sorted_b = b.sort();
// let sorted_b = b.sort();
// My left is contained in b match (sorted_b.contains(&self.left), sorted_b.contains(&self.right), self.contains(&sorted_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, _) => { (true, true, _) => {
// self is contained by b // self is contained by sorted_b
MaybeSplit::Nothing MaybeSplit::Nothing
} }
(false, false, false) => { (false, false, false) => {
@ -92,14 +180,14 @@ impl SelectionDesc {
Self { Self {
left: self.left, left: self.left,
right: AnchorPosition { right: AnchorPosition {
row: b.left.row, row: sorted_b.left.row,
col: b.left.col.saturating_sub(1), col: sorted_b.left.col.saturating_sub(1),
}, },
}, },
Self { Self {
left: AnchorPosition { left: AnchorPosition {
row: b.right.row, row: sorted_b.right.row,
col: b.right.col.saturating_add(1), col: sorted_b.right.col.saturating_add(1),
}, },
right: self.right, right: self.right,
}, },
@ -109,8 +197,8 @@ impl SelectionDesc {
// Only self's left is contained // Only self's left is contained
MaybeSplit::Just(Self { MaybeSplit::Just(Self {
left: AnchorPosition { left: AnchorPosition {
row: b.right.row, row: sorted_b.right.row,
col: b.right.col.saturating_add(1), col: sorted_b.right.col.saturating_add(1),
}, },
right: self.right, right: self.right,
}) })
@ -120,8 +208,8 @@ impl SelectionDesc {
MaybeSplit::Just(Self { MaybeSplit::Just(Self {
left: self.left, left: self.left,
right: AnchorPosition { right: AnchorPosition {
row: b.left.row, row: sorted_b.left.row,
col: b.left.col.saturating_sub(1), col: sorted_b.left.col.saturating_sub(1),
}, },
}) })
} }
@ -129,6 +217,21 @@ impl SelectionDesc {
} }
} }
impl From<&SelectionDesc> for SelectionDesc {
fn from(sd: &SelectionDesc) -> Self {
sd.clone()
}
}
impl From<&AnchorPosition> for SelectionDesc {
fn from(ap: &AnchorPosition) -> Self {
Self {
left: ap.clone(),
right: ap.clone(),
}
}
}
impl AsRef<SelectionDesc> for SelectionDesc { impl AsRef<SelectionDesc> for SelectionDesc {
fn as_ref(&self) -> &Self { fn as_ref(&self) -> &Self {
&self &self
@ -552,6 +655,36 @@ mod test {
} }
}}; }};
} }
// Reversed
macro_rules! sdr {
($b:expr, $d:expr) => {{
sd!(1, $d, 1, $b)
}};
($a:expr, $b:expr,$c:expr,$d:expr) => {{
SelectionDesc {
left: AnchorPosition { row: $c, col: $d },
right: AnchorPosition { row: $a, col: $b },
}
}};
}
macro_rules! mixed_test {
($left:tt, $op:ident, $right:tt, $expected:expr) => {
eprintln!("Testing ({}).{}({})", sd!$left, stringify!($op), &sd!$right);
assert_eq!(sd!$left.$op(&sd!$right), $expected);
eprintln!("Testing ({}).{}({})", sd!$left, stringify!($op), &sdr!$right);
assert_eq!(sd!$left.$op(&sdr!$right), $expected);
eprintln!("Testing ({}).{}({})", sdr!$left, stringify!($op), &sd!$right);
assert_eq!(sdr!$left.$op(&sd!$right), $expected);
eprintln!("Testing ({}).{}({})", sdr!$left, stringify!($op), &sdr!$right);
assert_eq!(sdr!$left.$op(&sdr!$right), $expected);
}
}
const SD: SelectionDesc = SelectionDesc { const SD: SelectionDesc = SelectionDesc {
left: AnchorPosition { row: 18, col: 9 }, left: AnchorPosition { row: 18, col: 9 },
right: AnchorPosition { row: 10, col: 1 }, right: AnchorPosition { row: 10, col: 1 },
@ -566,9 +699,10 @@ mod test {
#[test] #[test]
fn test_sort() { fn test_sort() {
// Check if sorting works
assert_eq!(SD.sort(), sd!(10, 1, 18, 9)); assert_eq!(SD.sort(), sd!(10, 1, 18, 9));
assert_eq!(SD.sort(), SD.sort().sort()); assert_eq!(SD.sort(), SD.sort().sort());
assert_eq!(sd!(10, 1, 18, 9).sort(), sd!(10, 1, 18, 9));
assert_eq!(sdr!(10, 1, 18, 9).sort(), sd!(10, 1, 18, 9));
} }
#[test] #[test]
@ -590,6 +724,98 @@ mod test {
assert!(!SD.contains(&sd!(10, 1, 18, 10))); assert!(!SD.contains(&sd!(10, 1, 18, 10)));
assert!(!SD.contains(&sd!(9, 1, 18, 9))); assert!(!SD.contains(&sd!(9, 1, 18, 9)));
assert!(!SD.contains(&sd!(10, 0, 18, 9))); assert!(!SD.contains(&sd!(10, 0, 18, 9)));
assert!(SD.contains(&sdr!(17, 9, 10, 1)));
assert!(SD.contains(&sdr!(18, 8, 10, 1)));
assert!(SD.contains(&sdr!(18, 9, 11, 1)));
assert!(SD.contains(&sdr!(18, 9, 10, 2)));
assert!(SD.contains(&sdr!(10, 1, 17, 9)));
assert!(SD.contains(&sdr!(10, 1, 18, 8)));
assert!(SD.contains(&sdr!(11, 1, 18, 9)));
assert!(SD.contains(&sdr!(10, 2, 18, 9)));
assert!(!SD.contains(&sdr!(19, 9, 10, 1)));
assert!(!SD.contains(&sdr!(18, 10, 10, 1)));
assert!(!SD.contains(&sdr!(18, 9, 9, 1)));
assert!(!SD.contains(&sdr!(18, 9, 10, 0)));
assert!(!SD.contains(&sdr!(10, 1, 19, 9)));
assert!(!SD.contains(&sdr!(10, 1, 18, 10)));
assert!(!SD.contains(&sdr!(9, 1, 18, 9)));
assert!(!SD.contains(&sdr!(10, 0, 18, 9)));
}
#[test]
fn test_partial_union() {
// Testing a+b
// 01234567
// a: ^_^
// b: ^____^
mixed_test!((1, 3), partial_union, (0, 5), Some(sd!(0, 5)));
// 01234567
// a: ^__^
// b: ^____^
mixed_test!((0, 3), partial_union, (0, 5), Some(sd!(0, 5)));
// 01234567
// a: ^___^
// b: ^___^
mixed_test!((1, 5), partial_union, (1, 5), Some(sd!(1, 5)));
// 01234567
// a: ^_____^
// b: ^____^
mixed_test!((0, 6), partial_union, (0, 5), Some(sd!(0, 6)));
// 01234567
// a: ^____^
// b: ^____^
mixed_test!((1, 6), partial_union, (0, 5), Some(sd!(0, 6)));
// 01234567
// a: ^____^
// b: ^____^
mixed_test!((0, 5), partial_union, (1, 6), Some(sd!(0, 6)));
// 01234567
// a: ^______^
// b: ^____^
mixed_test!((0, 7), partial_union, (1, 6), Some(sd!(0, 7)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((3, 3), partial_union, (0, 5), Some(sd!(0, 5)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((0, 0), partial_union, (0, 5), Some(sd!(0, 5)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((0, 0), partial_union, (1, 6), Some(sd!(0, 6)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((5, 5), partial_union, (0, 5), Some(sd!(0, 5)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((6, 6), partial_union, (0, 5), Some(sd!(0, 6)));
// 01234567
// a: ^
// b: ^____^
mixed_test!((7, 7), partial_union, (0, 5), None);
// 01234567
// a: ^
// b: ^____^
mixed_test!((0, 0), partial_union, (2, 7), None);
} }
#[test] #[test]
@ -600,63 +826,72 @@ mod test {
// a: ^_^ // a: ^_^
// b: ^____^ // b: ^____^
assert_eq!(sd!(1, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); assert_eq!(sd!(1, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(1, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^__^ // a: ^__^
// b: ^____^ // b: ^____^
assert_eq!(sd!(0, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); assert_eq!(sd!(0, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(0, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^___^ // a: ^___^
// b: ^___^ // b: ^___^
assert_eq!(sd!(1, 5).subtract(&sd!(1, 5)), MaybeSplit::Nothing); assert_eq!(sd!(1, 5).subtract(&sd!(1, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(1, 5).subtract(&sdr!(1, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^_____^ // a: ^_____^
// b: ^____^ // b: ^____^
assert_eq!(sd!(0, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6))); assert_eq!(sd!(0, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
assert_eq!(sd!(0, 6).subtract(&sdr!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
// 01234567 // 01234567
// a: ^____^ // a: ^____^
// b: ^____^ // b: ^____^
assert_eq!(sd!(1, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6))); assert_eq!(sd!(1, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
assert_eq!(sd!(1, 6).subtract(&sdr!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
// 01234567 // 01234567
// a: ^____^ // a: ^____^
// b: ^____^ // b: ^____^
assert_eq!(sd!(0, 5).subtract(&sd!(1, 6)), MaybeSplit::Just(sd!(0, 0))); assert_eq!(sd!(0, 5).subtract(&sd!(1, 6)), MaybeSplit::Just(sd!(0, 0)));
assert_eq!(sd!(0, 5).subtract(&sdr!(1, 6)), MaybeSplit::Just(sd!(0, 0)));
// 01234567 // 01234567
// a: ^______^ // a: ^______^
// b: ^____^ // b: ^____^
assert_eq!( assert_eq!(sd! (0, 7).subtract(&sd!(1, 6)), MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7)) );
sd!(0, 7).subtract(&sd!(1, 6)), assert_eq!(sd! (0, 7).subtract(&sdr!(1, 6)), MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7)) );
MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7))
);
// 01234567 // 01234567
// a: ^ // a: ^
// b: ^____^ // b: ^____^
assert_eq!(sd!(3, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); assert_eq!(sd!(3, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(3, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^ // a: ^
// b: ^____^ // b: ^____^
assert_eq!(sd!(0, 0).subtract(&sd!(0, 5)), MaybeSplit::Nothing); assert_eq!(sd!(0, 0).subtract(&sd!(0, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(0, 0).subtract(&sdr!(0, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^ // a: ^
// b: ^____^ // b: ^____^
assert_eq!(sd!(0, 0).subtract(&sd!(1, 6)), MaybeSplit::Just(sd!(0, 0))); assert_eq!(sd!(0, 0).subtract(&sd!(1, 6)), MaybeSplit::Just(sd!(0, 0)));
assert_eq!(sd!(0, 0).subtract(&sdr!(1, 6)), MaybeSplit::Just(sd!(0, 0)));
// 01234567 // 01234567
// a: ^ // a: ^
// b: ^____^ // b: ^____^
assert_eq!(sd!(5, 5).subtract(&sd!(0, 5)), MaybeSplit::Nothing); assert_eq!(sd!(5, 5).subtract(&sd!(0, 5)), MaybeSplit::Nothing);
assert_eq!(sd!(5, 5).subtract(&sdr!(0, 5)), MaybeSplit::Nothing);
// 01234567 // 01234567
// a: ^ // a: ^
// b: ^____^ // b: ^____^
assert_eq!(sd!(6, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6))); assert_eq!(sd!(6, 6).subtract(&sd!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
assert_eq!(sd!(6, 6).subtract(&sdr!(0, 5)), MaybeSplit::Just(sd!(6, 6)));
} }
} }

View File

@ -55,6 +55,15 @@ fn bounding_box(_options: &Options) -> Result<Vec<SelectionDesc>, KakError> {
}) })
.ok_or_else(|| KakError::Custom(String::from("Selection is empty")))?; .ok_or_else(|| KakError::Custom(String::from("Selection is empty")))?;
// let (leftmost_row, rightmost_row) = selection_descs
// .first()
// .map(|sd| sd.left.row)
// .zip(selection_descs.last().map(|sd| sd.right.row))
// .ok_or_else(|| KakError::Custom(String::from("Selection is empty")))?;
// Get every line in the document
// let document_selections_desc: Vec<SelectionDesc> = get_selections_desc(Some("%<a-s>"))?;
// Now, split on newline // Now, split on newline
// TODO: Should I use <a-s>? // TODO: Should I use <a-s>?
// kakplugin::cmd(&format!("exec 'S\\n<ret>'"))?; // kakplugin::cmd(&format!("exec 'S\\n<ret>'"))?;