Skip to main content

markdown/
synced.rs

1//! A document held as markdown and as blocks at once.
2
3use 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
13/// A document as markdown and as blocks, each kept up with the other.
14///
15/// Every block owns a range of the text. The ranges partition it in block
16/// order, each running from where the parser starts its block to where it
17/// starts the next, and a block that is not written owns an empty one. Read
18/// back, the text gives the blocks with their texts normalized.
19///
20/// [`Self::set_doc`] rewrites the text of the blocks that changed and leaves
21/// every other byte as it was. [`Self::set_text`] reads again the blocks whose
22/// text changed. Each works on a window of top-level blocks around the change,
23/// read back with the top-level block on either side of it and widened until
24/// those read as they did, up to the whole document.
25pub struct Synced {
26    text: String,
27    doc: Rc<Doc>,
28    /// `doc`'s blocks with their texts normalized: what is written.
29    written: Vec<Block>,
30    layout: Layout,
31    ranges: Vec<Range<usize>>,
32    marks: Marks,
33    /// Whether the text may define a link reference, which every block reads
34    /// wherever it stands, so no window can be read alone.
35    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    /// Each block's range of [`Self::text`], in block order.
63    pub fn ranges(&self) -> &[Range<usize>] {
64        &self.ranges
65    }
66
67    /// The blocks are now `doc`. The blocks that differ from the ones before
68    /// are written again.
69    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(&region, &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    /// Write blocks `changed` alone, with the gap before them in place of
157    /// the one there, and whether that reads back. Nothing is written if not.
158    /// `layout` and [`Self::written`] are the new blocks'; the text and the
159    /// ranges still the old ones, `delta` blocks fewer after the change.
160    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                // The gap ends the block written last, and starts after it.
167                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        // Blocks not written between the last one written and the change own
178        // empty ranges inside the gap, and move with it.
179        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        // The first block written after the change is the one whose reading
195        // the new gap can change.
196        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        // The gap reached back past the block the region starts at.
205        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(&region, &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    /// The text is now `text`. The blocks whose text changed are read again.
218    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    /// Where each of `at` lands in the text, `None` for one this cannot place.
315    ///
316    /// The top-level blocks holding them are written again, with the positions
317    /// in, as [`crate::serialize_at_many`] writes a document: a trailing space
318    /// with a position after it is no longer trailing, and stays.
319    pub fn offsets(&mut self, at: &[Cursor]) -> Vec<Option<usize>> {
320        let n = self.doc.blocks.len();
321        // Windows of blocks and the positions in them, in block order.
322        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        // Back to front, so the text a window has yet to rewrite has not
337        // moved. Each position is kept against its window's start until the
338        // end.
339        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(&region, &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                    // The positions may have changed how the text around them
364                    // is spelled: write it without them, and place each alone.
365                    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                // A wider window takes in every window it now reaches.
394                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    /// Blocks `range` written as [`Self::offsets`] writes them, with the
419    /// positions `which` of `at` in: the text, each block's range of it, and
420    /// where each position landed.
421    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            // No caret sits inside a mention: its text is its preview's.
440            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        // The gaps, and which blocks are written at all, are the blocks' own:
452        // a position is no reason to write a blank paragraph.
453        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                // A position in a block that is not written lands where the
466                // block would have stood.
467                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    /// The position each of `offsets` stands for, `None` for one this cannot
506    /// place — see [`crate::parse_at_many`].
507    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            // A window starts its block at its line where it can, which only
517            // ever moves a start back.
518            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    /// Where windows may begin and end over the text as it stands.
562    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    /// The first top-level block at or after `ix` that is not an ordered item.
568    /// An ordered item takes its number from the run before it, and a run
569    /// starting afresh takes the one it was written with.
570    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    /// Replace `bytes` of the text with `with`, and the ranges of `blocks`
583    /// with `chunks`, relative to the start of `bytes`.
584    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    /// Hand bytes no block was read from to the block before them, or to the
622    /// first block where none is before.
623    /// Only the ranges of `blocks` and their neighbours' edges are looked at.
624    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
639/// Where a window starting at block `ix` starts in `text`: the start of its
640/// line where only spaces stand before it there, else the block's own start.
641/// The text's end for `ix` past the blocks.
642fn 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
655/// Where a window may begin or end, over a layout of blocks whose bytes are
656/// the text's but for blocks `keep..from` of it, which are written afresh in
657/// place of the text's and `delta` more in number.
658///
659/// A block bounds a window when it stands at the top level and its bytes
660/// start a line at its first column. Indented bytes were written for what
661/// stood before them, and read differently after anything else.
662struct 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    /// Block `ix`'s number in the text, `None` for one written afresh.
691    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    /// The bound at or before `ix`, with any block before it that the parser
708    /// started where it starts. Such a block is spelled inside the next one's
709    /// bytes, and whether it stands depends on what follows it.
710    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
739/// Where the gap before block `ix` starts: the newline that ends the last
740/// line holding anything but spaces and quote markers above the block's own.
741/// `None` where the block does not start a line.
742fn 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
757/// `blocks` as the parser would read them back once written.
758fn 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
766/// Carry the newlines `was` ended with onto `out`, the text written for the
767/// last blocks, and into the last chunk.
768fn 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
787/// Equal but for an ordered item's number, which the whole document decides.
788fn 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
797/// Whether `text` may define a link reference, which every block reads
798/// wherever it stands.
799fn 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}