use std::cmp::{Ord, Ordering, PartialOrd};
use std::collections::VecDeque;
use crate::util::sort2;
use crate::prelude::Wrappable;
mod choice;
mod group;
mod state;
pub use choice::CursorChoice;
pub use group::{CursorGroup, CursorGroupCombineError, CursorGroupIter, CursorGroupIterMut};
pub use state::{CursorState, Selection, Selections};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Cursor {
pub(crate) xgoal: usize,
pub x: usize,
pub y: usize,
}
#[derive(Debug, Eq, PartialEq)]
pub enum CursorAdjustment {
Column {
line: usize,
column_start: usize,
amt_line: isize,
amt_col: isize,
},
Line {
line_start: usize,
line_end: usize,
amount: isize,
amount_after: isize,
},
}
impl Cursor {
pub fn new(line: usize, column: usize) -> Self {
Cursor { xgoal: column, x: column, y: line }
}
pub fn get_y(&self) -> usize {
self.y
}
pub fn get_x(&self) -> usize {
self.x
}
pub(crate) fn goal(mut self, goal: usize) -> Cursor {
self.xgoal = goal;
self
}
pub fn set_x(&mut self, x: usize) {
self.x = x;
self.xgoal = x;
}
pub fn set_y(&mut self, y: usize) {
self.y = y;
}
pub fn left(&mut self, off: usize) {
self.x = self.x.saturating_sub(off);
self.xgoal = self.x;
}
pub fn right(&mut self, off: usize) {
self.x = self.x.saturating_add(off);
self.xgoal = self.x;
}
pub fn down(&mut self, off: usize) {
self.y = self.y.saturating_add(off);
}
pub fn up(&mut self, off: usize) {
self.y = self.y.saturating_sub(off);
}
fn adjust_x(&mut self, off: isize) {
let abs = off.unsigned_abs();
if off < 0 {
self.left(abs);
} else {
self.right(abs);
}
}
fn adjust_y(&mut self, off: isize) {
let abs = off.unsigned_abs();
if off < 0 {
self.up(abs);
} else {
self.down(abs);
}
}
fn adjust1(&mut self, adj: &CursorAdjustment) {
match adj {
CursorAdjustment::Line { line_start, line_end, amount, amount_after } => {
if self.y >= *line_start && self.y <= *line_end {
if *amount == isize::MAX {
self.zero();
} else {
self.adjust_y(*amount);
}
} else if *amount_after != 0 && self.y > *line_end {
self.adjust_y(*amount_after);
}
},
CursorAdjustment::Column { line, column_start, amt_line, amt_col } => {
if self.y == *line && self.x >= *column_start {
self.adjust_y(*amt_line);
self.adjust_x(*amt_col);
}
},
}
}
}
pub trait Adjustable {
fn zero(&mut self);
fn adjust(&mut self, adj: &[CursorAdjustment]);
}
impl<T> Adjustable for Vec<T>
where
T: Adjustable,
{
fn zero(&mut self) {
for item in self.iter_mut() {
item.zero();
}
}
fn adjust(&mut self, adj: &[CursorAdjustment]) {
for item in self.iter_mut() {
item.adjust(adj);
}
}
}
impl<T> Adjustable for VecDeque<T>
where
T: Adjustable,
{
fn zero(&mut self) {
for item in self.iter_mut() {
item.zero();
}
}
fn adjust(&mut self, adj: &[CursorAdjustment]) {
for item in self.iter_mut() {
item.adjust(adj);
}
}
}
impl Adjustable for Cursor {
fn zero(&mut self) {
self.xgoal = 0;
self.x = 0;
self.y = 0;
}
fn adjust(&mut self, adjs: &[CursorAdjustment]) {
for adj in adjs {
self.adjust1(adj);
}
}
}
impl Wrappable for Cursor {
fn set_wrap(&mut self, wrap: bool) {
if wrap {
self.set_x(0);
}
}
}
impl PartialOrd for Cursor {
fn partial_cmp(&self, other: &Cursor) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Cursor {
fn cmp(&self, other: &Cursor) -> Ordering {
let ycmp = self.y.cmp(&other.y);
if ycmp != Ordering::Equal {
return ycmp;
}
let xcmp = self.x.cmp(&other.x);
if xcmp != Ordering::Equal {
return xcmp;
}
self.xgoal.cmp(&other.xgoal)
}
}
pub(crate) fn block_cursors(a: &Cursor, b: &Cursor) -> (Cursor, Cursor) {
let (lstart, lend) = sort2(a.y, b.y);
let lcol = a.x.min(b.x);
let rcol = a.x.max(b.x);
let rgoal = a.xgoal.max(b.xgoal);
(Cursor::new(lstart, lcol), Cursor::new(lend, rcol).goal(rgoal))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cursor_cmp() {
let c1 = Cursor::new(7, 6);
let c2 = Cursor::new(7, 10);
let c3 = Cursor::new(10, 0);
assert_eq!(c1.cmp(&c1), Ordering::Equal);
assert_eq!(c2.cmp(&c2), Ordering::Equal);
assert_eq!(c3.cmp(&c3), Ordering::Equal);
assert_eq!(c1.cmp(&c2), Ordering::Less);
assert_eq!(c1.cmp(&c3), Ordering::Less);
assert_eq!(c2.cmp(&c1), Ordering::Greater);
assert_eq!(c2.cmp(&c3), Ordering::Less);
assert_eq!(c3.cmp(&c1), Ordering::Greater);
assert_eq!(c3.cmp(&c2), Ordering::Greater);
}
#[test]
fn test_cursor_getters() {
let c1 = Cursor::new(5, 6);
let c2 = Cursor::new(0, 1000);
assert_eq!(c1.get_y(), 5);
assert_eq!(c1.get_x(), 6);
assert_eq!(c2.get_y(), 0);
assert_eq!(c2.get_x(), 1000);
}
}