mdtranslation/
utils.rs

1use pulldown_cmark::{Event, Tag};
2
3mod inline_group;
4mod nested_inline_group;
5mod puncttable;
6mod vec_map;
7mod vec_set;
8
9pub(crate) use inline_group::{InlineGroupEvent, InlineGroupIteratorExt};
10pub(crate) use nested_inline_group::{NestedInlineGroupEvent, NestedInlineGroupIteratorExt};
11pub(crate) use vec_map::VecMap;
12pub(crate) use vec_set::VecSet;
13
14pub(crate) fn is_whitespace(ch: char) -> bool {
15    ch.is_whitespace()
16}
17pub(crate) use puncttable::is_punctuation;
18
19pub(crate) fn normalize_inlines(inlines: &mut Vec<Event<'_>>, conservative: bool) {
20    let mut idx = 0;
21    while let Some(first_item) = inlines.get(idx) {
22        if let Event::Text(first_text) = first_item {
23            if !conservative || !first_text.ends_with('\n') {
24                let mut idx_end = idx + 1;
25                let mut replace_text = None;
26                while let Some(Event::Text(t)) = inlines.get(idx_end) {
27                    if conservative && t.ends_with('\n') {
28                        break;
29                    }
30                    replace_text.get_or_insert_with(|| first_text.to_string());
31                    replace_text.as_mut().unwrap().extend(t.chars());
32                    idx_end += 1;
33                }
34                if let Some(replace_text) = replace_text {
35                    inlines.splice(idx..idx_end, Some(Event::Text(replace_text.into())));
36                }
37            }
38        }
39        idx += 1;
40    }
41}
42
43pub(crate) fn is_block_event<'event>(
44    event: &pulldown_cmark::Event<'event>,
45    context: &Vec<Tag<'event>>,
46) -> bool {
47    match event {
48        Event::Start(tag) | Event::End(tag) => {
49            let tag: &pulldown_cmark::Tag = tag;
50            match tag {
51                Tag::Paragraph
52                | Tag::Heading(_)
53                | Tag::BlockQuote
54                | Tag::CodeBlock(_)
55                | Tag::List(_)
56                | Tag::Item
57                | Tag::Table(_)
58                | Tag::TableHead
59                | Tag::TableRow
60                | Tag::TableCell => true,
61                Tag::FootnoteDefinition(_) => {
62                    // FIXME: verify if it is correct to regard it as block-level.
63                    // since it is actually out-of-band.
64                    true
65                }
66                Tag::Emphasis
67                | Tag::Strong
68                | Tag::Strikethrough
69                | Tag::Link(_, _, _)
70                | Tag::Image(_, _, _) => false,
71            }
72        }
73        Event::Html(text) => context.is_empty() || text.ends_with("\n"),
74        Event::Rule => true,
75        Event::Text(_)
76        | Event::Code(_)
77        | Event::FootnoteReference(_)
78        | Event::SoftBreak
79        | Event::HardBreak
80        | Event::TaskListMarker(_) => false,
81    }
82}