use std::cmp::Ordering;
use std::ops::Range;
use log::{error, warn};
use crate::cursor::cursor_set::CursorSet;
use crate::primitives::has_invariant::HasInvariant;
use crate::text::text_buffer::TextBuffer;
pub const NEWLINE_WIDTH: u16 = 1;
pub const ZERO_CURSOR: Cursor = Cursor::new(0);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum CursorStatus {
None,
WithinSelection,
UnderCursor,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Selection {
pub b: usize,
pub e: usize,
}
impl Selection {
pub fn new(b: usize, e: usize) -> Self {
debug_assert!(b < e, "b {} e {}", b, e);
Selection { b, e }
}
pub fn within(&self, char_idx: usize) -> bool {
char_idx >= self.b && char_idx < self.e
}
pub fn len(&self) -> usize {
debug_assert!(self.b < self.e);
if self.b >= self.e {
error!("selection with begin > end, returning 0 for length: {:?}", self);
0
} else {
self.e - self.b
}
}
}
impl PartialOrd<Self> for Selection {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}
impl Ord for Selection {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&self.b, &other.b).then(Ord::cmp(&self.e, &other.e))
}
}
pub type ForwardWordDeterminant = dyn Fn(&dyn TextBuffer, usize, usize) -> bool;
pub type BackwardWordDeterminant = dyn Fn(&dyn TextBuffer, usize, usize) -> bool;
pub fn default_word_determinant(buffer: &dyn TextBuffer, first_idx: usize, current_idx: usize) -> bool {
match (buffer.char_at(first_idx), buffer.char_at(current_idx)) {
(Some(first_char), Some(current_char)) => first_char.is_whitespace() == current_char.is_whitespace(),
_ => false,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Cursor {
pub s: Option<Selection>,
pub a: usize,
pub preferred_column: Option<usize>,
}
impl Cursor {
pub fn single() -> Self {
Cursor {
s: None,
a: 0,
preferred_column: None,
}
}
pub const fn new(anc: usize) -> Self {
Cursor {
s: None,
a: anc,
preferred_column: None,
}
}
pub fn as_cursor_set(self) -> CursorSet {
CursorSet::singleton(self)
}
pub fn with_selection(self, selection: Selection) -> Self {
debug_assert!(selection.b == self.a || selection.e == self.a);
let a = if selection.e == self.a || selection.b == self.a {
self.a
} else {
warn!("Attempted setting selection not respecting invariant. Moving anchor to re-establish it.");
selection.e
};
Cursor {
s: Some(selection),
a,
..self
}
}
pub fn with_preferred_column(self, preferred_column: usize) -> Self {
Cursor {
preferred_column: Some(preferred_column),
..self
}
}
pub fn shift_by(&mut self, shift: isize) -> bool {
debug_assert!(self.check_invariant());
if shift == 0 {
return false;
}
if shift < 0 {
let abs_shift = shift.unsigned_abs();
if self.a < abs_shift || self.s.map(|sel| sel.b < abs_shift).unwrap_or(false) {
error!("attempted to substract {} from {:?}, ignoring completely.", shift, self);
return false;
}
}
self.a = (shift + self.a as isize) as usize;
if let Some(sel) = self.s {
self.s = Some(Selection::new((shift + sel.b as isize) as usize, (shift + sel.e as isize) as usize));
}
debug_assert!(self.check_invariant());
true
}
pub fn advance_and_clear(&mut self, advance_by: isize) -> bool {
debug_assert!(self.check_invariant());
let result = if advance_by < 0 && self.a < advance_by.unsigned_abs() {
error!("attempted to substract {} from {}, using 0 as failsafe.", advance_by, self.a);
self.a = 0;
self.clear_both();
false
} else {
self.a = (advance_by + self.a as isize) as usize;
self.clear_both();
true
};
debug_assert!(self.check_invariant());
result
}
pub fn update_select(&mut self, old_pos: usize, new_pos: usize) {
if old_pos == new_pos {
return;
}
match self.s {
None => self.s = Some(Selection::new(usize::min(old_pos, new_pos), usize::max(old_pos, new_pos))),
Some(sel) => {
debug_assert!(old_pos == sel.b || old_pos == sel.e);
if sel.b == old_pos {
if new_pos != sel.e {
self.s = Some(Selection::new(usize::min(new_pos, sel.e), usize::max(new_pos, sel.e)));
} else {
self.s = None;
}
} else if sel.e == old_pos {
if sel.b != new_pos {
self.s = Some(Selection::new(usize::min(new_pos, sel.b), usize::max(new_pos, sel.b)));
} else {
self.s = None;
}
} else {
error!("invariant that selection begins or ends with anchor broken. Not crashing, but fix it.");
}
}
};
debug_assert!(self.check_invariant());
}
pub fn clear_selection(&mut self) {
self.s = None;
debug_assert!(self.check_invariant());
}
pub fn clear_pc(&mut self) {
self.preferred_column = None;
debug_assert!(self.check_invariant());
}
pub fn clear_both(&mut self) -> bool {
let res = self.s.is_some() || self.preferred_column.is_some();
self.s = None;
self.preferred_column = None;
debug_assert!(self.check_invariant());
res
}
pub fn get_cursor_status_for_char(&self, char_idx: usize) -> CursorStatus {
if char_idx == self.a {
return CursorStatus::UnderCursor;
}
if self.s.is_some() && self.s.unwrap().within(char_idx) {
return CursorStatus::WithinSelection;
}
CursorStatus::None
}
pub fn move_home(&mut self, rope: &dyn TextBuffer, selecting: bool) -> bool {
debug_assert!(self.check_invariant());
let old_pos = self.a;
let line = rope.char_to_line(self.a).unwrap(); let new_pos = rope.line_to_char(line).unwrap();
debug_assert!(new_pos <= old_pos);
let result = if new_pos == self.a {
if self.preferred_column.is_some() {
self.preferred_column = None;
true
} else {
false
}
} else {
self.a = new_pos;
if selecting {
self.update_select(old_pos, new_pos);
} else {
self.clear_selection();
}
self.preferred_column = None;
true
};
debug_assert!(self.check_invariant());
result
}
pub fn move_end(&mut self, rope: &dyn TextBuffer, selecting: bool) -> bool {
debug_assert!(self.check_invariant());
let old_pos = self.a;
let next_line = rope.char_to_line(self.a).unwrap() + 1;
let new_pos = if rope.len_lines() > next_line {
rope.line_to_char(next_line).unwrap() - 1 } else {
rope.len_chars() };
debug_assert!(new_pos >= old_pos);
let res = if new_pos == self.a {
if self.preferred_column.is_some() {
self.preferred_column = None;
true
} else {
false
}
} else {
self.a = new_pos;
if selecting {
self.update_select(old_pos, new_pos);
} else {
self.clear_selection();
}
self.preferred_column = None;
true
};
debug_assert!(self.check_invariant());
res
}
pub(crate) fn word_begin(&mut self, buffer: &dyn TextBuffer, selecting: bool, word_determinant: &BackwardWordDeterminant) -> bool {
debug_assert!(self.check_invariant());
if self.a == 0 {
return false;
}
let old_pos = self.a;
if self.a > 0 {
self.a -= 1;
let first_char_pos = self.a;
while self.a > 0 && word_determinant(buffer, first_char_pos, self.a - 1) {
self.a -= 1;
}
}
if selecting {
self.update_select(old_pos, self.a);
} else {
self.clear_selection();
}
debug_assert!(old_pos >= self.a);
debug_assert!(self.check_invariant());
old_pos != self.a
}
pub(crate) fn word_end(&mut self, buffer: &dyn TextBuffer, selecting: bool, word_determinant: &ForwardWordDeterminant) -> bool {
if self.a == buffer.len_chars() {
return false;
}
let old_pos = self.a;
if self.a < buffer.len_chars() {
if word_determinant(buffer, old_pos, self.a) {
while self.a < buffer.len_chars() && word_determinant(buffer, old_pos, self.a) {
self.a += 1;
}
} else {
self.a += 1;
}
}
if selecting {
self.update_select(old_pos, self.a);
} else {
self.clear_selection();
}
debug_assert!(old_pos <= self.a);
debug_assert!(self.check_invariant());
old_pos != self.a
}
pub fn simplify(&mut self) -> bool {
let mut res = false;
if self.preferred_column.is_some() {
self.preferred_column = None;
res = true;
}
if self.s.is_some() {
self.s = None;
res = true;
}
debug_assert!(self.check_invariant());
res
}
pub fn is_simple(&self) -> bool {
self.s.is_none()
}
pub fn anchor_left(&self) -> bool {
self.s.map(|s| s.b == self.a).unwrap_or(false)
}
pub fn anchor_right(&self) -> bool {
self.s.map(|s| s.e == self.a).unwrap_or(false)
}
pub fn get_begin(&self) -> usize {
self.s.map(|s| s.b).unwrap_or(self.a)
}
pub fn get_end(&self) -> usize {
self.s.map(|s| s.e).unwrap_or(self.a)
}
pub fn intersects(&self, char_range: &Range<usize>) -> bool {
debug_assert!(self.check_invariant());
if self.is_simple() {
return char_range.start <= self.a && self.a < char_range.end;
}
let mut brackets = vec![
(char_range.start, true),
(char_range.end, false),
(self.get_begin(), true),
(self.get_end(), false),
];
brackets.sort();
let mut how_many_open_brackets: u8 = 0;
for b in brackets {
if b.1 {
how_many_open_brackets += 1;
} else {
how_many_open_brackets -= 1;
}
if how_many_open_brackets > 1 {
return true;
}
}
false
}
}
impl HasInvariant for Cursor {
fn check_invariant(&self) -> bool {
if let Some(s) = self.s {
s.b != s.e && (s.b == self.a || s.e == self.a)
} else {
true
}
}
}
impl PartialOrd<Self> for Cursor {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}
impl Ord for Cursor {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&self.a, &other.a).then(Ord::cmp(&self.s, &other.s).then(Ord::cmp(&self.preferred_column, &other.preferred_column)))
}
}
impl From<(usize, usize, usize)> for Cursor {
fn from(val: (usize, usize, usize)) -> Self {
Cursor {
s: Some(Selection { b: val.0, e: val.1 }),
a: val.2,
preferred_column: None,
}
}
}
impl From<usize> for Cursor {
fn from(val: usize) -> Self {
Cursor {
s: None,
a: val,
preferred_column: None,
}
}
}