1use std::ops::Range;
2
3use super::{
4 document::{BlockContent, EditorDocument},
5 id::BlockId,
6 inline::InlineText,
7 kind::BlockKindData,
8 selection::BlockSelection,
9 step::{DetachedBlock, Step, StepError, StepMap},
10};
11
12#[non_exhaustive]
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub enum EditOrigin {
16 Typing,
17 Backspace,
18 DeleteForward,
19 WordBackspace,
21 WordDelete,
23 Ime,
24 Paste,
25 Cut,
26 Command(&'static str),
27 InputRule(&'static str),
28 Api,
29 Undo,
30 Redo,
31}
32
33#[derive(Clone, Debug)]
35pub struct Transaction {
36 pub(crate) steps: Vec<Step>,
37 pub(crate) inverse: Vec<Step>,
38 pub(crate) maps: Vec<StepMap>,
39 pub(crate) selection_before: BlockSelection,
40 pub(crate) selection_after: BlockSelection,
41 pub(crate) origin: EditOrigin,
42}
43
44impl Transaction {
45 pub(crate) fn map_selection(&self, selection: &BlockSelection) -> Option<BlockSelection> {
48 let map = |mut p| {
49 for step_map in &self.maps {
50 p = step_map.map(p)?;
51 }
52 Some(p)
53 };
54 match selection {
55 BlockSelection::Text { anchor, head } => Some(BlockSelection::Text {
56 anchor: map(*anchor)?,
57 head: map(*head)?,
58 }),
59 BlockSelection::Blocks { .. } | BlockSelection::Cells { .. } => Some(selection.clone()),
60 }
61 }
62
63 pub(crate) fn is_empty(&self) -> bool {
64 self.steps.is_empty()
65 }
66
67 pub(crate) fn replay(steps: &[Step], doc: &mut EditorDocument) -> Result<(), StepError> {
69 for step in steps {
70 step.apply(doc)?;
71 }
72 doc.refresh_order();
73 doc.revision += 1;
74 Ok(())
75 }
76}
77
78pub(crate) struct TransactionBuilder<'a> {
82 pub(crate) doc: &'a mut EditorDocument,
83 steps: Vec<Step>,
84 inverse: Vec<Step>,
85 maps: Vec<StepMap>,
86}
87
88impl<'a> TransactionBuilder<'a> {
89 pub(crate) fn new(doc: &'a mut EditorDocument) -> Self {
90 Self {
91 doc,
92 steps: Vec::new(),
93 inverse: Vec::new(),
94 maps: Vec::new(),
95 }
96 }
97
98 pub(crate) fn step(&mut self, step: Step) -> Result<StepMap, StepError> {
99 let (inverse, map) = step.apply(self.doc)?;
100 self.steps.push(step);
101 self.inverse.push(inverse);
102 self.maps.push(map.clone());
103 Ok(map)
104 }
105
106 pub(crate) fn replace_text(
107 &mut self,
108 block: BlockId,
109 range: Range<usize>,
110 insert: InlineText,
111 ) -> Result<(), StepError> {
112 if range.is_empty() && insert.is_empty() {
113 return Ok(());
114 }
115 self.step(Step::ReplaceText {
116 block,
117 range,
118 insert,
119 })
120 .map(|_| ())
121 }
122
123 pub(crate) fn replace_plain(
124 &mut self,
125 block: BlockId,
126 range: Range<usize>,
127 insert: &str,
128 ) -> Result<(), StepError> {
129 if range.is_empty() && insert.is_empty() {
130 return Ok(());
131 }
132 self.step(Step::ReplacePlain {
133 block,
134 range,
135 insert: insert.to_string(),
136 })
137 .map(|_| ())
138 }
139
140 pub(crate) fn insert(
141 &mut self,
142 parent: BlockId,
143 index: usize,
144 blocks: Vec<DetachedBlock>,
145 ) -> Result<(), StepError> {
146 if blocks.is_empty() {
147 return Ok(());
148 }
149 self.step(Step::InsertBlocks {
150 parent,
151 index,
152 blocks,
153 })
154 .map(|_| ())
155 }
156
157 pub(crate) fn remove(
160 &mut self,
161 parent: BlockId,
162 range: Range<usize>,
163 ) -> Result<Vec<DetachedBlock>, StepError> {
164 if range.is_empty() {
165 return Ok(Vec::new());
166 }
167 self.step(Step::RemoveBlocks { parent, range })?;
168 match self.inverse.last() {
169 Some(Step::InsertBlocks { blocks, .. }) => Ok(blocks.clone()),
170 _ => Err(StepError::OutOfRange),
171 }
172 }
173
174 pub(crate) fn remove_block(&mut self, id: BlockId) -> Result<DetachedBlock, StepError> {
175 let (parent, index) = self
176 .doc
177 .index_in_parent(id)
178 .ok_or(StepError::UnknownBlock(id))?;
179 self.remove(parent, index..index + 1)?
180 .pop()
181 .ok_or(StepError::OutOfRange)
182 }
183
184 pub(crate) fn move_block(
187 &mut self,
188 id: BlockId,
189 parent: BlockId,
190 index: usize,
191 ) -> Result<(), StepError> {
192 let block = self.remove_block(id)?;
193 self.insert(parent, index, vec![block])
194 }
195
196 pub(crate) fn set_kind(
197 &mut self,
198 block: BlockId,
199 kind: BlockKindData,
200 ) -> Result<(), StepError> {
201 if self.doc.kind(block) == Some(&kind) {
202 return Ok(());
203 }
204 self.step(Step::SetKind { block, kind }).map(|_| ())
205 }
206
207 pub(crate) fn normalize(&mut self) -> Result<(), StepError> {
216 loop {
217 let Some(fix) = self.find_violation() else {
218 return Ok(());
219 };
220 match fix {
221 Fix::RemoveEmpty(id) => {
222 self.remove_block(id)?;
223 }
224 Fix::WrapInItem(id) => {
225 let (parent, index) = self
226 .doc
227 .index_in_parent(id)
228 .ok_or(StepError::UnknownBlock(id))?;
229 let block = self.remove_block(id)?;
230 let item = DetachedBlock::container(
231 BlockKindData::ListItem {
232 checked: None,
233 spread: false,
234 },
235 vec![block],
236 );
237 self.insert(parent, index, vec![item])?;
238 }
239 Fix::LeadingParagraph(item) => {
240 self.insert(item, 0, vec![DetachedBlock::paragraph(InlineText::new())])?;
241 }
242 Fix::MergeLists(first, second) => {
243 let count = self.doc.children(second).len();
244 let items = self.remove(second, 0..count)?;
245 let len = self.doc.children(first).len();
246 self.insert(first, len, items)?;
247 self.remove_block(second)?;
248 }
249 Fix::NoLeaf => {
250 let root = self.doc.root;
251 let len = self.doc.children(root).len();
252 self.insert(root, len, vec![DetachedBlock::paragraph(InlineText::new())])?;
253 }
254 }
255 }
256 }
257
258 fn find_violation(&self) -> Option<Fix> {
259 let doc = &*self.doc;
260 let mut stack = vec![doc.root];
261 while let Some(id) = stack.pop() {
262 let entry = doc.get(id)?;
263 if let BlockContent::Children(children) = &entry.content {
264 if children.is_empty() && entry.kind != BlockKindData::Root {
265 return Some(Fix::RemoveEmpty(id));
266 }
267 if let BlockKindData::List { .. } = entry.kind
268 && let Some(stray) = children
269 .iter()
270 .find(|c| !matches!(doc.kind(**c), Some(BlockKindData::ListItem { .. })))
271 {
272 return Some(Fix::WrapInItem(*stray));
273 }
274 if let BlockKindData::ListItem { .. } = entry.kind
275 && !children.first().is_some_and(|c| doc.is_caret_leaf(*c))
276 {
277 return Some(Fix::LeadingParagraph(id));
278 }
279 for pair in children.windows(2) {
280 if let (
281 Some(BlockKindData::List { ordered: a, .. }),
282 Some(BlockKindData::List { ordered: b, .. }),
283 ) = (doc.kind(pair[0]), doc.kind(pair[1]))
284 && a == b
285 {
286 return Some(Fix::MergeLists(pair[0], pair[1]));
287 }
288 }
289 stack.extend(children.iter().copied());
290 }
291 }
292 if doc.walk_leaves().is_empty() {
293 return Some(Fix::NoLeaf);
294 }
295 None
296 }
297
298 pub(crate) fn finish(
299 mut self,
300 origin: EditOrigin,
301 selection_before: BlockSelection,
302 selection_after: BlockSelection,
303 ) -> Result<Transaction, StepError> {
304 self.normalize()?;
305 self.doc.refresh_order();
306 if !self.steps.is_empty() {
307 self.doc.revision += 1;
308 }
309 debug_assert_eq!(self.doc.check_invariants(), Ok(()));
310 let mut inverse = self.inverse;
311 inverse.reverse();
312 Ok(Transaction {
313 steps: self.steps,
314 inverse,
315 maps: self.maps,
316 selection_before,
317 selection_after,
318 origin,
319 })
320 }
321
322 pub(crate) fn rollback(self) {
324 for step in self.inverse.iter().rev() {
325 let _ = step.apply(self.doc);
326 }
327 self.doc.refresh_order();
328 }
329}
330
331enum Fix {
332 RemoveEmpty(BlockId),
333 WrapInItem(BlockId),
334 LeadingParagraph(BlockId),
335 MergeLists(BlockId, BlockId),
336 NoLeaf,
337}