specfold_core/
markdown.rs1pub mod body;
4pub mod link_dest;
5
6#[derive(Debug, Clone, Default)]
10pub struct Fence(Option<(char, usize)>);
11
12impl Fence {
13 pub fn step(&mut self, line: &str) -> bool {
15 let t = line.trim_start();
16 let run = |ch: char| t.chars().take_while(|c| *c == ch).count();
17 match self.0 {
18 Some((ch, n)) => {
19 let m = run(ch);
20 if m >= n && t[m * ch.len_utf8()..].trim().is_empty() {
21 self.0 = None;
22 return true;
23 }
24 false
25 }
26 None => {
27 for ch in ['`', '~'] {
28 let n = run(ch);
29 if n >= 3 && !(ch == '`' && t[n..].contains('`')) {
31 self.0 = Some((ch, n));
32 return true;
33 }
34 }
35 false
36 }
37 }
38 }
39
40 pub fn inside(&self) -> bool {
42 self.0.is_some()
43 }
44}
45
46pub fn atx(line: &str) -> Option<(usize, &str)> {
50 let indent = line.len() - line.trim_start_matches(' ').len();
51 if indent > 3 {
52 return None;
53 }
54 let t = &line[indent..];
55 let n = t.chars().take_while(|c| *c == '#').count();
56 let rest = &t[n..];
57 if !(1..=6).contains(&n) || !(rest.is_empty() || rest.starts_with([' ', '\t'])) {
58 return None;
59 }
60 let text = rest.trim();
62 let open = text.trim_end_matches('#');
63 let text = if open.is_empty() || open.ends_with([' ', '\t']) { open.trim_end() } else { text };
64 Some((n, text))
65}
66
67pub mod counts;
68pub mod frontmatter;
69pub mod sections;
70pub mod tables;
71pub mod tasks;