ide/
markdown_remove.rs

1//! Removes markdown from strings.
2use pulldown_cmark::{Event, Parser, Tag};
3
4/// Removes all markdown, keeping the text and code blocks
5///
6/// Currently limited in styling, i.e. no ascii tables or lists
7pub(crate) fn remove_markdown(markdown: &str) -> String {
8    let mut out = String::new();
9    out.reserve_exact(markdown.len());
10    let parser = Parser::new(markdown);
11
12    for event in parser {
13        match event {
14            Event::Text(text) | Event::Code(text) => out.push_str(&text),
15            Event::SoftBreak => out.push(' '),
16            Event::HardBreak | Event::Rule | Event::End(Tag::CodeBlock(_)) => out.push('\n'),
17            Event::End(Tag::Paragraph) => out.push_str("\n\n"),
18            Event::Start(_)
19            | Event::End(_)
20            | Event::Html(_)
21            | Event::FootnoteReference(_)
22            | Event::TaskListMarker(_) => (),
23        }
24    }
25
26    if let Some(mut p) = out.rfind(|c| c != '\n') {
27        while !out.is_char_boundary(p + 1) {
28            p += 1;
29        }
30        out.drain(p + 1..);
31    }
32
33    out
34}
35
36#[cfg(test)]
37mod tests {
38    use expect_test::expect;
39
40    use super::*;
41
42    #[test]
43    fn smoke_test() {
44        let res = remove_markdown(
45            r##"
46A function or function pointer.
47
48Functions are the primary way code is executed within Rust. Function blocks, usually just
49called functions, can be defined in a variety of different places and be assigned many
50different attributes and modifiers.
51
52Standalone functions that just sit within a module not attached to anything else are common,
53but most functions will end up being inside [`impl`] blocks, either on another type itself, or
54as a trait impl for that type.
55
56```rust
57fn standalone_function() {
58    // code
59}
60
61pub fn public_thing(argument: bool) -> String {
62    // code
63    # "".to_string()
64}
65
66struct Thing {
67    foo: i32,
68}
69
70impl Thing {
71    pub fn new() -> Self {
72        Self {
73            foo: 42,
74        }
75    }
76}
77```
78
79In addition to presenting fixed types in the form of `fn name(arg: type, ..) -> return_type`,
80functions can also declare a list of type parameters along with trait bounds that they fall
81into.
82
83```rust
84fn generic_function<T: Clone>(x: T) -> (T, T, T) {
85    (x.clone(), x.clone(), x.clone())
86}
87
88fn generic_where<T>(x: T) -> T
89    where T: std::ops::Add<Output = T> + Copy
90{
91    x + x + x
92}
93```
94
95Declaring trait bounds in the angle brackets is functionally identical to using a `where`
96clause. It's up to the programmer to decide which works better in each situation, but `where`
97tends to be better when things get longer than one line.
98
99Along with being made public via `pub`, `fn` can also have an [`extern`] added for use in
100FFI.
101
102For more information on the various types of functions and how they're used, consult the [Rust
103book] or the [Reference].
104
105[`impl`]: keyword.impl.html
106[`extern`]: keyword.extern.html
107[Rust book]: ../book/ch03-03-how-functions-work.html
108[Reference]: ../reference/items/functions.html
109"##,
110        );
111        expect![[r#"
112            A function or function pointer.
113
114            Functions are the primary way code is executed within Rust. Function blocks, usually just called functions, can be defined in a variety of different places and be assigned many different attributes and modifiers.
115
116            Standalone functions that just sit within a module not attached to anything else are common, but most functions will end up being inside impl blocks, either on another type itself, or as a trait impl for that type.
117
118            fn standalone_function() {
119                // code
120            }
121
122            pub fn public_thing(argument: bool) -> String {
123                // code
124                # "".to_string()
125            }
126
127            struct Thing {
128                foo: i32,
129            }
130
131            impl Thing {
132                pub fn new() -> Self {
133                    Self {
134                        foo: 42,
135                    }
136                }
137            }
138
139            In addition to presenting fixed types in the form of fn name(arg: type, ..) -> return_type, functions can also declare a list of type parameters along with trait bounds that they fall into.
140
141            fn generic_function<T: Clone>(x: T) -> (T, T, T) {
142                (x.clone(), x.clone(), x.clone())
143            }
144
145            fn generic_where<T>(x: T) -> T
146                where T: std::ops::Add<Output = T> + Copy
147            {
148                x + x + x
149            }
150
151            Declaring trait bounds in the angle brackets is functionally identical to using a where clause. It's up to the programmer to decide which works better in each situation, but where tends to be better when things get longer than one line.
152
153            Along with being made public via pub, fn can also have an extern added for use in FFI.
154
155            For more information on the various types of functions and how they're used, consult the Rust book or the Reference."#]].assert_eq(&res);
156    }
157
158    #[test]
159    fn on_char_boundary() {
160        expect!["a┘"].assert_eq(&remove_markdown("```text\na┘\n```"));
161        expect!["وقار"].assert_eq(&remove_markdown("```\nوقار\n```\n"));
162    }
163}