1use crate::block_parser::{Blocks, TopLevel};
6use crate::definitions::{Definitions, Seed};
7use crate::event_parser::EventParser;
8use crate::stream::{Event, EventKind, Tag, TagKind};
9use crate::ParseOptions;
10use alloc::string::String;
11use alloc::vec::Vec;
12
13#[derive(Debug)]
48pub struct IncrementalParser<'a> {
49 source: String,
50 options: &'a ParseOptions,
51 seed: Seed,
53 anchor: usize,
55 emitted: usize,
57 complete: usize,
59 stable: usize,
60 events: Vec<Event<'static>>,
61 finished: bool,
62}
63
64impl<'a> IncrementalParser<'a> {
65 pub fn new(options: &'a ParseOptions) -> Self {
67 Self {
68 source: String::new(),
69 options,
70 seed: Seed::default(),
71 anchor: 0,
72 emitted: 0,
73 complete: 0,
74 stable: 0,
75 events: Vec::new(),
76 finished: false,
77 }
78 }
79
80 pub fn source(&self) -> &str {
82 &self.source
83 }
84
85 pub fn stable_offset(&self) -> usize {
87 self.stable
88 }
89
90 pub fn push(&mut self, text: &str) -> impl Iterator<Item = Event<'static>> + '_ {
100 assert!(!self.finished, "cannot push after finishing");
101 let from = self.source.len().saturating_sub(1).max(self.complete);
103 self.source.push_str(text);
104 let complete = complete_lines(&self.source, from);
105 if complete > self.complete {
106 self.complete = complete;
107 self.prepare();
108 }
109 self.events.drain(..)
110 }
111
112 pub fn finish(&mut self) -> impl Iterator<Item = Event<'static>> + '_ {
118 assert!(!self.finished, "cannot finish twice");
119 self.finished = true;
120 let (blocks, definitions, outline) = Blocks::parse_from(
121 &self.source,
122 self.anchor,
123 self.options,
124 Definitions::seeded(&self.seed),
125 );
126 let parser = EventParser::from_blocks(&self.source, self.options, blocks, definitions);
127 deliver(
128 parser,
129 &outline.blocks,
130 self.emitted,
131 None,
132 &mut self.events,
133 );
134 self.stable = self.source.len();
135 self.events.drain(..)
136 }
137
138 fn prepare(&mut self) {
139 let source = &self.source[..self.complete];
140 if self.anchor == 0
141 && self.emitted == 0
142 && Blocks::frontmatter_pending(source, self.options)
143 {
144 return;
145 }
146 let (blocks, definitions, outline) = Blocks::parse_from(
147 source,
148 self.anchor,
149 self.options,
150 Definitions::seeded(&self.seed),
151 );
152 let tops = &outline.blocks;
153 let last = if outline.closed {
155 tops.len().checked_sub(1)
156 } else {
157 (1..tops.len())
158 .rev()
159 .find(|&next| tops[next].clean)
160 .map(|next| next - 1)
161 };
162 let Some(last) = last.filter(|&last| last >= self.emitted) else {
163 return;
164 };
165 let anchor = (0..=last)
167 .rev()
168 .find(|&index| tops[index].clean)
169 .unwrap_or(0);
170 let authored = definitions.before(tops[anchor].line);
171 let parser = EventParser::from_blocks(source, self.options, blocks, definitions);
172 deliver(parser, tops, self.emitted, Some(last), &mut self.events);
173 self.stable = tops[last].end;
174 self.seed.append(authored);
175 self.anchor = tops[anchor].line;
176 self.emitted = last - anchor + 1;
177 }
178}
179
180fn complete_lines(source: &str, from: usize) -> usize {
182 let bytes = source.as_bytes();
183 let end = bytes.len() - usize::from(bytes.last() == Some(&b'\r'));
184 bytes[from.min(end)..end]
185 .iter()
186 .rposition(|&byte| matches!(byte, b'\n' | b'\r'))
187 .map_or(0, |index| from + index + 1)
188}
189
190fn deliver(
192 mut parser: EventParser<'_>,
193 tops: &[TopLevel],
194 skip: usize,
195 until: Option<usize>,
196 output: &mut Vec<Event<'static>>,
197) {
198 let mut block = 0;
199 let mut depth = 0usize;
200 let mut separators = Vec::new();
201 loop {
202 if depth == 0 {
203 if until.is_some_and(|until| block > until) {
204 return;
205 }
206 parser.set_definition_limit(Some(tops.get(block).map_or(usize::MAX, |top| top.end)));
207 }
208 let Some(Ok(event)) = parser.next() else {
209 break;
210 };
211 match &event.kind {
212 EventKind::Start(Tag::Document(_)) | EventKind::End(TagKind::Document) => continue,
213 EventKind::Separator { .. } if depth == 0 => {
214 separators.push(event);
215 continue;
216 }
217 _ => {}
218 }
219 if depth == 0 {
220 if block >= skip {
221 output.append(&mut separators);
222 } else {
223 separators.clear();
224 }
225 }
226 match event.kind {
227 EventKind::Start(_) => depth += 1,
228 EventKind::End(_) => depth -= 1,
229 _ => {}
230 }
231 if block >= skip {
232 output.push(event);
233 }
234 if depth == 0 {
235 block += 1;
236 }
237 }
238 output.append(&mut separators);
240}
241
242#[cfg(test)]
243mod tests {
244 use super::{complete_lines, deliver, IncrementalParser};
245 use crate::block_parser::Blocks;
246 use crate::definitions::Definitions;
247 use crate::event_parser::EventParser;
248 use crate::stream::Event;
249 use crate::{Constructs, ParseOptions};
250 use alloc::string::String;
251 use alloc::vec::Vec;
252 use core::convert::TryFrom;
253
254 fn scoped(source: &str, options: &ParseOptions) -> Vec<Event<'static>> {
256 let (blocks, definitions, outline) =
257 Blocks::parse_from(source, 0, options, Definitions::default());
258 let parser = EventParser::from_blocks(source, options, blocks, definitions);
259 let mut events = Vec::new();
260 deliver(parser, &outline.blocks, 0, None, &mut events);
261 events
262 }
263
264 fn incremental<'a>(
265 pieces: impl IntoIterator<Item = &'a str>,
266 options: &ParseOptions,
267 ) -> Vec<Event<'static>> {
268 let mut parser = IncrementalParser::new(options);
269 let mut events = Vec::new();
270 for piece in pieces {
271 events.extend(parser.push(piece));
272 }
273 events.extend(parser.finish());
274 events
275 }
276
277 #[test]
278 fn definitions_resolve_as_in_a_scoped_one_shot_parse() {
279 const LINES: &[&str] = &[
280 "",
281 "[a] [b] [^n] [c][a] [A]",
282 "text [b]",
283 "[a]: /a",
284 "[A]: /upper",
285 "[b]: /b 'title",
286 "continued'",
287 "[c]:",
288 "/c",
289 "[^n]: note [a]",
290 " indented [b]",
291 "- [a]: /item",
292 "> [b]: /quote",
293 "- item [a]",
294 "> quote [b]",
295 "---",
296 "===",
297 "# [a]",
298 "|-|",
299 "| [a] | [b] |",
300 "```",
301 "<div>",
302 ];
303 let mut options = ParseOptions::gfm();
304 options.constructs = Constructs {
305 frontmatter: true,
306 ..Constructs::gfm()
307 };
308 let mut state = 0x9e37_79b9_7f4a_7c15_u64;
309 let mut random = move || {
310 state ^= state << 13;
311 state ^= state >> 7;
312 state ^= state << 17;
313 usize::from(u16::try_from(state >> 48).unwrap())
314 };
315 for _ in 0..3000 {
316 let mut source = String::new();
317 for _ in 0..(random() % 14) {
318 source.push_str(LINES[random() % LINES.len()]);
319 source.push_str(if random() % 4 == 0 { "\r\n" } else { "\n" });
320 }
321 let expected = scoped(&source, &options);
322 let characters = source
323 .char_indices()
324 .map(|(index, character)| &source[index..index + character.len_utf8()]);
325 assert_eq!(incremental(characters, &options), expected, "{source:?}");
326 let mut cuts: Vec<usize> = (0..4).map(|_| random() % (source.len() + 1)).collect();
327 cuts.sort_unstable();
328 let mut previous = 0;
329 let mut pieces = Vec::new();
330 for cut in cuts.into_iter().chain([source.len()]) {
331 pieces.push(&source[previous..cut]);
332 previous = cut;
333 }
334 assert_eq!(incremental(pieces, &options), expected, "{source:?}");
335 }
336 }
337
338 #[test]
339 fn complete_lines_exclude_a_trailing_carriage_return() {
340 assert_eq!(complete_lines("", 0), 0);
341 assert_eq!(complete_lines("a", 0), 0);
342 assert_eq!(complete_lines("a\n", 0), 2);
343 assert_eq!(complete_lines("a\nb", 0), 2);
344 assert_eq!(complete_lines("a\r", 0), 0);
345 assert_eq!(complete_lines("a\r\r", 0), 2);
346 assert_eq!(complete_lines("a\r\n", 0), 3);
347 assert_eq!(complete_lines("a\rb", 0), 2);
348 assert_eq!(complete_lines("a\nbc", 2), 0);
349 assert_eq!(complete_lines("a\nb\r\n", 3), 5);
350 }
351
352 #[test]
353 fn preparation_restarts_near_the_end_of_closed_blocks() {
354 let options = ParseOptions::gfm();
355 let mut parser = IncrementalParser::new(&options);
356 for index in 0..200 {
357 let block = match index % 4 {
358 0 => "# Heading\n\n",
359 1 => "A paragraph with *emphasis*.\n\n",
360 2 => "- a list\n- of items\n\n",
361 _ => "```\ncode\n```\n\n",
362 };
363 for character in block.split_inclusive(' ') {
364 let _ = parser.push(character);
365 }
366 assert!(parser.anchor <= parser.stable_offset());
368 assert!(parser.source.len() - parser.anchor <= 64);
369 }
370 }
371}