use crate::buffer::TextBuffer;
use std::ops::Range;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextEdit {
pub range: Range<usize>,
pub new_text: String,
pub old_text: String,
}
impl TextEdit {
pub fn new(
range: Range<usize>,
new_text: impl Into<String>,
old_text: impl Into<String>,
) -> Self {
Self {
range,
new_text: new_text.into(),
old_text: old_text.into(),
}
}
pub fn inverse(&self) -> TextEdit {
TextEdit {
range: self.range.start..(self.range.start + self.new_text.len()),
new_text: self.old_text.clone(),
old_text: self.new_text.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EditTransaction {
pub edits: Vec<TextEdit>,
}
impl EditTransaction {
pub fn new() -> Self {
Self { edits: Vec::new() }
}
pub fn push(&mut self, edit: TextEdit) {
self.edits.push(edit);
}
pub fn inverse(&self) -> EditTransaction {
let edits: Vec<TextEdit> = self.edits.iter().rev().map(|e| e.inverse()).collect();
EditTransaction { edits }
}
pub fn is_empty(&self) -> bool {
self.edits.is_empty()
}
}
impl Default for EditTransaction {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct EditHistory {
undo_stack: Vec<EditTransaction>,
redo_stack: Vec<EditTransaction>,
max_entries: usize,
}
const DEFAULT_MAX_ENTRIES: usize = 1000;
impl EditHistory {
pub fn new() -> Self {
Self {
undo_stack: Vec::new(),
redo_stack: Vec::new(),
max_entries: DEFAULT_MAX_ENTRIES,
}
}
pub fn with_max(max_entries: usize) -> Self {
assert!(max_entries > 0, "max_entries must be > 0");
Self {
undo_stack: Vec::new(),
redo_stack: Vec::new(),
max_entries,
}
}
pub fn apply(&mut self, txn: &EditTransaction, buffer: &mut TextBuffer) {
for edit in &txn.edits {
buffer.replace(edit.range.clone(), &edit.new_text);
}
self.undo_stack.push(txn.clone());
if self.undo_stack.len() > self.max_entries {
self.undo_stack.remove(0);
}
self.redo_stack.clear();
}
pub fn apply_edit(&mut self, buffer: &mut TextBuffer, range: Range<usize>, new_text: &str) {
let old_text = buffer.slice(range.clone()).to_string();
let edit = TextEdit::new(range, new_text, old_text);
let mut txn = EditTransaction::new();
txn.push(edit);
self.apply(&txn, buffer);
}
pub fn undo(&mut self, buffer: &mut TextBuffer) -> bool {
let txn = match self.undo_stack.pop() {
Some(t) => t,
None => return false,
};
let inv = txn.inverse();
for edit in &inv.edits {
buffer.replace(edit.range.clone(), &edit.new_text);
}
self.redo_stack.push(txn);
true
}
pub fn redo(&mut self, buffer: &mut TextBuffer) -> bool {
let txn = match self.redo_stack.pop() {
Some(t) => t,
None => return false,
};
for edit in &txn.edits {
buffer.replace(edit.range.clone(), &edit.new_text);
}
self.undo_stack.push(txn);
true
}
pub fn record(&mut self, txn: EditTransaction) {
self.undo_stack.push(txn);
if self.undo_stack.len() > self.max_entries {
self.undo_stack.remove(0);
}
self.redo_stack.clear();
}
pub fn pop_undo(&mut self) -> Option<EditTransaction> {
self.undo_stack.pop()
}
pub fn pop_redo(&mut self) -> Option<EditTransaction> {
self.redo_stack.pop()
}
pub fn push_redo(&mut self, txn: EditTransaction) {
self.redo_stack.push(txn);
}
pub fn push_undo(&mut self, txn: EditTransaction) {
self.undo_stack.push(txn);
if self.undo_stack.len() > self.max_entries {
self.undo_stack.remove(0);
}
}
pub fn undo_depth(&self) -> usize {
self.undo_stack.len()
}
pub fn redo_depth(&self) -> usize {
self.redo_stack.len()
}
pub fn max_entries(&self) -> usize {
self.max_entries
}
pub fn clear(&mut self) {
self.undo_stack.clear();
self.redo_stack.clear();
}
}
impl Default for EditHistory {
fn default() -> Self {
Self::new()
}
}