use crate::node::Node;
use crate::plugin::{Plugin, PluginKey, PluginSet, PluginStates};
use crate::schema::Schema;
use crate::selection::Selection;
use crate::step::{Step, StepError};
use crate::transform::Transform;
const DEFAULT_HISTORY_DEPTH: usize = 100;
fn apply_steps(doc: &Node, steps: &[Box<dyn Step>], schema: &Schema) -> Result<Node, StepError> {
let mut cur = doc.clone();
for s in steps {
cur = s.apply(&cur, schema)?;
}
Ok(cur)
}
#[derive(Debug, Clone)]
struct HistEntry {
undo: Vec<Box<dyn Step>>,
redo: Vec<Box<dyn Step>>,
selection_before: Selection,
selection_after: Selection,
}
#[derive(Debug, Clone)]
pub struct History {
done: Vec<HistEntry>,
undone: Vec<HistEntry>,
depth: usize,
}
impl Default for History {
fn default() -> Self {
History {
done: Vec::new(),
undone: Vec::new(),
depth: DEFAULT_HISTORY_DEPTH,
}
}
}
impl History {
pub fn with_depth(depth: usize) -> Self {
History {
depth,
..Default::default()
}
}
pub fn undo_depth(&self) -> usize {
self.done.len()
}
pub fn redo_depth(&self) -> usize {
self.undone.len()
}
fn record(&mut self, mut entry: HistEntry, join: bool) {
self.undone.clear();
if join {
if let Some(prev) = self.done.last_mut() {
let mut undo = std::mem::take(&mut entry.undo);
undo.extend(std::mem::take(&mut prev.undo));
prev.undo = undo;
prev.redo.extend(std::mem::take(&mut entry.redo));
prev.selection_after = entry.selection_after;
return;
}
}
self.done.push(entry);
if self.done.len() > self.depth {
self.done.remove(0);
}
}
}
#[derive(Debug, Clone)]
pub struct EditorState {
doc: Node,
selection: Selection,
schema: Schema,
history: History,
plugins: PluginStates,
}
impl EditorState {
pub fn new(doc: Node, schema: Schema) -> Self {
Self::with_plugins(doc, schema, PluginSet::new())
}
pub fn with_plugins(doc: Node, schema: Schema, plugins: PluginSet) -> Self {
let seed = EditorState {
doc,
selection: Selection::caret(0),
schema,
history: History::default(),
plugins: PluginStates::default(),
};
let plugin_states = PluginStates::from_set(plugins, &seed);
EditorState {
plugins: plugin_states,
..seed
}
}
pub fn with_history(mut self, history: History) -> Self {
self.history = history;
self
}
pub fn plugin<P: Plugin>(&self, _key: PluginKey<P>) -> Option<&P::State> {
self.plugins.get::<P>()
}
pub fn doc(&self) -> &Node {
&self.doc
}
pub fn selection(&self) -> Selection {
self.selection
}
pub fn schema(&self) -> &Schema {
&self.schema
}
pub fn history(&self) -> &History {
&self.history
}
pub fn tr(&self) -> Transaction {
Transaction {
tr: Transform::new(self.doc.clone()),
selection: self.selection,
selection_set: false,
add_to_history: true,
join: false,
history_intent: None,
}
}
pub fn apply(&self, tx: Transaction) -> EditorState {
if let Some(intent) = tx.history_intent {
return match intent {
HistoryIntent::Undo => self.undo().unwrap_or_else(|| self.clone()),
HistoryIntent::Redo => self.redo().unwrap_or_else(|| self.clone()),
};
}
let new_doc = tx.tr.doc().clone();
let selection = if tx.selection_set {
tx.selection
} else {
self.selection.map(&new_doc, tx.tr.mapping())
};
let mut history = self.history.clone();
if tx.add_to_history && tx.tr.doc_changed() {
if let Ok(undo) = tx.tr.invert_steps() {
let redo: Vec<Box<dyn Step>> = tx.tr.steps().to_vec();
history.record(
HistEntry {
undo,
redo,
selection_before: self.selection,
selection_after: selection,
},
tx.join,
);
}
}
let plugins = self.plugins.apply(&tx, self);
EditorState {
doc: new_doc,
selection,
schema: self.schema.clone(),
history,
plugins,
}
}
pub fn undo(&self) -> Option<EditorState> {
let entry = self.history.done.last()?.clone();
let doc = apply_steps(&self.doc, &entry.undo, &self.schema).ok()?;
let mut history = self.history.clone();
history.done.pop();
history.undone.push(entry.clone());
Some(EditorState {
doc,
selection: entry.selection_before,
schema: self.schema.clone(),
history,
plugins: self.plugins.clone(),
})
}
pub fn redo(&self) -> Option<EditorState> {
let entry = self.history.undone.last()?.clone();
let doc = apply_steps(&self.doc, &entry.redo, &self.schema).ok()?;
let mut history = self.history.clone();
history.undone.pop();
history.done.push(entry.clone());
Some(EditorState {
doc,
selection: entry.selection_after,
schema: self.schema.clone(),
history,
plugins: self.plugins.clone(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HistoryIntent {
Undo,
Redo,
}
#[derive(Debug, Clone)]
pub struct Transaction {
tr: Transform,
selection: Selection,
selection_set: bool,
add_to_history: bool,
join: bool,
history_intent: Option<HistoryIntent>,
}
impl Transaction {
pub fn doc(&self) -> &Node {
self.tr.doc()
}
pub fn transform(&mut self) -> &mut Transform {
&mut self.tr
}
pub fn set_selection(&mut self, selection: Selection) -> &mut Self {
self.selection = selection;
self.selection_set = true;
self
}
pub fn no_history(&mut self) -> &mut Self {
self.add_to_history = false;
self
}
pub fn join_history(&mut self) -> &mut Self {
self.join = true;
self
}
pub fn doc_changed(&self) -> bool {
self.tr.doc_changed()
}
pub fn set_history_intent(&mut self, intent: HistoryIntent) -> &mut Self {
self.history_intent = Some(intent);
self
}
pub fn history_intent(&self) -> Option<HistoryIntent> {
self.history_intent
}
}