formualizer_eval/engine/graph/editor/
change_log.rs1use crate::SheetId;
9use crate::engine::named_range::{NameScope, NamedDefinition};
10use crate::engine::vertex::VertexId;
11use crate::reference::CellRef;
12use formualizer_common::Coord as AbsCoord;
13use formualizer_common::LiteralValue;
14use formualizer_parse::parser::ASTNode;
15
16#[derive(Debug, Clone, PartialEq)]
18pub enum ChangeEvent {
19 SetValue {
21 addr: CellRef,
22 old: Option<LiteralValue>,
23 new: LiteralValue,
24 },
25 SetFormula {
26 addr: CellRef,
27 old: Option<ASTNode>,
28 new: ASTNode,
29 },
30 AddVertex {
32 id: VertexId,
33 coord: AbsCoord,
34 sheet_id: SheetId,
35 value: Option<LiteralValue>,
36 formula: Option<ASTNode>,
37 kind: Option<crate::engine::vertex::VertexKind>,
38 flags: Option<u8>,
39 },
40 RemoveVertex {
41 id: VertexId,
42 old_value: Option<LiteralValue>,
44 old_formula: Option<ASTNode>,
45 old_dependencies: Vec<VertexId>, old_dependents: Vec<VertexId>, coord: Option<AbsCoord>,
48 sheet_id: Option<SheetId>,
49 kind: Option<crate::engine::vertex::VertexKind>,
50 flags: Option<u8>,
51 },
52
53 CompoundStart {
55 description: String, depth: usize,
57 },
58 CompoundEnd {
59 depth: usize,
60 },
61
62 VertexMoved {
64 id: VertexId,
65 old_coord: AbsCoord,
66 new_coord: AbsCoord,
67 },
68 FormulaAdjusted {
69 id: VertexId,
70 old_ast: ASTNode,
71 new_ast: ASTNode,
72 },
73 NamedRangeAdjusted {
74 name: String,
75 scope: NameScope,
76 old_definition: NamedDefinition,
77 new_definition: NamedDefinition,
78 },
79 EdgeAdded {
80 from: VertexId,
81 to: VertexId,
82 },
83 EdgeRemoved {
84 from: VertexId,
85 to: VertexId,
86 },
87
88 DefineName {
90 name: String,
91 scope: NameScope,
92 definition: NamedDefinition,
93 },
94 UpdateName {
95 name: String,
96 scope: NameScope,
97 old_definition: NamedDefinition,
98 new_definition: NamedDefinition,
99 },
100 DeleteName {
101 name: String,
102 scope: NameScope,
103 old_definition: Option<NamedDefinition>,
104 },
105}
106
107#[derive(Debug, Default)]
109pub struct ChangeLog {
110 events: Vec<ChangeEvent>,
111 enabled: bool,
112 compound_depth: usize,
114 seqs: Vec<u64>,
116 groups: Vec<Option<u64>>,
118 next_seq: u64,
119 group_stack: Vec<u64>,
121 next_group_id: u64,
122}
123
124impl ChangeLog {
125 pub fn new() -> Self {
126 Self {
127 events: Vec::new(),
128 enabled: true,
129 compound_depth: 0,
130 seqs: Vec::new(),
131 groups: Vec::new(),
132 next_seq: 0,
133 group_stack: Vec::new(),
134 next_group_id: 1,
135 }
136 }
137
138 pub fn record(&mut self, event: ChangeEvent) {
139 if self.enabled {
140 let seq = self.next_seq;
141 self.next_seq += 1;
142 let current_group = self.group_stack.last().copied();
143 self.events.push(event);
144 self.seqs.push(seq);
145 self.groups.push(current_group);
146 }
147 }
148
149 pub fn begin_compound(&mut self, description: String) {
151 self.compound_depth += 1;
152 if self.compound_depth == 1 {
153 let gid = self.next_group_id;
155 self.next_group_id += 1;
156 self.group_stack.push(gid);
157 } else {
158 if let Some(&gid) = self.group_stack.last() {
160 self.group_stack.push(gid);
161 }
162 }
163 if self.enabled {
164 self.record(ChangeEvent::CompoundStart {
165 description,
166 depth: self.compound_depth,
167 });
168 }
169 }
170
171 pub fn end_compound(&mut self) {
173 if self.compound_depth > 0 {
174 if self.enabled {
175 self.record(ChangeEvent::CompoundEnd {
176 depth: self.compound_depth,
177 });
178 }
179 self.compound_depth -= 1;
180 self.group_stack.pop();
181 }
182 }
183
184 pub fn events(&self) -> &[ChangeEvent] {
185 &self.events
186 }
187
188 pub fn truncate(&mut self, len: usize) {
190 self.events.truncate(len);
191 self.seqs.truncate(len);
192 self.groups.truncate(len);
193 }
194
195 pub fn clear(&mut self) {
196 self.events.clear();
197 self.seqs.clear();
198 self.groups.clear();
199 self.compound_depth = 0;
200 self.group_stack.clear();
201 }
202
203 pub fn len(&self) -> usize {
204 self.events.len()
205 }
206
207 pub fn is_empty(&self) -> bool {
208 self.events.is_empty()
209 }
210
211 pub fn take_from(&mut self, index: usize) -> Vec<ChangeEvent> {
213 self.events.split_off(index)
214 }
215
216 pub fn set_enabled(&mut self, enabled: bool) {
218 self.enabled = enabled;
219 }
220
221 pub fn compound_depth(&self) -> usize {
223 self.compound_depth
224 }
225
226 pub fn meta(&self, index: usize) -> Option<(u64, Option<u64>)> {
228 self.seqs
229 .get(index)
230 .copied()
231 .zip(self.groups.get(index).copied())
232 }
233
234 pub fn last_group_indices(&self) -> Vec<usize> {
236 if let Some(&last_gid) = self.groups.iter().rev().flatten().next() {
237 let idxs: Vec<usize> = self
238 .groups
239 .iter()
240 .enumerate()
241 .filter_map(|(i, g)| if *g == Some(last_gid) { Some(i) } else { None })
242 .collect();
243 if !idxs.is_empty() {
244 return idxs;
245 }
246 }
247 self.events.len().checked_sub(1).into_iter().collect()
248 }
249}
250
251pub trait ChangeLogger {
253 fn record(&mut self, event: ChangeEvent);
254 fn set_enabled(&mut self, enabled: bool);
255 fn begin_compound(&mut self, description: String);
256 fn end_compound(&mut self);
257}
258
259impl ChangeLogger for ChangeLog {
260 fn record(&mut self, event: ChangeEvent) {
261 ChangeLog::record(self, event);
262 }
263
264 fn set_enabled(&mut self, enabled: bool) {
265 self.enabled = enabled;
266 }
267
268 fn begin_compound(&mut self, description: String) {
269 ChangeLog::begin_compound(self, description);
270 }
271
272 fn end_compound(&mut self) {
273 ChangeLog::end_compound(self);
274 }
275}
276
277pub struct NullChangeLogger;
279
280impl ChangeLogger for NullChangeLogger {
281 fn record(&mut self, _: ChangeEvent) {}
282 fn set_enabled(&mut self, _: bool) {}
283 fn begin_compound(&mut self, _: String) {}
284 fn end_compound(&mut self) {}
285}