use std::collections::VecDeque;
use markdown::{Cursor, Doc, Selection};
use crate::comment::Anchor;
pub const DEFAULT_UNDO_LIMIT: usize = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditKind {
Insert,
Delete,
Structure,
}
#[derive(Clone)]
struct Snapshot {
doc: Doc,
selection: Selection,
anchors: Vec<Anchor>,
}
pub struct History {
undo: VecDeque<Snapshot>,
redo: Vec<Snapshot>,
limit: usize,
last: Option<(EditKind, Cursor)>,
}
impl Default for History {
fn default() -> Self {
Self {
undo: VecDeque::new(),
redo: Vec::new(),
limit: DEFAULT_UNDO_LIMIT,
last: None,
}
}
}
impl History {
pub fn with_limit(limit: usize) -> Self {
Self {
limit,
..Self::default()
}
}
pub fn record(&mut self, kind: EditKind, doc: &Doc, selection: Selection, anchors: &[Anchor]) {
self.redo.clear();
if self.joins(kind, selection) {
return;
}
self.undo.push_back(Snapshot {
doc: doc.clone(),
selection,
anchors: anchors.to_vec(),
});
while self.undo.len() > self.limit {
self.undo.pop_front();
}
self.last = None;
}
pub fn landed(&mut self, kind: EditKind, selection: Selection) {
self.last = (kind != EditKind::Structure).then_some((kind, selection.head));
}
fn joins(&self, kind: EditKind, selection: Selection) -> bool {
if kind == EditKind::Structure || self.undo.is_empty() {
return false;
}
self.last == Some((kind, selection.head)) && selection.is_collapsed()
}
pub fn interrupt(&mut self) {
self.last = None;
}
pub fn undo(&mut self, doc: &Doc, selection: Selection, anchors: &[Anchor]) -> Option<Step> {
let previous = self.undo.pop_back()?;
self.redo.push(Snapshot {
doc: doc.clone(),
selection,
anchors: anchors.to_vec(),
});
self.last = None;
Some(previous.into())
}
pub fn redo(&mut self, doc: &Doc, selection: Selection, anchors: &[Anchor]) -> Option<Step> {
let next = self.redo.pop()?;
self.undo.push_back(Snapshot {
doc: doc.clone(),
selection,
anchors: anchors.to_vec(),
});
self.last = None;
Some(next.into())
}
}
pub struct Step {
pub doc: Doc,
pub selection: Selection,
pub anchors: Vec<Anchor>,
}
impl From<Snapshot> for Step {
fn from(snapshot: Snapshot) -> Self {
Self {
doc: snapshot.doc,
selection: snapshot.selection,
anchors: snapshot.anchors,
}
}
}