rustla/parser/state_machine/
body.rs

1/*!
2This module contains the transition functions related to `State::Body`.
3
4Copyright © 2020 Santtu Söderholm
5*/
6
7use super::*;
8use crate::parser::types_and_aliases::IndentedBlockResult;
9
10/// The transition method for matching bullets in `Body` state.
11/// Causes the parser to push a new machine in the state
12/// `BulletList` on top of its machine stack. Leaves the reponsibility
13/// of the actual parsing to that state.
14pub fn bullet(
15    src_lines: &Vec<String>,
16    base_indent: usize,
17    section_level: &mut usize,
18    line_cursor: &mut LineCursor,
19    mut doctree: DocTree,
20    captures: &regex::Captures,
21    pattern_name: &Pattern,
22) -> TransitionResult {
23
24    let detected_bullet = captures.get(2).unwrap().as_str().chars().next().unwrap();
25    let detected_bullet_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
26    let detected_text_indent = captures.get(0).unwrap().as_str().chars().count() + base_indent;
27
28    let sublist_data = TreeNodeType::BulletList {
29        bullet: detected_bullet,
30        bullet_indent: detected_bullet_indent,
31        text_indent: detected_text_indent,
32    };
33
34    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_bullet_indent) {
35        IndentationMatch::JustRight => {
36            doctree = match doctree.push_data_and_focus(sublist_data) {
37                Ok(tree) => tree,
38                Err(tree) => {
39                    return TransitionResult::Failure {
40                        message: format!(
41                            "Node insertion error on line {}. Computer says no...",
42                            line_cursor.sum_total()
43                        ),
44                        doctree: tree,
45                    }
46                }
47            };
48            return TransitionResult::Success {
49                doctree: doctree,
50                push_or_pop: PushOrPop::Push(vec![State::BulletList]),
51                line_advance: LineAdvance::None,
52            };
53        }
54        IndentationMatch::TooMuch => {
55            doctree = match doctree.push_data_and_focus(TreeNodeType::BlockQuote {
56                body_indent: detected_bullet_indent,
57            }) {
58                Ok(tree) => tree,
59                Err(tree) => {
60                    return TransitionResult::Failure {
61                        message: format!(
62                            "Node insertion error on line {}. Computer says no...",
63                            line_cursor.sum_total()
64                        ),
65                        doctree: tree,
66                    }
67                }
68            };
69            return TransitionResult::Success {
70                doctree: doctree,
71                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
72                line_advance: LineAdvance::None,
73            };
74        }
75        _ => {
76            doctree = doctree.focus_on_parent();
77            return TransitionResult::Success {
78                doctree: doctree,
79                push_or_pop: PushOrPop::Pop,
80                line_advance: LineAdvance::None,
81            };
82        }
83    }
84}
85
86/// Transition method for matching enumerators in the `Body` state.
87/// Attempts to create a new enumerated list node and focus on it,
88/// while at the same time pushing a new `EnumeratedList` state on
89/// top of the parser machine stack.
90///
91/// This does not yet parse the first detected list item.
92/// That responsibility is on the corresponding enumerator method
93/// of the `EnumeratedList` state.
94pub fn enumerator(
95    src_lines: &Vec<String>,
96    base_indent: usize,
97    section_level: &mut usize,
98    line_cursor: &mut LineCursor,
99    mut doctree: DocTree,
100    captures: &regex::Captures,
101    pattern_name: &Pattern,
102) -> TransitionResult {
103
104    let detected_enumerator_indent =
105        captures.get(1).unwrap().as_str().chars().count() + base_indent;
106    let detected_text_indent = captures.get(0).unwrap().as_str().chars().count() + base_indent;
107    // let detected_enum_str = captures.get(2).unwrap().as_str();
108
109    let (detected_number, detected_kind, detected_delims) =
110        match converters::enum_captures_to_int_kind_and_delims(
111            &captures,
112            None,
113            false,
114            false,
115            0,
116            1
117        ) {
118            Some((number, kind, delims)) => (number, kind, delims),
119            None => return TransitionResult::Failure {
120                message: format!(
121                    "Could not convert a list enumerator to number on line {}. Computer says no...",
122                    line_cursor.sum_total()
123                ),
124                doctree: doctree,
125            },
126        };
127
128    // Ceck validity of list item
129    if ! Parser::is_enumerated_list_item(
130        src_lines,
131        line_cursor,
132        captures,
133        section_level,
134        base_indent,
135        detected_enumerator_indent,
136        detected_number,
137        detected_kind,
138        detected_delims,
139        pattern_name,
140        &detected_kind,
141        false,
142        0,
143        1,
144    ) {
145        return text(src_lines, base_indent, section_level, line_cursor, doctree, captures, pattern_name)
146    };
147
148    let list_node_data = TreeNodeType::EnumeratedList {
149        delims: detected_delims,
150        kind: detected_kind,
151        start_index: detected_number, //detected_enum_as_usize,
152        n_of_items: 0,
153        enumerator_indent: detected_enumerator_indent,
154    };
155
156    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_enumerator_indent) {
157        IndentationMatch::JustRight => {
158            doctree = match doctree.push_data_and_focus(list_node_data) {
159                Ok(tree) => tree,
160                Err(tree) => {
161                    return TransitionResult::Failure {
162                        message: format!(
163                            "Node insertion error on line {}. Computer says no...",
164                            line_cursor.sum_total()
165                        ),
166                        doctree: tree,
167                    }
168                }
169            };
170            return TransitionResult::Success {
171                doctree: doctree,
172                push_or_pop: PushOrPop::Push(vec![State::EnumeratedList]),
173                line_advance: LineAdvance::None,
174            };
175        }
176        IndentationMatch::TooMuch => {
177            doctree = match doctree.push_data_and_focus(
178                TreeNodeType::BlockQuote {
179                    body_indent: detected_enumerator_indent,
180                }
181            ) {
182                Ok(tree) => tree,
183                Err(tree) => {
184                    return TransitionResult::Failure {
185                        message: format!(
186                            "Node insertion error on line {}. Computer says no...",
187                            line_cursor.sum_total()
188                        ),
189                        doctree: tree,
190                    }
191                }
192            };
193            return TransitionResult::Success {
194                doctree: doctree,
195                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
196                line_advance: LineAdvance::None,
197            };
198        }
199        _ => {
200            return TransitionResult::Success {
201                doctree: doctree.focus_on_parent(),
202                push_or_pop: PushOrPop::Pop,
203                line_advance: LineAdvance::None,
204            };
205        }
206    }
207}
208
209/// A transitioin function for handling detected field markers in a state that generates body type nodes.
210pub fn field_marker(
211    src_lines: &Vec<String>,
212    base_indent: usize,
213    section_level: &mut usize,
214    line_cursor: &mut LineCursor,
215    mut doctree: DocTree,
216    captures: &regex::Captures,
217    pattern_name: &Pattern,
218) -> TransitionResult {
219
220
221    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
222
223    let list_node_data = TreeNodeType::FieldList {
224        marker_indent: detected_marker_indent,
225    };
226
227    // Match against the parent node. Only document root ignores indentation;
228    // inside any other container it makes a difference.
229    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
230        IndentationMatch::JustRight => {
231            doctree = match doctree.push_data_and_focus(list_node_data) {
232                Ok(tree) => tree,
233                Err(tree) => {
234                    return TransitionResult::Failure {
235                        message: format!(
236                            "Node insertion error on line {}. Computer says no...",
237                            line_cursor.sum_total()
238                        ),
239                        doctree: tree,
240                    }
241                }
242            };
243            return TransitionResult::Success {
244                doctree: doctree,
245                push_or_pop: PushOrPop::Push(vec![State::FieldList]),
246                line_advance: LineAdvance::None,
247            };
248        }
249        IndentationMatch::TooMuch => {
250            doctree = match doctree.push_data_and_focus(
251                TreeNodeType::BlockQuote {
252                    body_indent: detected_marker_indent,
253                }
254            ) {
255                Ok(tree) => tree,
256                Err(tree) => {
257                    return TransitionResult::Failure {
258                        message: format!(
259                            "Node insertion error on line {}. Computer says no...",
260                            line_cursor.sum_total()
261                        ),
262                        doctree: tree,
263                    }
264                }
265            };
266            return TransitionResult::Success {
267                doctree: doctree,
268                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
269                line_advance: LineAdvance::None,
270            };
271        }
272        _ => {
273            return TransitionResult::Success {
274                doctree: doctree.focus_on_parent(),
275                push_or_pop: PushOrPop::Pop,
276                line_advance: LineAdvance::None,
277            };
278        }
279    }
280}
281
282/// A transition function for generating footnotes
283pub fn footnote(
284    src_lines: &Vec<String>,
285    base_indent: usize,
286    section_level: &mut usize,
287    line_cursor: &mut LineCursor,
288    mut doctree: DocTree,
289    captures: &regex::Captures,
290    pattern_name: &Pattern,
291) -> TransitionResult {
292
293
294    // Detected parameters...
295    let indent_after_marker = captures.get(0).unwrap().as_str().chars().count() + base_indent;
296    let detected_marker_indent = match captures.name("indent") {
297        Some(whitespace) => whitespace.as_str().chars().count() + base_indent,
298        None => return TransitionResult::Failure {
299            message: format!(
300                "Could not scan footnote marker for indentation on line {}. Computer says no...",
301                line_cursor.sum_total()
302            ),
303            doctree: doctree
304        }
305    };
306    let (detected_kind, detected_label_str) = if let Some(label) = captures.name("manual") {
307        (FootnoteKind::Manual, label.as_str())
308    } else if let Some(label) = captures.name("autonumbered") {
309        (FootnoteKind::AutoNumbered, label.as_str())
310    } else if let Some(label) = captures.name("simplename") {
311        (FootnoteKind::SimpleRefName, label.as_str())
312    } else if let Some(label) = captures.name("autosymbol") {
313        (FootnoteKind::AutoSymbol, label.as_str())
314    } else {
315        return TransitionResult::Failure {
316            message: format!("No footnote type information inside footnote transition function. Computer says no..."),
317            doctree: doctree
318        };
319    };
320    let (detected_body_indent, offset) = match Parser::indent_on_subsequent_lines(
321        src_lines,
322        line_cursor.relative_offset(),
323    ) {
324        Some((indent, offset)) => {
325            let indent = if indent > detected_marker_indent {
326                indent
327            } else {
328                indent_after_marker
329            };
330            (indent, offset)
331        },
332        None => (indent_after_marker, 0 as usize)
333    };
334
335    let (label, target) = match detected_footnote_label_to_ref_label(
336        &doctree,
337        &detected_kind,
338        detected_label_str
339    ){
340        Some((label, target)) => (label, target),
341        None => return TransitionResult::Failure {
342            message: format!(
343                "Cound not transform a footnote marker into a label--target-pair on line {}. Computer says no...",
344                line_cursor.sum_total()
345            ),
346            doctree: doctree
347        }
348    };
349
350    // Match against the parent node. Only document root ignores indentation;
351    // inside any other container it makes a difference.
352    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
353        IndentationMatch::JustRight => {
354            let footnote_data = TreeNodeType::Footnote {
355                body_indent: detected_body_indent,
356                kind: detected_kind,
357                label: label.clone(),
358                target: target.clone(),
359            };
360            doctree = match doctree.push_data_and_focus(footnote_data) {
361                Ok(tree) => tree,
362                Err(tree) => {
363                    return TransitionResult::Failure {
364                        message: format!(
365                            "Node insertion error on line {}. Computer says no...",
366                            line_cursor.sum_total()
367                        ),
368                        doctree: tree,
369                    }
370                }
371            };
372
373            let (doctree, offset, state_stack) = match Parser::parse_first_node_block(doctree, src_lines, base_indent, line_cursor, detected_body_indent, Some(indent_after_marker), State::Footnote, section_level, false) {
374                Ok((parsing_result, offset)) => if let ParsingResult::EOF { doctree, state_stack } | ParsingResult::EmptyStateStack { doctree, state_stack } = parsing_result {
375                    (doctree, offset, state_stack)
376                } else {
377                    unreachable!(
378                        "Returned from a nested parsing session on line {} without necessary information. Computer says no...",
379                        line_cursor.sum_total()
380                    )
381                },
382                Err(ParsingResult::Failure { message, doctree }) => return TransitionResult::Failure {
383                    message: format!("Looks like footnote on line {} has no content. Computer says no...", line_cursor.sum_total()),
384                    doctree: doctree
385                },
386                _ => unreachable!("Parsing first node block on line {} resulted in unknown combination of return values. Computer says no...", line_cursor.sum_total())
387            };
388
389            return TransitionResult::Success {
390                doctree: doctree,
391                push_or_pop: PushOrPop::Push(state_stack),
392                line_advance: LineAdvance::Some(offset),
393            };
394        }
395        IndentationMatch::TooMuch => {
396            doctree = match doctree.push_data_and_focus(TreeNodeType::BlockQuote {
397                body_indent: detected_marker_indent,
398            }) {
399                Ok(tree) => tree,
400                Err(tree) => {
401                    return TransitionResult::Failure {
402                        message: format!(
403                            "Node insertion error on line {}. Computer says no...",
404                            line_cursor.sum_total()
405                        ),
406                        doctree: tree,
407                    }
408                }
409            };
410            return TransitionResult::Success {
411                doctree: doctree,
412                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
413                line_advance: LineAdvance::None,
414            };
415        }
416        _ => {
417            return TransitionResult::Success {
418                doctree: doctree.focus_on_parent(),
419                push_or_pop: PushOrPop::Pop,
420                line_advance: LineAdvance::None,
421            };
422        }
423    }
424}
425
426/// A transition function for generating citations
427pub fn citation(
428    src_lines: &Vec<String>,
429    base_indent: usize,
430    section_level: &mut usize,
431    line_cursor: &mut LineCursor,
432    mut doctree: DocTree,
433    captures: &regex::Captures,
434    pattern_name: &Pattern,
435) -> TransitionResult {
436
437    // Detected parameters...
438    let indent_after_marker = captures.get(0).unwrap().as_str().chars().count() + base_indent;
439    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
440    let detected_label_str = captures.get(2).unwrap().as_str();
441
442    let detected_body_indent = match Parser::indent_on_subsequent_lines(
443        src_lines,
444        line_cursor.relative_offset(),
445    ) {
446        Some((indent, offset)) => {
447            let indent = if indent > detected_marker_indent {
448                indent
449            } else {
450                indent_after_marker
451            };
452            indent
453        },
454        None => indent_after_marker
455    };
456
457    // Match against the parent node. Only document root ignores indentation;
458    // inside any other container it makes a difference.
459    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
460        IndentationMatch::JustRight => {
461            let citation_data = TreeNodeType::Citation {
462                body_indent: detected_body_indent,
463                label: detected_label_str.trim().to_string(),
464            };
465            doctree = match doctree.push_data_and_focus(citation_data) {
466                Ok(tree) => tree,
467                Err(tree) => {
468                    return TransitionResult::Failure {
469                        message: format!(
470                            "Node insertion error on line {}. Computer says no...",
471                            line_cursor.sum_total()
472                        ),
473                        doctree: tree,
474                    }
475                }
476            };
477
478            let (doctree, offset, state_stack) = match Parser::parse_first_node_block(doctree, src_lines, base_indent, line_cursor, detected_body_indent, Some(indent_after_marker), State::Citation, section_level,false) {
479                Ok((parsing_result, offset)) => if let ParsingResult::EOF { doctree, state_stack } | ParsingResult::EmptyStateStack { doctree, state_stack } = parsing_result {
480                    (doctree, offset, state_stack)
481                } else {
482                    unreachable!(
483                        "Returned from a nested parsing session on line {} without necessary information. Computer says no...",
484                        line_cursor.sum_total()
485                    )
486                },
487                Err(ParsingResult::Failure { message, doctree }) => return TransitionResult::Failure {
488                    message: format!(
489                        "Looks like citation on line {} has no content. Computer says no...",
490                        line_cursor.sum_total()
491                    ),
492                    doctree: doctree
493                },
494                _ => unreachable!("Parsing first node block on line {} resulted in unknown combination of return values. Computer says no...", line_cursor.sum_total())
495            };
496
497            return TransitionResult::Success {
498                doctree: doctree,
499                push_or_pop: PushOrPop::Push(state_stack),
500                line_advance: LineAdvance::Some(offset),
501            };
502        }
503        IndentationMatch::TooMuch => {
504            doctree = match doctree.push_data_and_focus(
505                    TreeNodeType::BlockQuote {
506                    body_indent: detected_marker_indent,
507                }
508            ) {
509                Ok(tree) => tree,
510                Err(tree) => {
511                    return TransitionResult::Failure {
512                        message: format!(
513                            "Node insertion error on line {}. Computer says no...",
514                            line_cursor.sum_total()
515                        ),
516                        doctree: tree,
517                    }
518                }
519            };
520            return TransitionResult::Success {
521                doctree: doctree,
522                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
523                line_advance: LineAdvance::None,
524            };
525        }
526        _ => {
527            return TransitionResult::Success {
528                doctree: doctree.focus_on_parent(),
529                push_or_pop: PushOrPop::Pop,
530                line_advance: LineAdvance::None,
531            };
532        }
533    }
534}
535
536/// Parses a hyperlink target into a node.
537pub fn hyperlink_target(
538    src_lines: &Vec<String>,
539    base_indent: usize,
540    section_level: &mut usize,
541    line_cursor: &mut LineCursor,
542    mut doctree: DocTree,
543    captures: &regex::Captures,
544    pattern_name: &Pattern,
545) -> TransitionResult {
546
547
548
549    // Detected parameters
550    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count();
551    let detected_text_indent = captures.get(0).unwrap().as_str().chars().count();
552    let detected_target_label = captures.get(2).unwrap().as_str();
553
554    // Check for anonymous target
555    let label_as_string = if detected_target_label == "_" {
556        doctree.next_anon_target_label()
557    } else {
558        crate::common::normalize_refname(detected_target_label)
559    };
560
561    let detected_body_indent = if let Some(line) = src_lines.get(line_cursor.relative_offset() + 1)
562    {
563        if line.trim().is_empty() {
564            detected_text_indent
565        } else {
566            let indent = line.chars().take_while(|c| c.is_whitespace()).count() + base_indent;
567            if indent < detected_marker_indent + 3 {
568                detected_text_indent
569            } else {
570                indent
571            }
572        }
573    } else {
574        detected_text_indent
575    };
576
577    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
578
579        IndentationMatch::JustRight => {
580            // Read in the following block of text here and parse it to find out the type of hyperref target in question
581
582            let (block_string, offset): (String, usize) = match Parser::read_indented_block(
583                src_lines,
584                line_cursor.relative_offset(),
585                true,
586                true,
587                Some(detected_body_indent),
588                Some(detected_text_indent),
589                false,
590            ) {
591                IndentedBlockResult::Ok {lines, minimum_indent, offset, blank_finish } => (
592                    lines
593                        .join("\n")
594                        .chars()
595                        .filter(|c| !c.is_whitespace())
596                        .collect(),
597                    offset,
598                ),
599                _ => {
600                    return TransitionResult::Failure {
601                        message: format!(
602                            "Error when reading indented text block on line {}.",
603                            line_cursor.sum_total()
604                        ),
605                        doctree: doctree,
606                    }
607                }
608            };
609
610            // Here we check which type of target we are dealing with:
611            // 1. internal
612            // 2. external or
613            // 3. indirect
614            // in addition to the usual identation and such.
615            if block_string.is_empty() {
616                // ... the target is internal
617
618                // We simply add the detected label into the queue of internal target labels and proceed with parsing in the current state.
619                // Should a non-internal target or other type of target node be detected next,
620                // this set of labels will be set to reference that node.
621
622                doctree.push_to_internal_target_stack(label_as_string);
623
624                return TransitionResult::Success {
625                    doctree: doctree,
626                    push_or_pop: PushOrPop::Neither,
627                    line_advance: LineAdvance::Some(1), // Jump to the next line so we don't just keep trying to parse the same internal target.
628                };
629            }
630
631            let node_type: TreeNodeType = match Parser::inline_parse(block_string, Some(&mut doctree), line_cursor) {
632
633                InlineParsingResult::Nodes(nodes_data) => {
634
635                    if nodes_data.len() != 1 {
636                        return TransitionResult::Failure {
637                            message: format!("Hyperlink targets should only contain a single node. Computer says no on line {}...", line_cursor.sum_total()),
638                            doctree: doctree
639                        }
640                    }
641                    match nodes_data.get(0) {
642                        Some(TreeNodeType::Reference { reference, displayed_text }) =>  {
643
644                            use crate::common::Reference;
645
646                            match reference {
647                                Reference::Internal (ref_str) => TreeNodeType::IndirectHyperlinkTarget {
648                                    target: label_as_string,
649                                    indirect_target: match reference {
650                                        Reference::Internal(ref_str) => ref_str.to_string(),
651                                        Reference::URI(ref_str) | Reference::EMail(ref_str) => return TransitionResult::Failure {
652                                        message: format!("Wrong type of inline node when parsing an indirect target {} on line {}. Computer says no...", ref_str, line_cursor.sum_total()),
653                                        doctree: doctree
654                                        },
655                                    },
656                                    marker_indent: detected_marker_indent
657                                },
658                                Reference::URI (ref_str) => TreeNodeType::ExternalHyperlinkTarget {
659                                    marker_indent: detected_marker_indent,
660                                    target: label_as_string,
661                                    uri: ref_str.to_owned()
662                                },
663                                Reference::EMail (ref_str) => TreeNodeType::ExternalHyperlinkTarget {
664                                    marker_indent: detected_marker_indent,
665                                    target: label_as_string,
666                                    uri: ref_str.to_owned()
667                                }
668                            }
669                        }
670                        _ => return TransitionResult::Failure {
671                            message: format!("Hyperlink target on line {} didn't match any known types. Computer says no...", line_cursor.sum_total()),
672                            doctree: doctree
673                        }
674                    }
675                }
676
677                _ => panic!("Inline parser failed when parsing a hyperlink target on line {} .Computer says no...", line_cursor.sum_total())
678
679            };
680
681            let node = TreeNode::new(node_type, doctree.node_count(), None, None);
682
683            match doctree.push_child(node) {
684                Ok(()) => {}
685                Err(node) => {
686                    return TransitionResult::Failure {
687                        message: format!(
688                            "Node insertion error on line {}. Computer says no...",
689                            line_cursor.sum_total()
690                        ),
691                        doctree: doctree,
692                    }
693                }
694            };
695
696            return TransitionResult::Success {
697                doctree: doctree,
698                push_or_pop: PushOrPop::Neither,
699                line_advance: LineAdvance::Some(1),
700            };
701        }
702
703        IndentationMatch::TooMuch => {
704            doctree = match doctree.push_data_and_focus(
705                    TreeNodeType::BlockQuote {
706                    body_indent: detected_marker_indent,
707                }
708            ) {
709                Ok(tree) => tree,
710                Err(tree) => {
711                    return TransitionResult::Failure {
712                        message: format!(
713                            "Node insertion error on line {}. Computer says no...",
714                            line_cursor.sum_total()
715                        ),
716                        doctree: tree,
717                    }
718                }
719            };
720            return TransitionResult::Success {
721                doctree: doctree,
722                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
723                line_advance: LineAdvance::None,
724            };
725        }
726        _ => {
727            return TransitionResult::Success {
728                doctree: doctree.focus_on_parent(),
729                push_or_pop: PushOrPop::Pop,
730                line_advance: LineAdvance::None,
731            };
732        }
733    }
734}
735
736/// A transition function for parsing directives in a state that recognizes body elements.
737pub fn directive(
738    src_lines: &Vec<String>,
739    base_indent: usize,
740    section_level: &mut usize,
741    line_cursor: &mut LineCursor,
742    mut doctree: DocTree,
743    captures: &regex::Captures,
744    pattern_name: &Pattern,
745) -> TransitionResult {
746
747
748    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
749    let detected_directive_label = captures
750        .get(2)
751        .unwrap()
752        .as_str()
753        .to_lowercase()
754        .split_whitespace()
755        .collect::<String>()
756        .to_lowercase();
757    let detected_first_indent = captures.get(0).unwrap().as_str().chars().count();
758
759    let empty_after_marker: bool = {
760        let line = src_lines.get(line_cursor.relative_offset()).unwrap(); // Unwrapping is not a problem here.
761
762        match line.char_indices().nth(detected_first_indent) {
763            Some((index, _)) => line[index..].trim().is_empty(),
764            None => true,
765        }
766    };
767
768    let (body_indent, body_offset) =
769        match Parser::indent_on_subsequent_lines(src_lines, line_cursor.relative_offset()) {
770            Some((indent, offset)) => (indent, offset),
771            None => (detected_first_indent, 0), // EOF encountered => stay on same line
772        };
773
774    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
775        IndentationMatch::JustRight => {
776            match detected_directive_label.as_str() {
777                "attention" | "caution" | "danger" | "error" | "hint" | "important" | "note"
778                | "tip" | "warning" => directive_parsers::parse_standard_admonition(
779                    src_lines,
780                    body_indent,
781                    *section_level,
782                    detected_first_indent,
783                    doctree,
784                    line_cursor,
785                    detected_directive_label.as_str(),
786                    empty_after_marker,
787                ),
788
789                "admonition" => directive_parsers::parse_generic_admonition(
790                    src_lines,
791                    doctree,
792                    line_cursor,
793                    empty_after_marker,
794                    body_indent,
795                    Some(detected_first_indent),
796                ),
797
798                "image" => directive_parsers::parse_image(
799                    src_lines,
800                    doctree,
801                    line_cursor,
802                    empty_after_marker,
803                    body_indent,
804                    Some(detected_first_indent),
805                ),
806
807                "figure" => directive_parsers::parse_figure(
808                    src_lines,
809                    doctree,
810                    line_cursor,
811                    base_indent,
812                    empty_after_marker,
813                    body_indent,
814                    Some(detected_first_indent),
815                    *section_level,
816                ),
817
818                "topic" => directive_parsers::parse_unknown_directive(
819                    doctree,
820                    src_lines,
821                    line_cursor,
822                    detected_directive_label.as_str(),
823                    detected_first_indent,
824                    body_indent,
825                    empty_after_marker,
826                ),
827
828                "sidebar" => directive_parsers::parse_unknown_directive(
829                    doctree,
830                    src_lines,
831                    line_cursor,
832                    detected_directive_label.as_str(),
833                    detected_first_indent,
834                    body_indent,
835                    empty_after_marker,
836                ),
837
838                "line-block" => directive_parsers::parse_unknown_directive(
839                    doctree,
840                    src_lines,
841                    line_cursor,
842                    detected_directive_label.as_str(),
843                    detected_first_indent,
844                    body_indent,
845                    empty_after_marker,
846                ),
847
848                "parsed-literal" => directive_parsers::parse_unknown_directive(
849                    doctree,
850                    src_lines,
851                    line_cursor,
852                    detected_directive_label.as_str(),
853                    detected_first_indent,
854                    body_indent,
855                    empty_after_marker,
856                ),
857
858                "code" => directive_parsers::parse_code(
859                    src_lines,
860                    doctree,
861                    line_cursor,
862                    base_indent,
863                    empty_after_marker,
864                    body_indent,
865                    Some(detected_first_indent),
866                    *section_level,
867                ),
868
869                "math" => directive_parsers::parse_math_block(
870                    src_lines,
871                    doctree,
872                    line_cursor,
873                    body_indent,
874                    empty_after_marker,
875                    detected_first_indent
876                ),
877
878                "rubric" => directive_parsers::parse_unknown_directive(
879                    doctree,
880                    src_lines,
881                    line_cursor,
882                    detected_directive_label.as_str(),
883                    detected_first_indent,
884                    body_indent,
885                    empty_after_marker,
886                ),
887
888                "epigraph" => directive_parsers::parse_unknown_directive(
889                    doctree,
890                    src_lines,
891                    line_cursor,
892                    detected_directive_label.as_str(),
893                    detected_first_indent,
894                    body_indent,
895                    empty_after_marker,
896                ),
897
898                "highlights" => directive_parsers::parse_unknown_directive(
899                    doctree,
900                    src_lines,
901                    line_cursor,
902                    detected_directive_label.as_str(),
903                    detected_first_indent,
904                    body_indent,
905                    empty_after_marker,
906                ),
907
908                "pull-quote" => directive_parsers::parse_unknown_directive(
909                    doctree,
910                    src_lines,
911                    line_cursor,
912                    detected_directive_label.as_str(),
913                    detected_first_indent,
914                    body_indent,
915                    empty_after_marker,
916                ),
917
918                "compound" => directive_parsers::parse_unknown_directive(
919                    doctree,
920                    src_lines,
921                    line_cursor,
922                    detected_directive_label.as_str(),
923                    detected_first_indent,
924                    body_indent,
925                    empty_after_marker,
926                ),
927
928                "container" => directive_parsers::parse_unknown_directive(
929                    doctree,
930                    src_lines,
931                    line_cursor,
932                    detected_directive_label.as_str(),
933                    detected_first_indent,
934                    body_indent,
935                    empty_after_marker,
936                ),
937
938                "table" => directive_parsers::parse_unknown_directive(
939                    doctree,
940                    src_lines,
941                    line_cursor,
942                    detected_directive_label.as_str(),
943                    detected_first_indent,
944                    body_indent,
945                    empty_after_marker,
946                ),
947
948                "csv-table" => directive_parsers::parse_unknown_directive(
949                    doctree,
950                    src_lines,
951                    line_cursor,
952                    detected_directive_label.as_str(),
953                    detected_first_indent,
954                    body_indent,
955                    empty_after_marker,
956                ),
957
958                "list-table" => directive_parsers::parse_list_table(
959                    src_lines,
960                    doctree,
961                    line_cursor,
962                    base_indent,
963                    empty_after_marker,
964                    Some(detected_first_indent),
965                    body_indent,
966                    *section_level,
967                ),
968
969                // DOCUMENT PARTS
970                "contents" => directive_parsers::parse_unknown_directive(
971                    doctree,
972                    src_lines,
973                    line_cursor,
974                    detected_directive_label.as_str(),
975                    detected_first_indent,
976                    body_indent,
977                    empty_after_marker,
978                ),
979
980                "sectnum" | "section-numbering" => directive_parsers::parse_unknown_directive(
981                    doctree,
982                    src_lines,
983                    line_cursor,
984                    detected_directive_label.as_str(),
985                    detected_first_indent,
986                    body_indent,
987                    empty_after_marker,
988                ),
989
990                "header" => directive_parsers::parse_unknown_directive(
991                    doctree,
992                    src_lines,
993                    line_cursor,
994                    detected_directive_label.as_str(),
995                    detected_first_indent,
996                    body_indent,
997                    empty_after_marker,
998                ),
999
1000                "footer" => directive_parsers::parse_unknown_directive(
1001                    doctree,
1002                    src_lines,
1003                    line_cursor,
1004                    detected_directive_label.as_str(),
1005                    detected_first_indent,
1006                    body_indent,
1007                    empty_after_marker,
1008                ),
1009
1010                "target-notes" => directive_parsers::parse_unknown_directive(
1011                    doctree,
1012                    src_lines,
1013                    line_cursor,
1014                    detected_directive_label.as_str(),
1015                    detected_first_indent,
1016                    body_indent,
1017                    empty_after_marker,
1018                ),
1019
1020                "footnotes" => {
1021                    unimplemented!("Footnotes (plural) directive is mentioned in the rST specification but is not implemented yet.")
1022                }
1023
1024                "citations" => {
1025                    unimplemented!("Citations (plural) directive is mentioned in the rST specification but is not implemented yet.")
1026                }
1027
1028                "meta" => directive_parsers::parse_unknown_directive(
1029                    doctree,
1030                    src_lines,
1031                    line_cursor,
1032                    detected_directive_label.as_str(),
1033                    detected_first_indent,
1034                    body_indent,
1035                    empty_after_marker,
1036                ),
1037
1038                // MISCELLANEOUS
1039                "include" => directive_parsers::parse_unknown_directive(
1040                    doctree,
1041                    src_lines,
1042                    line_cursor,
1043                    detected_directive_label.as_str(),
1044                    detected_first_indent,
1045                    body_indent,
1046                    empty_after_marker,
1047                ),
1048
1049                "raw" => directive_parsers::parse_unknown_directive(
1050                    doctree,
1051                    src_lines,
1052                    line_cursor,
1053                    detected_directive_label.as_str(),
1054                    detected_first_indent,
1055                    body_indent,
1056                    empty_after_marker,
1057                ),
1058
1059                "class" => directive_parsers::parse_class(
1060                    src_lines,
1061                    doctree,
1062                    line_cursor,
1063                    detected_first_indent,
1064                    body_indent,
1065                    empty_after_marker,
1066                    *section_level,
1067                ),
1068
1069                "role" => directive_parsers::parse_unknown_directive(
1070                    doctree,
1071                    src_lines,
1072                    line_cursor,
1073                    detected_directive_label.as_str(),
1074                    detected_first_indent,
1075                    body_indent,
1076                    empty_after_marker,
1077                ),
1078
1079                "default-role" => directive_parsers::parse_unknown_directive(
1080                    doctree,
1081                    src_lines,
1082                    line_cursor,
1083                    detected_directive_label.as_str(),
1084                    detected_first_indent,
1085                    body_indent,
1086                    empty_after_marker,
1087                ),
1088
1089                "title" => directive_parsers::parse_unknown_directive(
1090                    doctree,
1091                    src_lines,
1092                    line_cursor,
1093                    detected_directive_label.as_str(),
1094                    detected_first_indent,
1095                    body_indent,
1096                    empty_after_marker,
1097                ),
1098
1099                "restructuredtext-test-directive" => directive_parsers::parse_unknown_directive(
1100                    doctree,
1101                    src_lines,
1102                    line_cursor,
1103                    detected_directive_label.as_str(),
1104                    detected_first_indent,
1105                    body_indent,
1106                    empty_after_marker,
1107                ),
1108
1109                // SPHINX-SPECIFIC DIRECTIVES
1110                "toctree" => directive_parsers::parse_unknown_directive(
1111                    doctree,
1112                    src_lines,
1113                    line_cursor,
1114                    detected_directive_label.as_str(),
1115                    detected_first_indent,
1116                    body_indent,
1117                    empty_after_marker,
1118                ),
1119
1120                "versionadded" => directive_parsers::parse_unknown_directive(
1121                    doctree,
1122                    src_lines,
1123                    line_cursor,
1124                    detected_directive_label.as_str(),
1125                    detected_first_indent,
1126                    body_indent,
1127                    empty_after_marker,
1128                ),
1129
1130                "versionchanged" => directive_parsers::parse_unknown_directive(
1131                    doctree,
1132                    src_lines,
1133                    line_cursor,
1134                    detected_directive_label.as_str(),
1135                    detected_first_indent,
1136                    body_indent,
1137                    empty_after_marker,
1138                ),
1139
1140                "deprecated" => directive_parsers::parse_unknown_directive(
1141                    doctree,
1142                    src_lines,
1143                    line_cursor,
1144                    detected_directive_label.as_str(),
1145                    detected_first_indent,
1146                    body_indent,
1147                    empty_after_marker,
1148                ),
1149
1150                "seealso" => directive_parsers::parse_unknown_directive(
1151                    doctree,
1152                    src_lines,
1153                    line_cursor,
1154                    detected_directive_label.as_str(),
1155                    detected_first_indent,
1156                    body_indent,
1157                    empty_after_marker,
1158                ),
1159
1160                "centered" => directive_parsers::parse_unknown_directive(
1161                    doctree,
1162                    src_lines,
1163                    line_cursor,
1164                    detected_directive_label.as_str(),
1165                    detected_first_indent,
1166                    body_indent,
1167                    empty_after_marker,
1168                ),
1169
1170                "hlist" => directive_parsers::parse_unknown_directive(
1171                    doctree,
1172                    src_lines,
1173                    line_cursor,
1174                    detected_directive_label.as_str(),
1175                    detected_first_indent,
1176                    body_indent,
1177                    empty_after_marker,
1178                ),
1179
1180                "highlight" => directive_parsers::parse_unknown_directive(
1181                    doctree,
1182                    src_lines,
1183                    line_cursor,
1184                    detected_directive_label.as_str(),
1185                    detected_first_indent,
1186                    body_indent,
1187                    empty_after_marker,
1188                ),
1189
1190                "code-block" | "sourcecode" => directive_parsers::parse_sphinx_code_block(
1191                    src_lines,
1192                    doctree,
1193                    line_cursor,
1194                    base_indent,
1195                    empty_after_marker,
1196                    body_indent,
1197                    Some(detected_first_indent),
1198                ),
1199
1200                "literalinclude" => directive_parsers::parse_unknown_directive(
1201                    doctree,
1202                    src_lines,
1203                    line_cursor,
1204                    detected_directive_label.as_str(),
1205                    detected_first_indent,
1206                    body_indent,
1207                    empty_after_marker,
1208                ),
1209
1210                "glossary" => directive_parsers::parse_unknown_directive(
1211                    doctree,
1212                    src_lines,
1213                    line_cursor,
1214                    detected_directive_label.as_str(),
1215                    detected_first_indent,
1216                    body_indent,
1217                    empty_after_marker,
1218                ),
1219
1220                "sectionauthor" => directive_parsers::parse_unknown_directive(
1221                    doctree,
1222                    src_lines,
1223                    line_cursor,
1224                    detected_directive_label.as_str(),
1225                    detected_first_indent,
1226                    body_indent,
1227                    empty_after_marker,
1228                ),
1229
1230                "codeauthor" => directive_parsers::parse_unknown_directive(
1231                    doctree,
1232                    src_lines,
1233                    line_cursor,
1234                    detected_directive_label.as_str(),
1235                    detected_first_indent,
1236                    body_indent,
1237                    empty_after_marker,
1238                ),
1239
1240                "index" => directive_parsers::parse_unknown_directive(
1241                    doctree,
1242                    src_lines,
1243                    line_cursor,
1244                    detected_directive_label.as_str(),
1245                    detected_first_indent,
1246                    body_indent,
1247                    empty_after_marker,
1248                ),
1249
1250                "only" => directive_parsers::parse_sphinx_only(
1251                    src_lines,
1252                    doctree,
1253                    line_cursor,
1254                    empty_after_marker,
1255                    detected_first_indent,
1256                    body_indent,
1257                ),
1258
1259                "tabularcolumns" => directive_parsers::parse_unknown_directive(
1260                    doctree,
1261                    src_lines,
1262                    line_cursor,
1263                    detected_directive_label.as_str(),
1264                    detected_first_indent,
1265                    body_indent,
1266                    empty_after_marker,
1267                ),
1268
1269                "productionlist" => directive_parsers::parse_unknown_directive(
1270                    doctree,
1271                    src_lines,
1272                    line_cursor,
1273                    detected_directive_label.as_str(),
1274                    detected_first_indent,
1275                    body_indent,
1276                    empty_after_marker,
1277                ),
1278
1279                // A+-SPECIFIC DIRECTIVES
1280                "questionnaire" => directive_parsers::parse_aplus_questionnaire(
1281                    src_lines,
1282                    doctree,
1283                    line_cursor,
1284                    base_indent,
1285                    empty_after_marker,
1286                    detected_first_indent,
1287                    body_indent,
1288                ),
1289
1290                "submit" => directive_parsers::parse_aplus_submit(
1291                    src_lines,
1292                    doctree,
1293                    line_cursor,
1294                    detected_first_indent,
1295                    body_indent,
1296                    empty_after_marker,
1297                ),
1298
1299                "ae-input" => directive_parsers::parse_aplus_active_element_input(
1300                    src_lines,
1301                    doctree,
1302                    line_cursor,
1303                    base_indent,
1304                    empty_after_marker,
1305                    detected_first_indent,
1306                    body_indent,
1307                ),
1308
1309                "ae-output" => directive_parsers::parse_aplus_active_element_output(
1310                    src_lines,
1311                    doctree,
1312                    line_cursor,
1313                    base_indent,
1314                    empty_after_marker,
1315                    detected_first_indent,
1316                    body_indent,
1317                ),
1318
1319                "hidden_block" => directive_parsers::parse_unknown_directive(
1320                    doctree,
1321                    src_lines,
1322                    line_cursor,
1323                    detected_directive_label.as_str(),
1324                    detected_first_indent,
1325                    body_indent,
1326                    empty_after_marker,
1327                ),
1328
1329                "point-of-interest" => directive_parsers::parse_aplus_point_of_interest(
1330                    src_lines,
1331                    doctree,
1332                    line_cursor,
1333                    base_indent,
1334                    empty_after_marker,
1335                    detected_first_indent,
1336                    body_indent,
1337                    *section_level,
1338                ),
1339
1340                "annotated" => directive_parsers::parse_unknown_directive(
1341                    doctree,
1342                    src_lines,
1343                    line_cursor,
1344                    detected_directive_label.as_str(),
1345                    detected_first_indent,
1346                    body_indent,
1347                    empty_after_marker,
1348                ),
1349
1350                "lineref-code-block" => directive_parsers::parse_unknown_directive(
1351                    doctree,
1352                    src_lines,
1353                    line_cursor,
1354                    detected_directive_label.as_str(),
1355                    detected_first_indent,
1356                    body_indent,
1357                    empty_after_marker,
1358                ),
1359
1360                "repl-res-count-reset" => directive_parsers::parse_unknown_directive(
1361                    doctree,
1362                    src_lines,
1363                    line_cursor,
1364                    detected_directive_label.as_str(),
1365                    detected_first_indent,
1366                    body_indent,
1367                    empty_after_marker,
1368                ),
1369
1370                "acos-submit" => directive_parsers::parse_unknown_directive(
1371                    doctree,
1372                    src_lines,
1373                    line_cursor,
1374                    detected_directive_label.as_str(),
1375                    detected_first_indent,
1376                    body_indent,
1377                    empty_after_marker,
1378                ),
1379
1380                "div" => directive_parsers::parse_unknown_directive(
1381                    doctree,
1382                    src_lines,
1383                    line_cursor,
1384                    detected_directive_label.as_str(),
1385                    detected_first_indent,
1386                    body_indent,
1387                    empty_after_marker,
1388                ),
1389
1390                "styled-topic" => directive_parsers::parse_unknown_directive(
1391                    doctree,
1392                    src_lines,
1393                    line_cursor,
1394                    detected_directive_label.as_str(),
1395                    detected_first_indent,
1396                    body_indent,
1397                    empty_after_marker,
1398                ),
1399
1400                // A+ MEDIA DIRECTIVES
1401                "story" => directive_parsers::parse_unknown_directive(
1402                    doctree,
1403                    src_lines,
1404                    line_cursor,
1405                    detected_directive_label.as_str(),
1406                    detected_first_indent,
1407                    body_indent,
1408                    empty_after_marker,
1409                ),
1410
1411                "jsvee" => directive_parsers::parse_unknown_directive(
1412                    doctree,
1413                    src_lines,
1414                    line_cursor,
1415                    detected_directive_label.as_str(),
1416                    detected_first_indent,
1417                    body_indent,
1418                    empty_after_marker,
1419                ),
1420
1421                "youtube" => directive_parsers::parse_unknown_directive(
1422                    doctree,
1423                    src_lines,
1424                    line_cursor,
1425                    detected_directive_label.as_str(),
1426                    detected_first_indent,
1427                    body_indent,
1428                    empty_after_marker,
1429                ),
1430
1431                "local-video" => directive_parsers::parse_unknown_directive(
1432                    doctree,
1433                    src_lines,
1434                    line_cursor,
1435                    detected_directive_label.as_str(),
1436                    detected_first_indent,
1437                    body_indent,
1438                    empty_after_marker,
1439                ),
1440
1441                "embedded-page" => directive_parsers::parse_unknown_directive(
1442                    doctree,
1443                    src_lines,
1444                    line_cursor,
1445                    detected_directive_label.as_str(),
1446                    detected_first_indent,
1447                    body_indent,
1448                    empty_after_marker,
1449                ),
1450
1451                _ => directive_parsers::parse_unknown_directive(
1452                    doctree,
1453                    src_lines,
1454                    line_cursor,
1455                    detected_directive_label.as_str(),
1456                    detected_first_indent,
1457                    body_indent,
1458                    empty_after_marker,
1459                ),
1460            }
1461        }
1462        IndentationMatch::TooMuch => {
1463            doctree = match doctree.push_data_and_focus(
1464                    TreeNodeType::BlockQuote {
1465                    body_indent: detected_marker_indent,
1466                }
1467            ) {
1468                Ok(tree) => tree,
1469                Err(tree) => {
1470                    return TransitionResult::Failure {
1471                        message: format!(
1472                            "Node insertion error on line {}. Computer says no...",
1473                            line_cursor.sum_total()
1474                        ),
1475                        doctree: tree,
1476                    }
1477                }
1478            };
1479            return TransitionResult::Success {
1480                doctree: doctree,
1481                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
1482                line_advance: LineAdvance::None,
1483            };
1484        }
1485        _ => {
1486            return TransitionResult::Success {
1487                doctree: doctree.focus_on_parent(),
1488                push_or_pop: PushOrPop::Pop,
1489                line_advance: LineAdvance::None,
1490            };
1491        }
1492    }
1493}
1494
1495/// A function for parsing reST comments.
1496pub fn comment(
1497    src_lines: &Vec<String>,
1498    base_indent: usize,
1499    section_level: &mut usize,
1500    line_cursor: &mut LineCursor,
1501    mut doctree: DocTree,
1502    captures: &regex::Captures,
1503    pattern_name: &Pattern,
1504) -> TransitionResult {
1505
1506
1507    let match_len = captures.get(0).unwrap().as_str().chars().count() + base_indent;
1508    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
1509    let next_line_indent = if let Some(line) = src_lines.get(line_cursor.relative_offset() + 1) {
1510        if line.trim().is_empty() {
1511            match_len
1512        } else {
1513            let indent = line.chars().take_while(|c| c.is_whitespace()).count() + base_indent;
1514            if indent < detected_marker_indent + 1 {
1515                match_len
1516            } else {
1517                indent
1518            }
1519        }
1520    } else {
1521        match_len
1522    };
1523
1524    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_marker_indent) {
1525        IndentationMatch::JustRight => {
1526            let (comment_string, offset) = match Parser::read_indented_block(
1527                src_lines,
1528                line_cursor.relative_offset(),
1529                false,
1530                true,
1531                Some(next_line_indent - base_indent),
1532                Some(match_len - base_indent),
1533                false,
1534            ) {
1535                IndentedBlockResult::Ok {lines, minimum_indent, offset, blank_finish } => (lines.join("\n").trim().to_string(), offset),
1536                _ => {
1537                    return TransitionResult::Failure {
1538                        message: format!(
1539                            "Could not read comment on line {}.",
1540                            line_cursor.sum_total(),
1541                        ),
1542                        doctree: doctree,
1543                    }
1544                }
1545            };
1546
1547            let comment_data = if comment_string.is_empty() {
1548                TreeNodeType::Comment { text: None }
1549            } else {
1550                TreeNodeType::Comment {
1551                    text: Some(comment_string),
1552                }
1553            };
1554
1555            return TransitionResult::Success {
1556                doctree: match doctree.push_data(comment_data) {
1557                    Ok(doctree) => doctree,
1558                    Err(doctree) => {
1559                        return TransitionResult::Failure {
1560                            message: format!(
1561                                "Error when reading comment on line {}. Computer says no...",
1562                                line_cursor.sum_total()
1563                            ),
1564                            doctree: doctree,
1565                        }
1566                    }
1567                },
1568                push_or_pop: PushOrPop::Neither,
1569                line_advance: LineAdvance::Some(offset),
1570            };
1571        }
1572        IndentationMatch::TooMuch => {
1573            doctree = match doctree.push_data_and_focus(
1574                    TreeNodeType::BlockQuote {
1575                    body_indent: detected_marker_indent,
1576                }
1577            ) {
1578                Ok(tree) => tree,
1579                Err(tree) => {
1580                    return TransitionResult::Failure {
1581                        message: format!(
1582                            "Node insertion error on line {}. Computer says no...",
1583                            line_cursor.sum_total()
1584                        ),
1585                        doctree: tree,
1586                    }
1587                }
1588            };
1589            return TransitionResult::Success {
1590                doctree: doctree,
1591                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
1592                line_advance: LineAdvance::None,
1593            };
1594        }
1595        _ => {
1596            return TransitionResult::Success {
1597                doctree: doctree.focus_on_parent(),
1598                push_or_pop: PushOrPop::Pop,
1599                line_advance: LineAdvance::None,
1600            };
1601        }
1602    }
1603}
1604
1605/// A function that handles the parsing of blocks that start with text.
1606/// This includes paragraphs, but also underlined titles and definition lists.
1607/// The latter are detected via lookahead.
1608pub fn text(
1609    src_lines: &Vec<String>,
1610    base_indent: usize,
1611    section_level: &mut usize,
1612    line_cursor: &mut LineCursor,
1613    mut doctree: DocTree,
1614    captures: &regex::Captures,
1615    pattern_name: &Pattern,
1616) -> TransitionResult {
1617
1618    let detected_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
1619
1620    let next_line = src_lines.get(line_cursor.relative_offset() + 1);
1621
1622    if next_line.is_some() {
1623        let next_line_str = next_line.unwrap();
1624
1625        if let Some(line_capts) = crate::parser::automata::LINE_AUTOMATON.captures(next_line_str) {
1626            // Underlined section title
1627            if detected_indent > 0 {
1628                return TransitionResult::Failure {
1629                    message: format!(
1630                        "Found indented underlined section on line {}. Computer says no...",
1631                        line_cursor.sum_total()
1632                    ),
1633                    doctree: doctree,
1634                };
1635            }
1636
1637            let line_char = next_line_str.chars().next().unwrap();
1638            let section_style = SectionLineStyle::Under(line_char);
1639            let title_text = src_lines.get(line_cursor.relative_offset()).unwrap().trim();
1640            let section_data = doctree.new_section_data(title_text, section_style);
1641
1642            if let TreeNodeType::Section { level, .. } = section_data {
1643                let detected_level = level;
1644
1645                match doctree.shared_data() {
1646                    TreeNodeType::Document { .. } => {
1647                        doctree = match doctree.push_data_and_focus(section_data) {
1648                            Ok(tree) => tree,
1649                            Err(tree) => {
1650                                return TransitionResult::Failure {
1651                                    message: format!(
1652                                        "Node insertion error on line {}. Computer says no...",
1653                                        line_cursor.sum_total()
1654                                    ),
1655                                    doctree: tree,
1656                                }
1657                            }
1658                        };
1659                        *section_level = detected_level;
1660                    }
1661
1662                    TreeNodeType::Section { level, .. } => {
1663                        if detected_level <= *level {
1664                            *section_level = *level;
1665                            doctree = doctree.focus_on_parent();
1666                            return TransitionResult::Success {
1667                                doctree: doctree,
1668                                push_or_pop: PushOrPop::Pop,
1669                                line_advance: LineAdvance::None,
1670                            };
1671                        } else {
1672                            *section_level = detected_level;
1673                            doctree =
1674                                match doctree.push_data_and_focus(section_data) {
1675                                    Ok(tree) => tree,
1676                                    Err(tree) => return TransitionResult::Failure {
1677                                        message: format!(
1678                                            "Node insertion error on line {}. Computer says no...",
1679                                            line_cursor.sum_total()
1680                                        ),
1681                                        doctree: tree,
1682                                    },
1683                                };
1684                        }
1685                    }
1686
1687                    _ => {
1688                        doctree = doctree.focus_on_parent();
1689
1690                        if let TreeNodeType::Section { level, .. } = doctree.shared_data() {
1691                            *section_level = *level;
1692                        }
1693
1694                        return TransitionResult::Success {
1695                            doctree: doctree,
1696                            push_or_pop: PushOrPop::Pop,
1697                            line_advance: LineAdvance::None,
1698                        };
1699                    }
1700                }
1701                return TransitionResult::Success {
1702                    doctree: doctree,
1703                    push_or_pop: PushOrPop::Push(vec![State::Section]),
1704                    line_advance: LineAdvance::Some(2), // Jump over the section underline
1705                };
1706            }
1707        }
1708
1709        if let Some(text_capts) = crate::parser::automata::TEXT_AUTOMATON.captures(next_line_str) {
1710            // Paragraph or definition list item. Determine based on indentation.
1711
1712            let next_line_indent =
1713                text_capts.get(1).unwrap().as_str().chars().count() + base_indent;
1714
1715            if next_line_indent == detected_indent {
1716                // Paragraph
1717
1718                return parse_paragraph(
1719                    src_lines,
1720                    base_indent,
1721                    line_cursor,
1722                    doctree,
1723                    detected_indent,
1724                );
1725            } else if next_line_indent > detected_indent {
1726                // Definition list item
1727
1728                match Parser::parent_indent_matches(doctree.shared_data(), detected_indent) {
1729                    IndentationMatch::JustRight => {
1730                        doctree = match doctree.push_data_and_focus(TreeNodeType::DefinitionList {
1731                            term_indent: detected_indent,
1732                        }) {
1733                            Ok(tree) => tree,
1734                            Err(tree) => {
1735                                return TransitionResult::Failure {
1736                                    message: format!(
1737                                        "Node insertion error on line {}. Computer says no...",
1738                                        line_cursor.sum_total()
1739                                    ),
1740                                    doctree: tree,
1741                                }
1742                            }
1743                        };
1744
1745                        return TransitionResult::Success {
1746                            doctree: doctree,
1747                            push_or_pop: PushOrPop::Push(vec![State::DefinitionList]),
1748                            line_advance: LineAdvance::None,
1749                        };
1750                    }
1751                    IndentationMatch::TooMuch => {
1752                        doctree = match doctree.push_data_and_focus(
1753                                TreeNodeType::BlockQuote {
1754                                body_indent: detected_indent,
1755                            }
1756                        ) {
1757                            Ok(tree) => tree,
1758                            Err(tree) => {
1759                                return TransitionResult::Failure {
1760                                    message: format!(
1761                                        "Node insertion error on line {}. Computer says no...",
1762                                        line_cursor.sum_total()
1763                                    ),
1764                                    doctree: tree,
1765                                }
1766                            }
1767                        };
1768                        return TransitionResult::Success {
1769                            doctree: doctree,
1770                            push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
1771                            line_advance: LineAdvance::None,
1772                        };
1773                    }
1774                    _ => {
1775                        return TransitionResult::Success {
1776                            doctree: doctree.focus_on_parent(),
1777                            push_or_pop: PushOrPop::Pop,
1778                            line_advance: LineAdvance::None,
1779                        };
1780                    }
1781                }
1782            } else {
1783                // Paragraph line unaligned with previous lines => syntax error
1784
1785                return parse_paragraph(
1786                    src_lines,
1787                    base_indent,
1788                    line_cursor,
1789                    doctree,
1790                    detected_indent,
1791                );
1792            }
1793        } else {
1794            return parse_paragraph(
1795                src_lines,
1796                base_indent,
1797                line_cursor,
1798                doctree,
1799                detected_indent,
1800            );
1801        }
1802    } else {
1803        // End of input, so parse current line as a paragraph and leave it at that.
1804        return parse_paragraph(
1805            src_lines,
1806            base_indent,
1807            line_cursor,
1808            doctree,
1809            detected_indent,
1810        );
1811    }
1812}
1813
1814/// Parses reStructuredText transitions and section titles prefixed with an overline.
1815pub fn line(
1816    src_lines: &Vec<String>,
1817    base_indent: usize,
1818    section_level: &mut usize,
1819    line_cursor: &mut LineCursor,
1820    mut doctree: DocTree,
1821    captures: &regex::Captures,
1822    pattern_name: &Pattern,
1823) -> TransitionResult {
1824
1825
1826    /// #### TRANSITION_LINE_LENGTH
1827    /// The minimum length of a transition line.
1828    const TRANSITION_LINE_LENGTH: usize = 4;
1829
1830    let detected_line = captures.get(1).unwrap().as_str();
1831    let detected_line_char = detected_line.chars().next().unwrap();
1832    let detected_line_length = detected_line.trim_end().chars().count();
1833
1834    let current_line = line_cursor.relative_offset();
1835
1836    let previous_line = if let Some(num) = usize::checked_sub(current_line, 1) {
1837        src_lines.get(current_line - 1)
1838    } else {
1839        None
1840    };
1841
1842    let next_line = if let Some(num) = usize::checked_add(current_line, 1) {
1843        src_lines.get(current_line + 1)
1844    } else {
1845        None
1846    };
1847
1848    let at_doc_root = if let TreeNodeType::Document { .. } = doctree.shared_node_data() {
1849        true
1850    } else {
1851        false
1852    };
1853    let at_input_start = previous_line.is_none();
1854    let at_input_end = next_line.is_none();
1855
1856    if at_input_end {
1857        return TransitionResult::Failure {
1858            message: format!("Discovered a transition or an incomplete section at the end of (nested) input on line {}. Computer says no...", line_cursor.sum_total()),
1859            doctree: doctree
1860        };
1861    }
1862
1863    match (previous_line, next_line) {
1864        (Some(p_line), Some(n_line)) => {
1865            if p_line.trim().is_empty()
1866                && n_line.trim().is_empty()
1867                && detected_line_length >= TRANSITION_LINE_LENGTH
1868            {
1869                // A transition can only exist as a child of a document or a section, so a TransitionResult::Success is returned on insertion failure.
1870                // TODO: add more TransitionResult variants to allow for detection of incompatible parents and children?
1871                doctree = match doctree.push_data(TreeNodeType::Transition) {
1872                    Ok(tree) => tree,
1873                    Err(tree) => return TransitionResult::Success {
1874                        doctree: tree.focus_on_parent(),
1875                        push_or_pop: PushOrPop::Pop,
1876                        line_advance: LineAdvance::None
1877                    }
1878                };
1879                return TransitionResult::Success {
1880                    doctree: doctree,
1881                    push_or_pop: PushOrPop::Neither,
1882                    line_advance: LineAdvance::Some(2) // jump over the empty line following the transition
1883                }
1884            } else if crate::parser::automata::TEXT_AUTOMATON.is_match(n_line) {
1885                // A possible section title.
1886                // Check next line for line pattern and its length.
1887                if let Some(next_next_line) = src_lines.get(line_cursor.relative_offset() + 2) {
1888                    if let Some(capts) = crate::parser::automata::LINE_AUTOMATON.captures(next_next_line) {
1889                        let next_line_len = n_line.trim_end().chars().count(); // title text line
1890                        let next_next_line_char = next_next_line.trim_end().chars().next().unwrap();
1891                        let next_next_line_len = next_next_line.trim_end().chars().count();
1892
1893                        if detected_line_char == next_next_line_char
1894                            && detected_line_length == next_next_line_len
1895                            && next_line_len <= detected_line_length
1896                        {
1897                            // generate a section.
1898                            let section_line_style = SectionLineStyle::OverAndUnder(detected_line_char);
1899                            let section_data = doctree.new_section_data(n_line.trim(), section_line_style);
1900                            if let TreeNodeType::Section { level, .. } = section_data {
1901                                let detected_level = level;
1902                                match doctree.shared_data() {
1903                                    TreeNodeType::Document { .. } => {
1904                                        doctree = match doctree.push_data_and_focus(section_data) {
1905                                            Ok(tree) => tree,
1906                                            Err(tree) => return TransitionResult::Failure {
1907                                                message: format!(
1908                                                    "Node insertion error on line {}. Computer says no...",
1909                                                    line_cursor.sum_total()
1910                                                ),
1911                                                doctree: tree
1912                                            }
1913                                        };
1914                                        *section_level = detected_level;
1915                                    }
1916                                    TreeNodeType::Section { level, .. } => {
1917                                        if detected_level <= *level {
1918                                            *section_level = *level;
1919                                            doctree = doctree.focus_on_parent();
1920                                            return TransitionResult::Success {
1921                                                doctree: doctree,
1922                                                push_or_pop: PushOrPop::Pop,
1923                                                line_advance: LineAdvance::None
1924                                            }
1925                                        } else {
1926                                            *section_level = detected_level;
1927                                            doctree = match doctree.push_data_and_focus(section_data) {
1928                                                Ok(tree) => tree,
1929                                                Err(tree) => return TransitionResult::Failure {
1930                                                    message: format!("Node insertion error on line {}. Computer says no...", line_cursor.sum_total()),
1931                                                    doctree: tree
1932                                                }
1933                                            };
1934                                        }
1935                                    }
1936                                    _ => {
1937                                        doctree = doctree.focus_on_parent();
1938                                        if let TreeNodeType::Section{level, .. } = doctree.shared_data() {
1939                                            *section_level = *level;
1940                                        }
1941                                        return TransitionResult::Success {
1942                                            doctree: doctree,
1943                                            push_or_pop: PushOrPop::Pop,
1944                                            line_advance: LineAdvance::None
1945                                        }
1946                                    }
1947                                }
1948                                return TransitionResult::Success {
1949                                    doctree: doctree,
1950                                    push_or_pop: PushOrPop::Push(vec![State::Section]),
1951                                    line_advance: LineAdvance::Some(3) // Jump over the section underline
1952                                }
1953                            } else {
1954                                return TransitionResult::Failure {
1955                                    message: format!("No generated section where one was expected on line {}. Computer says no...", line_cursor.sum_total()),
1956                                    doctree: doctree
1957                                }
1958                            }
1959                        } else {
1960                            return TransitionResult::Failure {
1961                                message: format!("Found a section with unmatching over- and underline lengths or characters on line {}. Computer says no...", line_cursor.sum_total()),
1962                                doctree: doctree
1963                            }
1964                        }
1965                    } else {
1966                        return TransitionResult::Failure {
1967                            message: format!("Found section-like construct without underline on line {}. Computer says no...", line_cursor.sum_total()),
1968                            doctree: doctree
1969                        }
1970                    }
1971                } else {
1972                    return TransitionResult::Failure {
1973                        message: format!("Found something akin to an section title but no underline at the end of input on line {}. Computer says no...", line_cursor.sum_total()),
1974                        doctree: doctree
1975                    }
1976                }
1977            } else if captures.get(0).unwrap().as_str().trim() == "::" {
1978                // Empty paragraph
1979                return parse_paragraph(src_lines, base_indent, line_cursor, doctree, 0)
1980            } else {
1981                return TransitionResult::Failure {
1982                    message: format!("Unknown line construct on line {}. Computer says no...", line_cursor.sum_total()),
1983                    doctree: doctree
1984                }
1985            }
1986        }
1987
1988        (None, Some(n_line)) => {
1989            if crate::parser::automata::TEXT_AUTOMATON.is_match(n_line) {
1990                // A possible section title.
1991                // Check next line for line pattern and its length.
1992                if let Some(next_next_line) = src_lines.get(line_cursor.relative_offset() + 2) {
1993                    if let Some(capts) = crate::parser::automata::LINE_AUTOMATON.captures(next_next_line) {
1994
1995                        let next_line_len = n_line.trim_end().chars().count(); // title text line
1996                        let next_next_line_char = next_next_line.trim_end().chars().next().unwrap();
1997                        let next_next_line_len = next_next_line.trim_end().chars().count();
1998
1999                        if detected_line_char == next_next_line_char
2000                            && detected_line_length == next_next_line_len
2001                            && next_line_len <= detected_line_length
2002                        {
2003                            // generate a section.
2004                            let section_line_style = SectionLineStyle::OverAndUnder(detected_line_char);
2005                            let section_data = doctree.new_section_data(n_line.trim(), section_line_style);
2006                            if let TreeNodeType::Section { level, .. } = section_data {
2007                                let detected_level = level;
2008                                    match doctree.shared_data() {
2009
2010                                        TreeNodeType::Document { .. } => {
2011                                            doctree = match doctree.push_data_and_focus(section_data) {
2012                                                Ok(tree) => tree,
2013                                                Err(tree) => return TransitionResult::Failure {
2014                                                    message: format!("Node insertion error on line {}. Computer says no...", line_cursor.sum_total()),
2015                                                    doctree: tree
2016                                                }
2017                                            };
2018                                            *section_level = detected_level;
2019                                        }
2020
2021                                        TreeNodeType::Section { level, .. } => {
2022                                            if detected_level <= *level {
2023                                                *section_level = *level;
2024                                                doctree = doctree.focus_on_parent();
2025                                                return TransitionResult::Success {
2026                                                    doctree: doctree,
2027                                                    push_or_pop: PushOrPop::Pop,
2028                                                    line_advance: LineAdvance::None
2029                                                }
2030                                            } else {
2031                                                *section_level = detected_level;
2032                                                    doctree = match doctree.push_data_and_focus(section_data) {
2033                                                        Ok(tree) => tree,
2034                                                        Err(tree) => return TransitionResult::Failure {
2035                                                            message: format!(
2036                                                                "Node insertion error on line {}. Computer says no...",
2037                                                                line_cursor.sum_total()
2038                                                            ),
2039                                                            doctree: tree
2040                                                        }
2041                                                    };
2042                                            }
2043                                        }
2044                                        _ => {
2045                                            doctree = doctree.focus_on_parent();
2046                                            if let TreeNodeType::Section{level, .. } = doctree.shared_data() {
2047                                                *section_level = *level;
2048                                            }
2049                                            return TransitionResult::Success {
2050                                                doctree: doctree,
2051                                                push_or_pop: PushOrPop::Pop,
2052                                                line_advance: LineAdvance::None
2053                                            }
2054                                        }
2055                                    }
2056                                    return TransitionResult::Success {
2057                                    doctree: doctree,
2058                                    push_or_pop: PushOrPop::Push(vec![State::Section]),
2059                                    line_advance: LineAdvance::Some(3) // Jump over the section underline
2060                                    }
2061                            } else {
2062                                return TransitionResult::Failure {
2063                                    message: format!("No generated section where one was expected on line {}. Computer says no...", line_cursor.sum_total()),
2064                                    doctree: doctree
2065                                }
2066                            }
2067                        } else {
2068                            return TransitionResult::Failure {
2069                                message: format!("Found a section with unmatching over- and underline lengths or characters on line {}. Computer says no...", line_cursor.sum_total()),
2070                                doctree: doctree
2071                            }
2072                        }
2073                    } else {
2074                        return TransitionResult::Failure {
2075                            message: format!(
2076                                "Found section-like construct without underline on line {}. Computer says no...",
2077                                line_cursor.sum_total()
2078                            ),
2079                            doctree: doctree
2080                        }
2081                    }
2082                } else {
2083                    return TransitionResult::Failure {
2084                        message: format!("Found something akin to an section title but no underline at the end of input on line {}. Computer says no...", line_cursor.sum_total()),
2085                        doctree: doctree
2086                    }
2087                }
2088            } else if captures.get(0).unwrap().as_str().trim() == "::" {
2089                // Empty paragraph
2090                return parse_paragraph(src_lines, base_indent, line_cursor, doctree, 0)
2091            } else {
2092                return TransitionResult::Failure {
2093                    message: format!("No known pattern during a line transition on line {}. Computer says no...", line_cursor.sum_total()),
2094                    doctree: doctree
2095                }
2096            }
2097        }
2098        _ => return TransitionResult::Failure {
2099            message: format!("Found a transition-like construct on line {}, but no existing previous or next line. Computer says no...", line_cursor.sum_total()),
2100            doctree: doctree
2101        }
2102    }
2103}
2104
2105// ==================
2106//  Helper functions
2107// ==================
2108
2109/// Converts a foonote label into a label--target-pair based on the current state of `DocTree.foonote_data`,
2110/// if possible. Returns an `Option`al pair `(label, target)` if successful.
2111pub fn detected_footnote_label_to_ref_label(
2112    doctree: &DocTree,
2113    footnotekind: &FootnoteKind,
2114    detected_label_str: &str,
2115) -> Option<(String, String)> {
2116    use crate::common::normalize_refname;
2117
2118    let normalized_name = normalize_refname(detected_label_str);
2119    match footnotekind {
2120        FootnoteKind::Manual => {
2121            // In this case the doctree is simply asked whether it has a reference
2122            // with this name. If yes, the user is warned of a duplicate label,
2123            // but otherwise no special action is taken.
2124
2125            return Some((normalized_name.clone(), normalized_name));
2126        }
2127
2128        FootnoteKind::AutoNumbered => {
2129            // Here we iterate the set of all possible `u32` values
2130            // and once a number that has not been used as a label is found,
2131            // it is returned.
2132
2133            // TODO: retrieve a start value from doctree, so iteration doesn't have to start from 1...
2134
2135            let next_autonumber = if let Some(number_str) = doctree.new_autonumber_footnote_label() {
2136                number_str
2137            } else {
2138                return None
2139            };
2140            Some((next_autonumber.clone(), next_autonumber))
2141        }
2142
2143        FootnoteKind::SimpleRefName => {
2144            // Same as with automatically numbered footnotes, check if this has already a number representation
2145            // in the doctree and if not, return it.
2146
2147            let next_autonumber = if let Some(number_str) = doctree.new_autonumber_footnote_label() {
2148                number_str
2149            } else {
2150                return None
2151            };
2152            Some((next_autonumber, normalized_name))
2153        }
2154
2155        FootnoteKind::AutoSymbol => {
2156            // Generate a label from crate::common::FOONOTE_SYMBOLS based on the number of autosymbol footnotes
2157            // entered into the document thus far.
2158            if let Some(label) = doctree.new_symbolic_footnote_label() {
2159                return Some((label.clone(), label))
2160            } else {
2161                None
2162            }
2163        }
2164    }
2165}
2166
2167/// A helper for parsing a paragraph node.
2168fn parse_paragraph(
2169    src_lines: &Vec<String>,
2170    base_indent: usize,
2171    line_cursor: &mut LineCursor,
2172    mut doctree: DocTree,
2173    detected_indent: usize,
2174) -> TransitionResult {
2175
2176    match Parser::parent_indent_matches(doctree.shared_node_data(), detected_indent) {
2177        IndentationMatch::JustRight => {
2178            let relative_indent = detected_indent - base_indent;
2179
2180            let mut block = match Parser::read_text_block(
2181                src_lines,
2182                line_cursor.relative_offset(),
2183                true,
2184                true,
2185                Some(relative_indent),
2186                true
2187            ) {
2188                TextBlockResult::Ok {lines, offset } => lines.join("\n").trim_end().to_string(),
2189                TextBlockResult::Err {lines, offset } => {
2190                    return TransitionResult::Failure {
2191                        message: format!("Error when reading text block on line {}.", line_cursor.sum_total()),
2192                        doctree: doctree,
2193                    }
2194                }
2195            };
2196
2197            lazy_static! {
2198                /// There are two kinds of literal block indicators:
2199                /// 1. preceded by whitespace
2200                /// 2. not preceded by whitespace
2201                ///
2202                /// In the first case, both `::`s will be removed. In the second case, only the first one will disappear.
2203                static ref LITERAL_BLOCK_INDICATOR: Regex = Regex::new(r"(\s{0,1}|\S)::$").unwrap();
2204            }
2205
2206            let literal_block_next: bool = if let Some(capts)
2207                = LITERAL_BLOCK_INDICATOR.captures(block.as_str())
2208            {
2209                // Remove literal block indicator from paragraph
2210                let indicator_len = if capts.get(1).unwrap().as_str().trim().is_empty() {
2211                    "::".chars().count()
2212                } else {
2213                    ":".chars().count()
2214                };
2215
2216                for _ in 0..indicator_len {
2217                    if let None = block.pop() {
2218                        return TransitionResult::Failure { // This should not ever be triggered
2219                            message: format!("Tried removing a literal block indicator from a paragraph starting on line {} but failed. Computer says no...", line_cursor.sum_total()),
2220                            doctree: doctree
2221                        };
2222                    }
2223                }
2224                true
2225            } else {
2226                false
2227            };
2228
2229            doctree = match doctree.push_data_and_focus(TreeNodeType::Paragraph {
2230                indent: detected_indent,
2231            }) {
2232                Ok(tree) => tree,
2233                Err(tree) => {
2234                    return TransitionResult::Failure {
2235                        message: format!(
2236                            "Node insertion error on line {}. Computer says no...",
2237                            line_cursor.sum_total()
2238                        ),
2239                        doctree: tree,
2240                    }
2241                }
2242            };
2243
2244            // Pass text to inline parser as a string
2245            doctree = match Parser::inline_parse(block, Some(&mut doctree), line_cursor) {
2246                InlineParsingResult::Nodes(nodes_data) => {
2247                    for data in nodes_data {
2248                        doctree = match doctree.push_data(data) {
2249                            Ok(tree) => tree,
2250                            Err(tree) => {
2251                                return TransitionResult::Failure {
2252                                    message: format!(
2253                                        "Node insertion error on line {}. Computer says no...",
2254                                        line_cursor.sum_total()
2255                                    ),
2256                                    doctree: tree,
2257                                }
2258                            }
2259                        };
2260                    }
2261
2262                    doctree.focus_on_parent()
2263                }
2264                InlineParsingResult::NoNodes => {
2265                    doctree = doctree.focus_on_parent();
2266                    match doctree.pop_child() {
2267                        Some(child) => {
2268                            eprintln!(
2269                                "Removing an empty paragraph from the tree on line {}...",
2270                                line_cursor.sum_total()
2271                            );
2272                        }
2273                        None => {}
2274                    };
2275                    doctree
2276                }
2277            };
2278
2279            if literal_block_next {
2280                return TransitionResult::Success {
2281                    doctree: doctree,
2282                    push_or_pop: PushOrPop::Push(vec![State::LiteralBlock]),
2283                    line_advance: LineAdvance::Some(1),
2284                };
2285            } else {
2286                return TransitionResult::Success {
2287                    doctree: doctree,
2288                    push_or_pop: PushOrPop::Neither,
2289                    line_advance: LineAdvance::Some(1),
2290                };
2291            }
2292        }
2293
2294        IndentationMatch::TooMuch => {
2295            doctree = match doctree.push_data_and_focus(
2296                    TreeNodeType::BlockQuote {
2297                    body_indent: detected_indent,
2298                }
2299            ) {
2300                Ok(tree) => tree,
2301                Err(tree) => {
2302                    return TransitionResult::Failure {
2303                        message: format!(
2304                            "Node insertion error on line {}. Computer says no...",
2305                            line_cursor.sum_total()
2306                        ),
2307                        doctree: tree,
2308                    }
2309                }
2310            };
2311            return TransitionResult::Success {
2312                doctree: doctree,
2313                push_or_pop: PushOrPop::Push(vec![State::BlockQuote]),
2314                line_advance: LineAdvance::None,
2315            };
2316        }
2317
2318        _ => {
2319            return TransitionResult::Success {
2320                doctree: doctree.focus_on_parent(),
2321                push_or_pop: PushOrPop::Pop,
2322                line_advance: LineAdvance::None,
2323            };
2324        }
2325    }
2326}