makepad_code_editor/
history.rs1use 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}