From 8a3a77df55d8a03780264aa6a1388741b3bec689 Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Tue, 9 Aug 2022 17:45:46 -0400 Subject: [PATCH] Start working on new features --- kakplugin/src/types.rs | 309 ++++++++++++++++++++++++++++++++++++----- src/box_.rs | 19 ++- 2 files changed, 286 insertions(+), 42 deletions(-) diff --git a/kakplugin/src/types.rs b/kakplugin/src/types.rs index 39dfa52..1ff463c 100644 --- a/kakplugin/src/types.rs +++ b/kakplugin/src/types.rs @@ -1,6 +1,10 @@ use crate::KakError; use core::fmt::{Display, Formatter}; -use std::{fmt, str::FromStr}; +use std::{ + cmp::{max, min}, + fmt, + str::FromStr, +}; pub type Selection = String; @@ -23,6 +27,50 @@ pub struct SelectionWithSubselections { pub subselections: Vec, } +/// 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 for RowSelectionDesc { + type Error = KakError; + fn try_from(sd: SelectionDesc) -> Result { + 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 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)] pub struct SelectionDesc { pub left: AnchorPosition, @@ -48,37 +96,77 @@ impl SelectionDesc { } #[must_use] - pub fn contains(&self, b: &Self) -> bool { + pub fn contains(&self, other: ISD) -> bool + where + ISD: Into, + { // Cursor and anchor can be flipped. Set a.0 to be leftmost cursor - let sorted_a = self.sort(); - let sorted_b = b.sort(); + let (a, b) = (self.sort(), other.into().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 { + // 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 { + // 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] pub fn subtract(&self, b: &Self) -> MaybeSplit { - // let sorted_self = self.sort(); - // let sorted_b = b.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) { + match (sorted_b.contains(&self.left), sorted_b.contains(&self.right), self.contains(&sorted_b)) { (true, true, _) => { - // self is contained by b + // self is contained by sorted_b MaybeSplit::Nothing } (false, false, false) => { @@ -92,14 +180,14 @@ impl SelectionDesc { Self { left: self.left, right: AnchorPosition { - row: b.left.row, - col: b.left.col.saturating_sub(1), + row: sorted_b.left.row, + col: sorted_b.left.col.saturating_sub(1), }, }, Self { left: AnchorPosition { - row: b.right.row, - col: b.right.col.saturating_add(1), + row: sorted_b.right.row, + col: sorted_b.right.col.saturating_add(1), }, right: self.right, }, @@ -109,8 +197,8 @@ impl SelectionDesc { // Only self's left is contained MaybeSplit::Just(Self { left: AnchorPosition { - row: b.right.row, - col: b.right.col.saturating_add(1), + row: sorted_b.right.row, + col: sorted_b.right.col.saturating_add(1), }, right: self.right, }) @@ -120,8 +208,8 @@ impl SelectionDesc { MaybeSplit::Just(Self { left: self.left, right: AnchorPosition { - row: b.left.row, - col: b.left.col.saturating_sub(1), + row: sorted_b.left.row, + 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 for SelectionDesc { fn as_ref(&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 { left: AnchorPosition { row: 18, col: 9 }, right: AnchorPosition { row: 10, col: 1 }, @@ -566,9 +699,10 @@ mod test { #[test] fn test_sort() { - // Check if sorting works assert_eq!(SD.sort(), sd!(10, 1, 18, 9)); 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] @@ -590,6 +724,98 @@ mod test { assert!(!SD.contains(&sd!(10, 1, 18, 10))); assert!(!SD.contains(&sd!(9, 1, 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] @@ -600,63 +826,72 @@ mod test { // a: ^_^ // b: ^____^ assert_eq!(sd!(1, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(1, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing); // 01234567 // a: ^__^ // b: ^____^ assert_eq!(sd!(0, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(0, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing); // 01234567 // a: ^___^ // b: ^___^ assert_eq!(sd!(1, 5).subtract(&sd!(1, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(1, 5).subtract(&sdr!(1, 5)), MaybeSplit::Nothing); // 01234567 // a: ^_____^ // b: ^____^ 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 // a: ^____^ // b: ^____^ 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 // a: ^____^ // b: ^____^ 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 // a: ^______^ // b: ^____^ - assert_eq!( - sd!(0, 7).subtract(&sd!(1, 6)), - MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7)) - ); + assert_eq!(sd! (0, 7).subtract(&sd!(1, 6)), MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7)) ); + assert_eq!(sd! (0, 7).subtract(&sdr!(1, 6)), MaybeSplit::JustTwo(sd!(0, 0), sd!(7, 7)) ); // 01234567 // a: ^ // b: ^____^ assert_eq!(sd!(3, 3).subtract(&sd!(0, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(3, 3).subtract(&sdr!(0, 5)), MaybeSplit::Nothing); // 01234567 // a: ^ // b: ^____^ assert_eq!(sd!(0, 0).subtract(&sd!(0, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(0, 0).subtract(&sdr!(0, 5)), MaybeSplit::Nothing); // 01234567 // a: ^ // b: ^____^ 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 // a: ^ // b: ^____^ assert_eq!(sd!(5, 5).subtract(&sd!(0, 5)), MaybeSplit::Nothing); + assert_eq!(sd!(5, 5).subtract(&sdr!(0, 5)), MaybeSplit::Nothing); // 01234567 // a: ^ // b: ^____^ 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))); } } diff --git a/src/box_.rs b/src/box_.rs index bd3938a..7ae9815 100644 --- a/src/box_.rs +++ b/src/box_.rs @@ -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 { @@ -55,6 +55,15 @@ fn bounding_box(_options: &Options) -> Result, KakError> { }) .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 = get_selections_desc(Some("%"))?; + // Now, split on newline // TODO: Should I use ? // kakplugin::cmd(&format!("exec 'S\\n'"))?;