use crate::DataProvider;
use crate::canvas::state::SelectionState;
use crate::editor::EditorCore;
pub(crate) const DEFAULT_HISTORY_LIMIT: usize = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum EditKind {
Insert,
Delete,
Other,
}
impl EditKind {
fn coalesces(self) -> bool {
matches!(self, EditKind::Insert | EditKind::Delete)
}
}
#[derive(Debug, Clone)]
pub(crate) struct EditSnapshot {
content: Vec<String>,
current_field: usize,
cursor_pos: usize,
}
impl<D: DataProvider> EditorCore<D> {
fn snapshot_now(&self) -> EditSnapshot {
EditSnapshot {
content: self.data_provider.capture_content(),
current_field: self.ui_state.current_field,
cursor_pos: self.ui_state.cursor_pos,
}
}
pub(crate) fn record_checkpoint(&mut self, kind: EditKind) {
if !self.history_enabled {
return;
}
if kind.coalesces() && self.history_last_kind == Some(kind) {
return;
}
let snapshot = self.snapshot_now();
self.undo_stack.push(snapshot);
if self.undo_stack.len() > self.history_limit {
self.undo_stack.remove(0);
}
self.redo_stack.clear();
self.history_last_kind = Some(kind);
}
pub(crate) fn break_undo_coalescing(&mut self) {
self.history_last_kind = None;
}
fn apply_snapshot(&mut self, snapshot: EditSnapshot) {
self.data_provider.restore_content(&snapshot.content);
let field_count = self.data_provider.field_count();
self.ui_state.current_field = if field_count == 0 {
0
} else {
snapshot.current_field.min(field_count - 1)
};
let len = self.current_text().chars().count();
self.set_cursor_raw(snapshot.cursor_pos.min(len));
self.ui_state.selection = SelectionState::None;
self.after_history_restore();
}
fn after_history_restore(&mut self) {
#[cfg(feature = "validation")]
{
let count = self.data_provider.field_count();
for i in 0..count {
let text = self.data_provider.field_value(i).to_string();
let _ = self.ui_state.validation.validate_field_content(i, &text);
}
}
#[cfg(feature = "suggestions")]
self.ui_state.close_suggestions();
}
pub fn undo(&mut self) -> bool {
if let Some(previous) = self.undo_stack.pop() {
let current = self.snapshot_now();
self.redo_stack.push(current);
self.apply_snapshot(previous);
self.history_last_kind = None;
true
} else {
false
}
}
pub fn redo(&mut self) -> bool {
if let Some(next) = self.redo_stack.pop() {
let current = self.snapshot_now();
self.undo_stack.push(current);
self.apply_snapshot(next);
self.history_last_kind = None;
true
} else {
false
}
}
pub fn can_undo(&self) -> bool {
!self.undo_stack.is_empty()
}
pub fn can_redo(&self) -> bool {
!self.redo_stack.is_empty()
}
pub fn clear_history(&mut self) {
self.undo_stack.clear();
self.redo_stack.clear();
self.history_last_kind = None;
}
pub fn set_history_limit(&mut self, limit: usize) {
self.history_limit = limit.max(1);
while self.undo_stack.len() > self.history_limit {
self.undo_stack.remove(0);
}
}
pub fn set_history_enabled(&mut self, enabled: bool) {
self.history_enabled = enabled;
if !enabled {
self.clear_history();
}
}
pub fn is_history_enabled(&self) -> bool {
self.history_enabled
}
}