1use crate::block::BlockError;
54use crate::diff::{ApplyError, Change};
55use crate::node::{Mark, Node};
56use crate::normalize::{normalize_children, NormalizeOptions};
57use crate::pos::PosError;
58use crate::range::{ensure_boundary, resolve_range, Position, Range, RangeError};
59use serde::{Deserialize, Serialize};
60use serde_json::{Map, Value};
61use std::fmt;
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65#[serde(
66 tag = "type",
67 rename_all = "camelCase",
68 rename_all_fields = "camelCase"
69)]
70pub enum PosContent {
71 Text {
73 text: String,
75 #[serde(skip_serializing_if = "Option::is_none", default)]
77 marks: Option<Vec<Mark>>,
78 },
79 Nodes {
81 nodes: Vec<Node>,
83 },
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88#[serde(
89 tag = "type",
90 rename_all = "camelCase",
91 rename_all_fields = "camelCase"
92)]
93pub enum PosEdit {
94 Insert {
96 pos: usize,
98 content: PosContent,
100 },
101 Delete {
103 from: usize,
105 to: usize,
107 },
108 Replace {
110 from: usize,
112 to: usize,
114 content: PosContent,
116 },
117 AddMark {
119 from: usize,
121 to: usize,
123 mark: Mark,
125 },
126 RemoveMark {
128 from: usize,
130 to: usize,
132 mark_type: String,
134 },
135 SetBlockAttrs {
137 pos: usize,
139 attrs: Map<String, Value>,
141 },
142}
143
144impl PosEdit {
145 fn span(&self) -> (usize, usize) {
147 match self {
148 PosEdit::Insert { pos, .. } | PosEdit::SetBlockAttrs { pos, .. } => (*pos, *pos),
149 PosEdit::Delete { from, to }
150 | PosEdit::Replace { from, to, .. }
151 | PosEdit::AddMark { from, to, .. }
152 | PosEdit::RemoveMark { from, to, .. } => (*from, *to),
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq)]
159pub enum PosEditError {
160 Pos(PosError),
162 Range(RangeError),
164 Block(BlockError),
166 Apply(ApplyError),
168 UnsupportedSpan {
171 from: usize,
173 to: usize,
175 },
176 OverlappingEdits {
178 from: usize,
180 to: usize,
182 },
183}
184
185impl From<PosError> for PosEditError {
186 fn from(e: PosError) -> Self {
187 PosEditError::Pos(e)
188 }
189}
190impl From<RangeError> for PosEditError {
191 fn from(e: RangeError) -> Self {
192 PosEditError::Range(e)
193 }
194}
195impl From<BlockError> for PosEditError {
196 fn from(e: BlockError) -> Self {
197 PosEditError::Block(e)
198 }
199}
200impl From<ApplyError> for PosEditError {
201 fn from(e: ApplyError) -> Self {
202 PosEditError::Apply(e)
203 }
204}
205
206impl fmt::Display for PosEditError {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 match self {
209 PosEditError::Pos(e) => write!(f, "pos-edit: {e}"),
210 PosEditError::Range(e) => write!(f, "pos-edit: {e}"),
211 PosEditError::Block(e) => write!(f, "pos-edit: {e}"),
212 PosEditError::Apply(e) => write!(f, "pos-edit: {e}"),
213 PosEditError::UnsupportedSpan { from, to } => {
214 write!(f, "pos-edit: unsupported cross-block span [{from},{to})")
215 }
216 PosEditError::OverlappingEdits { from, to } => {
217 write!(f, "pos-edit: overlapping edit at [{from},{to})")
218 }
219 }
220 }
221}
222
223impl std::error::Error for PosEditError {}
224
225impl Node {
226 pub fn apply_pos_edits(&mut self, edits: &[PosEdit]) -> Result<Vec<Change>, PosEditError> {
234 for e in edits {
238 let (lo, hi) = e.span();
239 if lo > hi {
240 return Err(PosEditError::Range(RangeError::InvertedRange));
241 }
242 }
243
244 let mut order: Vec<usize> = (0..edits.len()).collect();
246 order.sort_by(|&a, &b| edits[b].span().0.cmp(&edits[a].span().0));
247
248 for k in 1..order.len() {
251 let higher = edits[order[k - 1]].span();
252 let lower = edits[order[k]].span();
253 if lower.1 > higher.0 {
254 return Err(PosEditError::OverlappingEdits {
255 from: lower.0,
256 to: lower.1,
257 });
258 }
259 }
260
261 let mut work = self.clone();
262 for &i in &order {
263 apply_one(&mut work, &edits[i])?;
264 }
265 let patch = self.diff(&work);
266 *self = work;
267 Ok(patch)
268 }
269}
270
271fn block_mut<'a>(root: &'a mut Node, path: &[usize]) -> Result<&'a mut Node, PosEditError> {
274 root.node_at_mut(path).ok_or_else(|| {
275 PosEditError::Pos(PosError::PathNotFound {
276 path: path.to_vec(),
277 })
278 })
279}
280
281fn apply_one(work: &mut Node, edit: &PosEdit) -> Result<(), PosEditError> {
282 match edit {
283 PosEdit::Insert { pos, content } => insert_at(work, *pos, content),
284 PosEdit::Delete { from, to } => splice(work, *from, *to, None),
285 PosEdit::Replace { from, to, content } => splice(work, *from, *to, Some(content)),
286 PosEdit::AddMark { from, to, mark } => {
287 mark_span(work, *from, *to, &MarkOp::Add(mark.clone()))
288 }
289 PosEdit::RemoveMark {
290 from,
291 to,
292 mark_type,
293 } => mark_span(work, *from, *to, &MarkOp::Remove(mark_type.clone())),
294 PosEdit::SetBlockAttrs { pos, attrs } => set_block_attrs(work, *pos, attrs.clone()),
295 }
296}
297
298fn insert_at(work: &mut Node, pos: usize, content: &PosContent) -> Result<(), PosEditError> {
299 match content {
300 PosContent::Text { text, marks } => {
301 let (block, inline) = work.pos_to_inline(pos)?;
302 block_mut(work, &block)?.insert_text(inline, text, marks.as_deref())?;
303 Ok(())
304 }
305 PosContent::Nodes { nodes } => {
306 let r = work.resolve(pos)?;
307 let (block_path, idx) = match &r.text_offset {
308 Some(tp) => {
310 let bp = r.path.clone();
311 let i = ensure_boundary(
312 block_mut(work, &bp)?.children_mut(),
313 Position::new(r.index, tp.offset),
314 )?;
315 (bp, i)
316 }
317 None => (r.path.clone(), r.index),
318 };
319 let parent = block_mut(work, &block_path)?;
320 for (k, n) in nodes.iter().enumerate() {
321 parent.insert_child(idx + k, n.clone());
322 }
323 Ok(())
324 }
325 }
326}
327
328fn splice(
329 work: &mut Node,
330 from: usize,
331 to: usize,
332 content: Option<&PosContent>,
333) -> Result<(), PosEditError> {
334 let (fb, fi) = work.pos_to_inline(from)?;
335 let (tb, ti) = work.pos_to_inline(to)?;
336
337 if fb == tb {
338 return splice_same_block(block_mut(work, &fb)?, fi, ti, content);
339 }
340
341 let (parent, a, b) =
343 sibling_blocks(&fb, &tb).ok_or(PosEditError::UnsupportedSpan { from, to })?;
344
345 {
347 let block_a = block_mut(work, &fb)?;
348 let end = Position::new(block_a.children().len(), 0);
349 block_a.delete_range(Range::new(fi, end))?;
350 append_content(block_a, content)?;
351 }
352 block_mut(work, &tb)?.delete_range(Range::new(Position::new(0, 0), ti))?;
354 block_mut(work, &parent)?.children_mut().drain(a + 1..b);
356 work.join_blocks(&parent, a + 1)?;
357 Ok(())
358}
359
360fn splice_same_block(
361 block: &mut Node,
362 from: Position,
363 to: Position,
364 content: Option<&PosContent>,
365) -> Result<(), PosEditError> {
366 match content {
367 None => block.delete_range(Range::new(from, to))?,
368 Some(PosContent::Text { text, marks }) => {
369 block.replace_range(Range::new(from, to), text, marks.as_deref())?
370 }
371 Some(PosContent::Nodes { nodes }) => {
372 let children = block.children_mut();
373 let (s, e) = resolve_range(children, Range::new(from, to))?;
374 children.drain(s..e);
375 for (k, n) in nodes.iter().enumerate() {
376 children.insert(s + k, n.clone());
377 }
378 normalize_children(children, &NormalizeOptions::default());
379 }
380 }
381 Ok(())
382}
383
384fn append_content(block: &mut Node, content: Option<&PosContent>) -> Result<(), PosEditError> {
387 match content {
388 None => {}
389 Some(PosContent::Text { text, marks }) => {
390 let at = Position::new(block.children().len(), 0);
391 block.insert_text(at, text, marks.as_deref())?;
392 }
393 Some(PosContent::Nodes { nodes }) => {
394 let at = block.children().len();
395 for (k, n) in nodes.iter().enumerate() {
396 block.insert_child(at + k, n.clone());
397 }
398 }
399 }
400 Ok(())
401}
402
403enum MarkOp {
404 Add(Mark),
405 Remove(String),
406}
407
408fn apply_mark(block: &mut Node, range: Range, op: &MarkOp) -> Result<(), RangeError> {
409 match op {
410 MarkOp::Add(m) => block.add_mark_range(range, m.clone()),
411 MarkOp::Remove(t) => block.remove_mark_range(range, t),
412 }
413}
414
415fn mark_span(work: &mut Node, from: usize, to: usize, op: &MarkOp) -> Result<(), PosEditError> {
416 let (fb, fi) = work.pos_to_inline(from)?;
417 let (tb, ti) = work.pos_to_inline(to)?;
418
419 if fb == tb {
420 apply_mark(block_mut(work, &fb)?, Range::new(fi, ti), op)?;
421 return Ok(());
422 }
423
424 let (parent, a, b) =
425 sibling_blocks(&fb, &tb).ok_or(PosEditError::UnsupportedSpan { from, to })?;
426
427 {
429 let block_a = block_mut(work, &fb)?;
430 let end = Position::new(block_a.children().len(), 0);
431 apply_mark(block_a, Range::new(fi, end), op)?;
432 }
433 for k in (a + 1)..b {
435 let mut p = parent.clone();
436 p.push(k);
437 let blk = block_mut(work, &p)?;
438 let end = Position::new(blk.children().len(), 0);
439 apply_mark(blk, Range::new(Position::new(0, 0), end), op)?;
440 }
441 apply_mark(
443 block_mut(work, &tb)?,
444 Range::new(Position::new(0, 0), ti),
445 op,
446 )?;
447 Ok(())
448}
449
450fn set_block_attrs(
451 work: &mut Node,
452 pos: usize,
453 attrs: Map<String, Value>,
454) -> Result<(), PosEditError> {
455 let r = work.resolve(pos)?;
456 let mut target = r.path.clone();
462 if r.text_offset.is_none() {
463 let parent = block_mut(work, &r.path)?;
464 let descend = parent
465 .children()
466 .get(r.index)
467 .is_some_and(|c| c.node_type.as_deref() != Some("text"));
468 if descend {
469 target.push(r.index);
470 }
471 }
472 let node = block_mut(work, &target)?;
473 node.attrs = if attrs.is_empty() { None } else { Some(attrs) };
474 Ok(())
475}
476
477fn sibling_blocks(fb: &[usize], tb: &[usize]) -> Option<(Vec<usize>, usize, usize)> {
480 let ((&a, fp), (&b, tp)) = (fb.split_last()?, tb.split_last()?);
481 if fp == tp && a < b {
482 Some((fp.to_vec(), a, b))
483 } else {
484 None
485 }
486}