use crate::node::{Mark, Node};
use crate::normalize::{normalize_children, NormalizeOptions};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Position {
pub child: usize,
pub offset: usize,
}
impl Position {
#[inline]
pub fn new(child: usize, offset: usize) -> Self {
Self { child, offset }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Range {
pub start: Position,
pub end: Position,
}
impl Range {
#[inline]
pub fn new(start: Position, end: Position) -> Self {
Self { start, end }
}
#[inline]
pub fn collapsed(pos: Position) -> Self {
Self {
start: pos,
end: pos,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RangeError {
ChildOutOfRange {
child: usize,
},
OffsetOutOfRange {
child: usize,
offset: usize,
},
NotTextNode {
child: usize,
},
InvertedRange,
}
impl fmt::Display for RangeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RangeError::ChildOutOfRange { child } => write!(f, "range: child {child} out of range"),
RangeError::OffsetOutOfRange { child, offset } => {
write!(f, "range: offset {offset} out of range in child {child}")
}
RangeError::NotTextNode { child } => {
write!(f, "range: child {child} is not a text node")
}
RangeError::InvertedRange => write!(f, "range: end precedes start"),
}
}
}
impl std::error::Error for RangeError {}
impl Node {
pub fn insert_text(
&mut self,
pos: Position,
text: &str,
marks: Option<&[Mark]>,
) -> Result<(), RangeError> {
let children = self.children_mut();
let i = ensure_boundary(children, pos)?;
children.insert(i, make_text(text, marks));
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
pub fn delete_range(&mut self, range: Range) -> Result<(), RangeError> {
let children = self.children_mut();
let (s, e) = resolve_range(children, range)?;
children.drain(s..e);
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
pub fn replace_range(
&mut self,
range: Range,
text: &str,
marks: Option<&[Mark]>,
) -> Result<(), RangeError> {
let children = self.children_mut();
let (s, e) = resolve_range(children, range)?;
children.drain(s..e);
if !text.is_empty() {
children.insert(s, make_text(text, marks));
}
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
pub fn add_mark_range(&mut self, range: Range, mark: Mark) -> Result<(), RangeError> {
let children = self.children_mut();
let (s, e) = resolve_range(children, range)?;
for node in &mut children[s..e] {
if is_text(node) {
node.add_mark(mark.clone());
}
}
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
pub fn remove_mark_range(&mut self, range: Range, mark_type: &str) -> Result<(), RangeError> {
let children = self.children_mut();
let (s, e) = resolve_range(children, range)?;
for node in &mut children[s..e] {
if is_text(node) {
node.remove_mark(mark_type);
}
}
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
pub fn toggle_mark_range(&mut self, range: Range, mark: Mark) -> Result<(), RangeError> {
let children = self.children_mut();
let (s, e) = resolve_range(children, range)?;
let all_have = children[s..e]
.iter()
.filter(|n| is_text(n))
.all(|n| n.has_mark(&mark.mark_type));
for node in &mut children[s..e] {
if is_text(node) {
if all_have {
node.remove_mark(&mark.mark_type);
} else {
node.add_mark(mark.clone());
}
}
}
normalize_children(children, &NormalizeOptions::default());
Ok(())
}
}
#[inline]
fn is_text(n: &Node) -> bool {
n.node_type.as_deref() == Some("text")
}
#[inline]
fn char_len(n: &Node) -> usize {
n.text.as_deref().unwrap_or("").chars().count()
}
fn make_text(text: &str, marks: Option<&[Mark]>) -> Node {
match marks {
Some(m) if !m.is_empty() => Node::text_with_marks(text, m.iter().cloned()),
_ => Node::text(text),
}
}
fn split_text_at(node: &Node, k: usize) -> (Node, Node) {
let s = node.text.as_deref().unwrap_or("");
let byte = s.char_indices().nth(k).map_or(s.len(), |(b, _)| b);
let (l, r) = s.split_at(byte);
let mut left = node.clone();
left.text = Some(l.to_owned());
let mut right = node.clone();
right.text = Some(r.to_owned());
(left, right)
}
fn ensure_boundary(children: &mut Vec<Node>, pos: Position) -> Result<usize, RangeError> {
if pos.child > children.len() {
return Err(RangeError::ChildOutOfRange { child: pos.child });
}
if pos.child == children.len() {
return if pos.offset == 0 {
Ok(children.len())
} else {
Err(RangeError::OffsetOutOfRange {
child: pos.child,
offset: pos.offset,
})
};
}
if is_text(&children[pos.child]) {
let cl = char_len(&children[pos.child]);
if pos.offset == 0 {
return Ok(pos.child);
}
if pos.offset == cl {
return Ok(pos.child + 1);
}
if pos.offset > cl {
return Err(RangeError::OffsetOutOfRange {
child: pos.child,
offset: pos.offset,
});
}
let (l, r) = split_text_at(&children[pos.child], pos.offset);
children[pos.child] = l;
children.insert(pos.child + 1, r);
Ok(pos.child + 1)
} else if pos.offset == 0 {
Ok(pos.child)
} else {
Err(RangeError::NotTextNode { child: pos.child })
}
}
fn resolve_range(children: &mut Vec<Node>, range: Range) -> Result<(usize, usize), RangeError> {
let (sp, ep) = (range.start, range.end);
if (ep.child, ep.offset) < (sp.child, sp.offset) {
return Err(RangeError::InvertedRange);
}
let e = ensure_boundary(children, ep)?;
let len_after_end = children.len();
let s = ensure_boundary(children, sp)?;
let start_split_inserted = children.len() > len_after_end;
let e = if start_split_inserted && s <= e {
e + 1
} else {
e
};
Ok((s, e))
}