1use std::{ops::Range, rc::Rc};
4
5use crate::{
6 doc::{Block, BlockKind, Doc},
7 marks::Marks,
8 parse::{parse_at_many, parse_ranges_with, parse_with},
9 select::Cursor,
10 serialize::{Layout, sentinel, sentinel_index, write_gap_after, write_one, write_one_as},
11};
12
13pub struct Synced {
26 text: String,
27 doc: Rc<Doc>,
28 written: Vec<Block>,
30 layout: Layout,
31 ranges: Vec<Range<usize>>,
32 marks: Marks,
33 references: bool,
36}
37
38impl Synced {
39 pub fn new(text: impl Into<String>, marks: &Marks) -> Self {
40 let text = text.into();
41 let parsed = parse_ranges_with(&text, marks);
42 let written = parsed.doc.blocks.clone();
43 Self {
44 layout: Layout::of(&written),
45 written,
46 doc: Rc::new(parsed.doc),
47 ranges: parsed.block_ranges,
48 references: defines_references(&text),
49 text,
50 marks: marks.clone(),
51 }
52 }
53
54 pub fn text(&self) -> &str {
55 &self.text
56 }
57
58 pub fn doc(&self) -> &Rc<Doc> {
59 &self.doc
60 }
61
62 pub fn ranges(&self) -> &[Range<usize>] {
64 &self.ranges
65 }
66
67 pub fn set_doc(&mut self, doc: Rc<Doc>) {
70 if Rc::ptr_eq(&self.doc, &doc) {
71 return;
72 }
73 let (old, new) = (&self.doc.blocks, &doc.blocks);
74 let (n_old, n_new) = (old.len(), new.len());
75 let p = old.iter().zip(new).take_while(|(a, b)| a == b).count();
76 if p == n_old && p == n_new {
77 self.doc = doc;
78 return;
79 }
80 let room = n_old.min(n_new) - p;
81 let q = old
82 .iter()
83 .rev()
84 .zip(new.iter().rev())
85 .take(room)
86 .take_while(|(a, b)| a == b)
87 .count();
88 let changed = new[p..n_new - q].iter().cloned().map(|mut block| {
89 block.normalize_text();
90 block
91 });
92 self.written.splice(p..n_old - q, changed);
93 let layout = self
94 .layout
95 .respliced(&self.written, p..n_old - q, n_new - q - p);
96 let delta = n_new as isize - n_old as isize;
97 let old_ix = |ix: usize| (ix as isize - delta) as usize;
98
99 if self.write_changed(&layout, p, n_new - q, delta) {
100 self.doc = doc;
101 self.layout = layout;
102 return;
103 }
104 let mut s = match p {
105 0 => 0,
106 _ => Bounds::new(&layout, &self.ranges, &self.text, p, n_new - q, delta)
107 .at_or_before(p - 1),
108 };
109 let mut e = match n_new - q {
110 end if end == n_new => n_new,
111 end => {
112 Bounds::new(&layout, &self.ranges, &self.text, p, n_new - q, delta).at_or_after(end)
113 }
114 };
115 loop {
116 let start = snap(&self.text, &self.ranges, s);
117 let end = snap(&self.text, &self.ranges, old_ix(e));
118 let mut out = String::new();
119 let mut chunks = Vec::with_capacity(e - s);
120 for ix in s..e {
121 let at = out.len();
122 write_one(&mut out, &self.written, &layout, ix, &self.marks);
123 chunks.push(at..out.len());
124 }
125 if e == n_new {
126 keep_trailing_newlines(&self.text[start..end], &mut out, &mut chunks);
127 }
128 if s == 0 && e == n_new {
129 self.splice(start..end, &out, s..old_ix(e), chunks);
130 break;
131 }
132 let before = match s {
133 0 => 0,
134 _ => Bounds::new(&layout, &self.ranges, &self.text, p, n_new - q, delta)
135 .at_or_before(s - 1),
136 };
137 let after =
138 Bounds::new(&layout, &self.ranges, &self.text, p, n_new - q, delta).after(e);
139 let region = format!(
140 "{}{out}{}",
141 &self.text[snap(&self.text, &self.ranges, before)..start],
142 &self.text[end..snap(&self.text, &self.ranges, old_ix(after))],
143 );
144 if parse_with(®ion, &self.marks)
145 == normalized(&self.written[before..after], &self.marks)
146 {
147 self.splice(start..end, &out, s..old_ix(e), chunks);
148 break;
149 }
150 (s, e) = (before, after);
151 }
152 self.doc = doc;
153 self.layout = layout;
154 }
155
156 fn write_changed(&mut self, layout: &Layout, p: usize, end: usize, delta: isize) -> bool {
161 let old_ix = |ix: usize| (ix as isize - delta) as usize;
162 let n = self.written.len();
163 let previous = (0..p).rev().find(|&ix| layout.at[ix].is_some());
164 let start = match previous {
165 Some(previous) => match gap_start(&self.text, &self.ranges, p) {
166 Some(start) if start > self.ranges[previous].start => start,
168 _ => return false,
169 },
170 None => 0,
171 };
172 let stop = snap(&self.text, &self.ranges, old_ix(end));
173 let mut out = String::new();
174 if let Some(previous) = previous {
175 write_gap_after(&mut out, &self.written, layout, previous);
176 }
177 let first = previous.map_or(0, |previous| previous + 1);
180 let mut chunks = Vec::with_capacity(end - first);
181 for ix in first..end {
182 let at = out.len();
183 write_one(&mut out, &self.written, layout, ix, &self.marks);
184 chunks.push(at..out.len());
185 }
186 if end == n {
187 keep_trailing_newlines(&self.text[start..stop], &mut out, &mut chunks);
188 }
189 let view = Bounds::new(layout, &self.ranges, &self.text, p, end, delta);
190 let before = match p {
191 0 => 0,
192 _ => view.at_or_before(p - 1),
193 };
194 let after = match (end..n).find(|&ix| layout.at[ix].is_some()) {
197 Some(next) => view.after(next),
198 None => n,
199 };
200 let (head, tail) = (
201 snap(&self.text, &self.ranges, before),
202 snap(&self.text, &self.ranges, old_ix(after)),
203 );
204 if head > start || stop > tail {
206 return false;
207 }
208 let region = format!("{}{out}{}", &self.text[head..start], &self.text[stop..tail]);
209 if parse_with(®ion, &self.marks) != normalized(&self.written[before..after], &self.marks)
210 {
211 return false;
212 }
213 self.splice(start..stop, &out, first..old_ix(end), chunks);
214 true
215 }
216
217 pub fn set_text(&mut self, text: &str) {
219 if text == self.text {
220 return;
221 }
222 if self.ranges.is_empty() || self.references || defines_references(text) {
223 *self = Self::new(text, &self.marks);
224 return;
225 }
226 let (old, new) = (self.text.as_bytes(), text.as_bytes());
227 let mut p = old.iter().zip(new).take_while(|(a, b)| a == b).count();
228 while !self.text.is_char_boundary(p) {
229 p -= 1;
230 }
231 let room = old.len().min(new.len()) - p;
232 let mut q = old
233 .iter()
234 .rev()
235 .zip(new.iter().rev())
236 .take(room)
237 .take_while(|(a, b)| a == b)
238 .count();
239 while !self.text.is_char_boundary(old.len() - q) {
240 q -= 1;
241 }
242 let delta = new.len() as isize - old.len() as isize;
243 let shifted = |at: usize| (at as isize + delta) as usize;
244
245 let n = self.ranges.len();
246 let holding = |at: usize| {
247 self.ranges
248 .partition_point(|range| range.start <= at)
249 .max(1)
250 - 1
251 };
252 let mut s = self.bounds().at_or_before(holding(p));
253 let mut e = self.past_numbering(self.bounds().after(holding(old.len() - q)));
254 let (blocks, ranges, start) = loop {
255 let before = match s {
256 0 => 0,
257 _ => self.bounds().at_or_before(s - 1),
258 };
259 let after = match e {
260 e if e == n => n,
261 e => self.bounds().after(e),
262 };
263 let start = snap(&self.text, &self.ranges, before);
264 let end = shifted(snap(&self.text, &self.ranges, after));
265 let parsed = parse_ranges_with(&text[start..end], &self.marks);
266 let (blocks, ranges) = (parsed.doc.blocks, parsed.block_ranges);
267 let (lead, trail) = (s - before, after - e);
268 let fits = blocks.len() >= lead + trail;
269 let leads = fits
270 && (0..lead).all(|ix| {
271 same_but_number(&blocks[ix], &self.doc.blocks[before + ix])
272 && start + ranges[ix].start == self.ranges[before + ix].start
273 });
274 let trails = fits
275 && (0..trail).all(|ix| {
276 let at = blocks.len() - trail + ix;
277 same_but_number(&blocks[at], &self.doc.blocks[e + ix])
278 && start + ranges[at].start == shifted(self.ranges[e + ix].start)
279 });
280 if leads && trails {
281 let keep = lead..blocks.len() - trail;
282 break (blocks[keep.clone()].to_vec(), ranges[keep].to_vec(), start);
283 }
284 if before == 0 && after == n {
285 *self = Self::new(text, &self.marks);
286 return;
287 }
288 if !leads {
289 s = before;
290 }
291 if !trails {
292 e = self.past_numbering(after);
293 }
294 };
295
296 let ranges = ranges
297 .into_iter()
298 .map(|range| start + range.start..start + range.end);
299 let doc = Rc::make_mut(&mut self.doc);
300 doc.blocks.splice(s..e, blocks.iter().cloned());
301 doc.renumber();
302 self.written.splice(s..e, blocks);
303 renumber(&mut self.written);
304 let count = ranges.len();
305 self.ranges.splice(s..e, ranges);
306 for range in &mut self.ranges[s + count..] {
307 *range = shifted(range.start)..shifted(range.end);
308 }
309 self.text = text.to_owned();
310 self.close_gaps(s.saturating_sub(1)..s + count + 1);
311 self.layout = self.layout.respliced(&self.written, s..e, count);
312 }
313
314 pub fn offsets(&mut self, at: &[Cursor]) -> Vec<Option<usize>> {
320 let n = self.doc.blocks.len();
321 let mut windows: Vec<(usize, usize, Vec<usize>)> = Vec::new();
323 let mut order: Vec<usize> = (0..at.len()).filter(|&ix| at[ix].block < n).collect();
324 order.sort_by_key(|&ix| at[ix]);
325 for ix in order {
326 let s = self.bounds().at_or_before(at[ix].block);
327 let e = self.bounds().after(at[ix].block);
328 match windows.last_mut() {
329 Some(last) if s < last.1 => {
330 last.1 = last.1.max(e);
331 last.2.push(ix);
332 }
333 _ => windows.push((s, e, vec![ix])),
334 }
335 }
336 let mut placed: Vec<(usize, usize)> = Vec::new();
340 let mut out = vec![None; at.len()];
341 while let Some((mut s, mut e, mut which)) = windows.pop() {
342 loop {
343 let start = snap(&self.text, &self.ranges, s);
344 let end = snap(&self.text, &self.ranges, e);
345 let whole = s == 0 && e == n;
346 let before = match s {
347 0 => 0,
348 _ => self.bounds().at_or_before(s - 1),
349 };
350 let after = self.bounds().after(e);
351 let expected = normalized(&self.written[before..after], &self.marks);
352 let reads = |written: &str| {
353 let region = format!(
354 "{}{written}{}",
355 &self.text[snap(&self.text, &self.ranges, before)..start],
356 &self.text[end..snap(&self.text, &self.ranges, after)],
357 );
358 parse_with(®ion, &self.marks) == expected
359 };
360 let (mut written, mut chunks, mut found) = self.write_at(s..e, at, &which);
361 let mut settled = reads(&written);
362 if !settled {
363 let (clean, clean_chunks, _) = self.write_at(s..e, at, &[]);
366 if reads(&clean) || whole {
367 found = which
368 .iter()
369 .map(|&ix| {
370 let (one, _, alone) = self.write_at(s..e, at, &[ix]);
371 match alone.first() {
372 Some(&(_, offset)) if one == clean => (ix, offset),
373 _ => (ix, clean_chunks[at[ix].block - s].start),
374 }
375 })
376 .collect();
377 (written, chunks, settled) = (clean, clean_chunks, true);
378 }
379 }
380 if settled {
381 self.splice(start..end, &written, s..e, chunks);
382 placed.retain(|(placed, _)| !which.contains(placed));
383 for &ix in &which {
384 out[ix] = None;
385 }
386 for (ix, offset) in found {
387 placed.push((ix, offset));
388 out[ix] = Some(s);
389 }
390 break;
391 }
392 (s, e) = (before, after);
393 while windows.last().is_some_and(|last| last.1 > s) {
395 let (ws, _, more) = windows.pop().expect("checked");
396 s = s.min(ws);
397 which.extend(more);
398 }
399 which.extend(
400 placed
401 .iter()
402 .filter(|(ix, _)| out[*ix].is_some_and(|ws| ws < e))
403 .map(|(ix, _)| *ix),
404 );
405 which.sort_unstable();
406 which.dedup();
407 for &ix in &which {
408 e = e.max(self.bounds().after(at[ix].block));
409 }
410 }
411 }
412 for (ix, offset) in placed {
413 out[ix] = out[ix].map(|s| snap(&self.text, &self.ranges, s) + offset);
414 }
415 out
416 }
417
418 fn write_at(
422 &self,
423 range: Range<usize>,
424 at: &[Cursor],
425 which: &[usize],
426 ) -> (String, Vec<Range<usize>>, Vec<(usize, usize)>) {
427 let s = range.start;
428 let mut texts = self.doc.blocks[range.clone()].to_vec();
429 let mut placed = Vec::new();
430 let mut order = which.to_vec();
431 order.sort_by_key(|&ix| std::cmp::Reverse(at[ix]));
432 for ix in order {
433 let Some(mark) = sentinel(ix) else { continue };
434 let cursor = at[ix];
435 let Some(text) = texts[cursor.block - s].text_at_mut(cursor.part) else {
436 continue;
437 };
438 let mut offset = cursor.offset.min(text.text.len());
439 if let Some(mention) = text.mention_around(offset) {
441 offset = mention.end;
442 }
443 if text.text.is_char_boundary(offset) {
444 text.insert(offset, mark.encode_utf8(&mut [0; 4]));
445 placed.push(ix);
446 }
447 }
448 for block in &mut texts {
449 block.normalize_text();
450 }
451 let window = texts.len();
454 let mut blocks = self.written[range.clone()].to_vec();
455 let next = (range.end..self.written.len()).find(|&ix| self.layout.at[ix].is_some());
456 blocks.extend(next.map(|ix| self.written[ix].clone()));
457 let layout = Layout::of(&blocks);
458
459 let mut raw = String::new();
460 let mut bounds = Vec::with_capacity(window + 1);
461 let mut unwritten = Vec::new();
462 for (ix, text) in texts.iter().enumerate() {
463 bounds.push(raw.len());
464 if layout.at[ix].is_none() {
465 unwritten.extend(
468 which
469 .iter()
470 .filter(|&&placed| at[placed].block == s + ix)
471 .map(|&placed| (placed, raw.len())),
472 );
473 }
474 write_one_as(&mut raw, text, &blocks, &layout, ix, &self.marks);
475 }
476 bounds.push(raw.len());
477
478 let mut written = String::with_capacity(raw.len());
479 let mut clean = vec![0; raw.len() + 1];
480 let mut found = Vec::new();
481 for (at_raw, c) in raw.char_indices() {
482 clean[at_raw] = written.len();
483 match sentinel_index(c) {
484 Some(ix) if placed.contains(&ix) => found.push((ix, written.len())),
485 _ => written.push(c),
486 }
487 }
488 clean[raw.len()] = written.len();
489 found.extend(
490 unwritten
491 .into_iter()
492 .map(|(ix, at_raw)| (ix, clean[at_raw])),
493 );
494 let mut chunks: Vec<Range<usize>> = bounds
495 .windows(2)
496 .map(|pair| clean[pair[0]]..clean[pair[1]])
497 .collect();
498 if range.end == self.ranges.len() {
499 let start = snap(&self.text, &self.ranges, s);
500 keep_trailing_newlines(&self.text[start..], &mut written, &mut chunks);
501 }
502 (written, chunks, found)
503 }
504
505 pub fn cursors(&self, offsets: &[usize]) -> Vec<Option<Cursor>> {
508 let n = self.ranges.len();
509 if n == 0 || self.references {
510 return parse_at_many(&self.text, offsets, &self.marks).1;
511 }
512 let start_of = |ix: usize| snap(&self.text, &self.ranges, ix);
513 let mut out = vec![None; offsets.len()];
514 for (ix, &offset) in offsets.iter().enumerate() {
515 let offset = offset.min(self.text.len());
516 let mut block = self
519 .ranges
520 .partition_point(|range| range.start <= offset)
521 .max(1)
522 - 1;
523 if block + 1 < n && start_of(block + 1) <= offset {
524 block += 1;
525 }
526 let mut s = self.bounds().at_or_before(block);
527 let mut e = self.bounds().after(block);
528 out[ix] = loop {
529 let (start, end) = (start_of(s), start_of(e));
530 let (doc, found) =
531 parse_at_many(&self.text[start..end], &[offset - start], &self.marks);
532 let reads = found[0].is_some()
533 && doc.blocks.len() == e - s
534 && doc
535 .blocks
536 .iter()
537 .zip(&self.doc.blocks[s..e])
538 .all(|(a, b)| same_but_number(a, b));
539 if reads {
540 break found[0].map(|cursor| Cursor {
541 block: cursor.block + s,
542 ..cursor
543 });
544 }
545 if s == 0 && e == n {
546 break parse_at_many(&self.text, &[offset], &self.marks).1[0];
547 }
548 s = match s {
549 0 => 0,
550 _ => self.bounds().at_or_before(s - 1),
551 };
552 e = match e {
553 e if e == n => n,
554 e => self.bounds().after(e),
555 };
556 };
557 }
558 out
559 }
560
561 fn bounds(&self) -> Bounds<'_> {
563 let n = self.ranges.len();
564 Bounds::new(&self.layout, &self.ranges, &self.text, n, n, 0)
565 }
566
567 fn past_numbering(&self, mut ix: usize) -> usize {
571 while self
572 .doc
573 .blocks
574 .get(ix)
575 .is_some_and(|block| matches!(block.kind, BlockKind::Ordered { .. }))
576 {
577 ix = self.bounds().after(ix);
578 }
579 ix
580 }
581
582 fn splice(
585 &mut self,
586 bytes: Range<usize>,
587 with: &str,
588 blocks: Range<usize>,
589 chunks: Vec<Range<usize>>,
590 ) {
591 let start = bytes.start;
592 let shift = with.len() as isize - bytes.len() as isize;
593 self.text.replace_range(bytes.clone(), with);
594 let written = chunks.len();
595 self.ranges.splice(
596 blocks.clone(),
597 chunks
598 .into_iter()
599 .map(|range| start + range.start..start + range.end),
600 );
601 let tail = blocks.start + written;
602 for range in &mut self.ranges[tail..] {
603 *range = (range.start as isize + shift) as usize..(range.end as isize + shift) as usize;
604 }
605 self.close_gaps(blocks.start.saturating_sub(1)..tail + 1);
606 debug_assert!(
607 self.ranges
608 .windows(2)
609 .all(|pair| pair[0].end == pair[1].start)
610 && self
611 .ranges
612 .last()
613 .is_none_or(|last| last.end == self.text.len())
614 && self.ranges.iter().all(|range| range.start <= range.end),
615 "ranges {:?} over {} bytes after {bytes:?} {blocks:?}",
616 self.ranges,
617 self.text.len()
618 );
619 }
620
621 fn close_gaps(&mut self, blocks: Range<usize>) {
625 let len = self.text.len();
626 let n = self.ranges.len();
627 for ix in blocks.start.max(1)..(blocks.end + 1).min(n) {
628 self.ranges[ix - 1].end = self.ranges[ix].start;
629 }
630 if let Some(first) = self.ranges.first_mut() {
631 first.start = 0;
632 }
633 if let Some(last) = self.ranges.last_mut() {
634 last.end = len;
635 }
636 }
637}
638
639fn snap(text: &str, ranges: &[Range<usize>], ix: usize) -> usize {
643 let Some(range) = ranges.get(ix) else {
644 return text.len();
645 };
646 let at = range.start;
647 let line = text[..at].rfind('\n').map_or(0, |newline| newline + 1);
648 let floor = ix.checked_sub(1).map_or(0, |before| ranges[before].start);
649 match line >= floor && text[line..at].bytes().all(|b| b == b' ' || b == b'\t') {
650 true => line,
651 false => at,
652 }
653}
654
655struct Bounds<'a> {
663 layout: &'a Layout,
664 ranges: &'a [Range<usize>],
665 text: &'a str,
666 keep: usize,
667 from: usize,
668 delta: isize,
669}
670
671impl<'a> Bounds<'a> {
672 fn new(
673 layout: &'a Layout,
674 ranges: &'a [Range<usize>],
675 text: &'a str,
676 keep: usize,
677 from: usize,
678 delta: isize,
679 ) -> Self {
680 Self {
681 layout,
682 ranges,
683 text,
684 keep,
685 from,
686 delta,
687 }
688 }
689
690 fn old(&self, ix: usize) -> Option<usize> {
692 match ix {
693 ix if ix < self.keep => Some(ix),
694 ix if ix >= self.from => Some((ix as isize - self.delta) as usize),
695 _ => None,
696 }
697 }
698
699 fn bounds(&self, ix: usize) -> bool {
700 self.layout.top(ix)
701 && self.old(ix).is_none_or(|old| {
702 let at = snap(self.text, self.ranges, old);
703 !matches!(self.text.as_bytes().get(at), Some(b' ' | b'\t'))
704 })
705 }
706
707 fn at_or_before(&self, ix: usize) -> usize {
711 let mut top = (0..=ix.min(self.layout.at.len().saturating_sub(1)))
712 .rev()
713 .find(|&ix| self.bounds(ix))
714 .unwrap_or(0);
715 while top > 0 {
716 let glued = self
717 .old(top)
718 .zip(self.old(top - 1))
719 .and_then(|(at, before)| Some((self.ranges.get(at)?, self.ranges.get(before)?)))
720 .is_some_and(|(at, before)| before.start == at.start);
721 if !glued {
722 break;
723 }
724 top -= 1;
725 }
726 top
727 }
728
729 fn at_or_after(&self, ix: usize) -> usize {
730 let n = self.layout.at.len();
731 (ix..n).find(|&ix| self.bounds(ix)).unwrap_or(n)
732 }
733
734 fn after(&self, ix: usize) -> usize {
735 self.at_or_after(ix + 1)
736 }
737}
738
739fn gap_start(text: &str, ranges: &[Range<usize>], ix: usize) -> Option<usize> {
743 let mut at = snap(text, ranges, ix);
744 if at == 0 || text.as_bytes()[at - 1] != b'\n' {
745 return None;
746 }
747 loop {
748 let end = at - 1;
749 let line = text[..end].rfind('\n').map_or(0, |newline| newline + 1);
750 if line == 0 || !text[line..end].trim_matches([' ', '\t', '>']).is_empty() {
751 return Some(end);
752 }
753 at = line;
754 }
755}
756
757fn normalized(blocks: &[Block], marks: &Marks) -> Doc {
759 let mut doc = Doc {
760 blocks: blocks.to_vec(),
761 };
762 doc.normalize_with(marks);
763 doc
764}
765
766fn keep_trailing_newlines(was: &str, out: &mut String, chunks: &mut [Range<usize>]) {
769 let trail = was.len() - was.trim_end_matches('\n').len();
770 let have = out.len() - out.trim_end_matches('\n').len();
771 for _ in have..trail {
772 out.push('\n');
773 }
774 if let Some(last) = chunks.last_mut() {
775 last.end = out.len();
776 }
777}
778
779fn renumber(blocks: &mut Vec<Block>) {
780 let mut doc = Doc {
781 blocks: std::mem::take(blocks),
782 };
783 doc.renumber();
784 *blocks = doc.blocks;
785}
786
787fn same_but_number(a: &Block, b: &Block) -> bool {
789 match (&a.kind, &b.kind) {
790 (BlockKind::Ordered { text: x, .. }, BlockKind::Ordered { text: y, .. }) => {
791 x == y && a.indent == b.indent && a.quote == b.quote
792 }
793 _ => a == b,
794 }
795}
796
797fn defines_references(text: &str) -> bool {
800 text.contains("]:")
801 && text.lines().any(|line| {
802 let line = line.trim_start_matches(['>', ' ']);
803 line.starts_with('[') && line.contains("]:")
804 })
805}