use std::ops::Range;
use markdown::{Annotation, Cursor, Selection, Splice};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CommentId(pub u64);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Anchor {
pub id: CommentId,
pub range: Selection,
pub state: Annotation,
}
impl Anchor {
pub fn new(id: CommentId, range: Selection) -> Self {
Self {
id,
range,
state: Annotation::default(),
}
}
pub fn detached(&self) -> bool {
self.range.is_collapsed()
}
pub(crate) fn map(&mut self, delta: &Delta) {
let (start, end) = self.range.ordered();
self.range = match (delta.cursor(start), delta.cursor(end)) {
(Some(start), Some(end)) => Selection::new(start.min(end), start.max(end)),
(surviving, other) => Selection::at(surviving.or(other).unwrap_or_default()),
};
}
}
pub(crate) enum Delta {
Spliced(Splice),
Moved { at: Range<usize>, to: Option<usize> },
Opened { at: usize, count: usize },
}
impl Delta {
fn cursor(&self, at: Cursor) -> Option<Cursor> {
match self {
Self::Spliced(splice) => {
let (start, end) = splice.removed.ordered();
if at < start {
return Some(at);
}
if at <= end {
return Some(splice.caret);
}
if at.block == end.block && at.part == end.part {
return Some(Cursor {
offset: splice.caret.offset + (at.offset - end.offset),
..splice.caret
});
}
Some(Cursor {
block: at.block.checked_add_signed(splice.blocks)?,
..at
})
}
Self::Moved { at: span, to } => {
if span.contains(&at.block) {
return Some(Cursor {
block: (*to)? + (at.block - span.start),
..at
});
}
let pulled = if at.block >= span.end {
at.block - span.len()
} else {
at.block
};
let block = match to {
Some(to) if pulled >= *to => pulled + span.len(),
_ => pulled,
};
Some(Cursor { block, ..at })
}
Self::Opened { at: opened, count } => Some(Cursor {
block: if at.block >= *opened {
at.block + count
} else {
at.block
},
..at
}),
}
}
}