wayver's git archive


an obsidian renderer
git clone https://git.wayver.dev/sable

sable-markdown/src/parser/inline/mod.rs@main

raw
Date Commit Message Author Files + -
2026-02-23 21:59 beginning on port over some of the changes from markdown-ppp since the f... wayverd 11 527 15
...

1mod autolink;
2mod code_span;
3mod emphasis;
4mod environment_variable;
5mod footnote_reference;
6mod hard_newline;
7mod html_entity;
8mod image;
9mod inline_link;
10mod reference_link;
11mod strikethrough;
12mod tag;
13mod text;
14mod wikilink;
15
16#[cfg(test)]
17mod tests;
18
19use nom::{
20    IResult, Parser,
21    branch::alt,
22    combinator::map,
23    multi::{many0, many1},
24};
25
26use crate::ast::Inline;
27
28/// Merges consecutive Text elements into a single Text element
29fn merge_consecutive_text_elements(inlines: Vec<Inline>) -> Vec<Inline> {
30    let mut result = Vec::new();
31    let mut current_text = String::new();
32    let mut has_text = false;
33
34    for inline in inlines {
35        match inline {
36            Inline::Text(text) => {
37                current_text.push_str(&text);
38                has_text = true;
39            }
40            other => {
41                // If we have accumulated text, add it to result
42                if has_text {
43                    result.push(Inline::Text(current_text.clone()));
44                    current_text.clear();
45                    has_text = false;
46                }
47                // Add the non-text element
48                result.push(other);
49            }
50        }
51    }
52
53    // Don't forget the last accumulated text
54    if has_text {
55        result.push(Inline::Text(current_text));
56    }
57
58    result
59}
60
61pub(super) fn inline_many0<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, Vec<Inline>> {
62    move |input: &'a str| {
63        let (input, list_of_lists) = many0(inline()).parse(input)?;
64        let r: Vec<_> = list_of_lists.into_iter().flatten().collect();
65        let merged = merge_consecutive_text_elements(r);
66        Ok((input, merged))
67    }
68}
69
70pub(super) fn inline_many1<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, Vec<Inline>> {
71    move |input: &'a str| {
72        let (input, list_of_lists) = many1(inline()).parse(input)?;
73        let r: Vec<_> = list_of_lists.into_iter().flatten().collect();
74        let merged = merge_consecutive_text_elements(r);
75        Ok((input, merged))
76    }
77}
78
79pub(super) fn inline<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, Vec<Inline>> {
80    move |input: &'a str| {
81        alt((
82            map(wikilink::wikilink(), Inline::Wikilink),
83            map(autolink::autolink, Inline::Autolink),
84            map(inline_link::inline_link(), Inline::Link),
85            footnote_reference::footnote_reference,
86            reference_link::reference_link(),
87            hard_newline::hard_newline,
88            image::image(),
89            map(code_span::code_span, Inline::Code),
90            environment_variable::environment_variable,
91            emphasis::emphasis(),
92            strikethrough::strikethrough(),
93            map(tag::tag(), Inline::Tag),
94            text::text(),
95        ))
96        .map(|v| vec![v])
97        .parse(input)
98    }
99}
100