makepad-code-editor 1.0.0

Makepad studio
Documentation
use crate::{
    selection::SelectionSet,
    session::SessionId,
    text::{Edit, Text},
};

#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct History {
    text: Text,
    current_desc: Option<GroupDesc>,
    undo_stack: Stack,
    redo_stack: Stack,
}

#[derive(Clone,Copy)]
pub enum NewGroup{
    Yes,
    No
}

impl History {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn as_text(&self) -> &Text {
        &self.text
    }

    pub fn force_new_group(&mut self) {
        self.current_desc = None;
    }

    pub fn push_or_extend_group(
        &mut self,
        session_id: SessionId,
        edit_kind: EditKind,
        selections: &SelectionSet,
    ) {
        let desc = GroupDesc {
            session_id,
            edit_kind,
        };
        if !self
            .current_desc
            .map_or(false, |current_desc| current_desc.can_merge_with(desc))
        {
            self.undo_stack.push_group(selections.clone());
            self.current_desc = Some(desc);
        }
    }

    pub fn apply_edit(&mut self, edit: Edit) {
        let inverted_edit = edit.clone().invert(&self.text);
        self.text.apply_change(edit.change);
        self.undo_stack.push_edit(inverted_edit);
        self.redo_stack.clear();
    }

    pub fn undo(
        &mut self,
        selections: &SelectionSet,
        edits: &mut Vec<Edit>,
    ) -> Option<SelectionSet> {
        if let Some(new_selections) = self.undo_stack.pop_group(edits) {
            self.redo_stack.push_group(selections.clone());
            for edit in edits {
                let inverted_edit = edit.clone().invert(&self.text);
                self.text.apply_change(edit.change.clone());
                self.redo_stack.push_edit(inverted_edit);
            }
            self.current_desc = None;
            Some(new_selections)
        } else {
            None
        }
    }

    pub fn redo(
        &mut self,
        selections: &SelectionSet,
        edits: &mut Vec<Edit>,
    ) -> Option<SelectionSet> {
        if let Some(new_selections) = self.redo_stack.pop_group(edits) {
            self.undo_stack.push_group(selections.clone());
            for edit in edits {
                let inverted_edit = edit.clone().invert(&self.text);
                self.text.apply_change(edit.change.clone());
                self.undo_stack.push_edit(inverted_edit);
            }
            self.current_desc = None;
            Some(new_selections)
        } else {
            None
        }
    }

    pub fn into_text(self) -> Text {
        self.text
    }
}

impl From<Text> for History {
    fn from(text: Text) -> Self {
        Self {
            text,
            ..Self::default()
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EditKind {
    Insert,
    InsertSpace,
    Delete,
    Indent,
    Group(u64),
    Outdent,
    Other,
}

impl EditKind {
    fn can_merge_with(self, other: Self) -> bool {
        if self == Self::Other {
            return false;
        }
        self == other
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct GroupDesc {
    session_id: SessionId,
    edit_kind: EditKind,
}

impl GroupDesc {
    fn can_merge_with(self, other: GroupDesc) -> bool {
        self.session_id == other.session_id && self.edit_kind.can_merge_with(other.edit_kind)
    }
}

#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
struct Stack {
    groups: Vec<Group>,
    edits: Vec<Edit>,
}

impl Stack {
    fn push_group(&mut self, selections: SelectionSet) {
        self.groups.push(Group {
            selections,
            edit_start: self.edits.len(),
        });
    }

    fn push_edit(&mut self, edit: Edit) {
        self.edits.push(edit);
    }

    fn pop_group(&mut self, edits: &mut Vec<Edit>) -> Option<SelectionSet> {
        match self.groups.pop() {
            Some(group) => {
                edits.extend(self.edits.drain(group.edit_start..).rev());
                Some(group.selections)
            }
            None => None,
        }
    }

    fn clear(&mut self) {
        self.groups.clear();
        self.edits.clear();
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct Group {
    selections: SelectionSet,
    edit_start: usize,
}