makepad_code_editor/
history.rs

1use crate::{
2    selection::SelectionSet,
3    session::SessionId,
4    text::{Edit, Text},
5};
6
7#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
8pub struct History {
9    text: Text,
10    current_desc: Option<GroupDesc>,
11    undo_stack: Stack,
12    redo_stack: Stack,
13}
14
15impl History {
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    pub fn as_text(&self) -> &Text {
21        &self.text
22    }
23
24    pub fn force_new_group(&mut self) {
25        self.current_desc = None;
26    }
27
28    pub fn push_or_extend_group(
29        &mut self,
30        session_id: SessionId,
31        edit_kind: EditKind,
32        selections: &SelectionSet,
33    ) {
34        let desc = GroupDesc {
35            session_id,
36            edit_kind,
37        };
38        if !self
39            .current_desc
40            .map_or(false, |current_desc| current_desc.can_merge_with(desc))
41        {
42            self.undo_stack.push_group(selections.clone());
43            self.current_desc = Some(desc);
44        }
45    }
46
47    pub fn apply_edit(&mut self, edit: Edit) {
48        let inverted_edit = edit.clone().invert(&self.text);
49        self.text.apply_change(edit.change);
50        self.undo_stack.push_edit(inverted_edit);
51        self.redo_stack.clear();
52    }
53
54    pub fn undo(
55        &mut self,
56        selections: &SelectionSet,
57        edits: &mut Vec<Edit>,
58    ) -> Option<SelectionSet> {
59        if let Some(new_selections) = self.undo_stack.pop_group(edits) {
60            self.redo_stack.push_group(selections.clone());
61            for edit in edits {
62                let inverted_edit = edit.clone().invert(&self.text);
63                self.text.apply_change(edit.change.clone());
64                self.redo_stack.push_edit(inverted_edit);
65            }
66            self.current_desc = None;
67            Some(new_selections)
68        } else {
69            None
70        }
71    }
72
73    pub fn redo(
74        &mut self,
75        selections: &SelectionSet,
76        edits: &mut Vec<Edit>,
77    ) -> Option<SelectionSet> {
78        if let Some(new_selections) = self.redo_stack.pop_group(edits) {
79            self.undo_stack.push_group(selections.clone());
80            for edit in edits {
81                let inverted_edit = edit.clone().invert(&self.text);
82                self.text.apply_change(edit.change.clone());
83                self.undo_stack.push_edit(inverted_edit);
84            }
85            self.current_desc = None;
86            Some(new_selections)
87        } else {
88            None
89        }
90    }
91
92    pub fn into_text(self) -> Text {
93        self.text
94    }
95}
96
97impl From<Text> for History {
98    fn from(text: Text) -> Self {
99        Self {
100            text,
101            ..Self::default()
102        }
103    }
104}
105
106#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
107pub enum EditKind {
108    Insert,
109    InsertSpace,
110    Delete,
111    Indent,
112    Outdent,
113    Other,
114}
115
116impl EditKind {
117    fn can_merge_with(self, other: Self) -> bool {
118        if self == Self::Other {
119            return false;
120        }
121        self == other
122    }
123}
124
125#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
126struct GroupDesc {
127    session_id: SessionId,
128    edit_kind: EditKind,
129}
130
131impl GroupDesc {
132    fn can_merge_with(self, other: GroupDesc) -> bool {
133        self.session_id == other.session_id && self.edit_kind.can_merge_with(other.edit_kind)
134    }
135}
136
137#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
138struct Stack {
139    groups: Vec<Group>,
140    edits: Vec<Edit>,
141}
142
143impl Stack {
144    fn push_group(&mut self, selections: SelectionSet) {
145        self.groups.push(Group {
146            selections,
147            edit_start: self.edits.len(),
148        });
149    }
150
151    fn push_edit(&mut self, edit: Edit) {
152        self.edits.push(edit);
153    }
154
155    fn pop_group(&mut self, edits: &mut Vec<Edit>) -> Option<SelectionSet> {
156        match self.groups.pop() {
157            Some(group) => {
158                edits.extend(self.edits.drain(group.edit_start..).rev());
159                Some(group.selections)
160            }
161            None => None,
162        }
163    }
164
165    fn clear(&mut self) {
166        self.groups.clear();
167        self.edits.clear();
168    }
169}
170
171#[derive(Clone, Debug, Eq, Hash, PartialEq)]
172struct Group {
173    selections: SelectionSet,
174    edit_start: usize,
175}