markdown_it/plugins/extra/
front_matter.rs1use crate::parser::block::{BlockRule, BlockState};
2use crate::parser::extset::{MarkdownItExt, RootExt};
3use crate::{MarkdownIt, Node};
4
5pub const DEFAULT_MAX_LINES: usize = 256;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum FrontMatterKind {
10 Yaml,
11 Toml,
12}
13
14#[derive(Debug, Clone)]
15pub struct FrontMatter {
16 pub kind: FrontMatterKind,
17 pub raw: String,
18 pub start_line: usize,
19 pub end_line: usize,
20}
21
22impl FrontMatter {
23 pub fn parse_with<T, E>(
24 &self,
25 parser: impl FnOnce(FrontMatterKind, &str) -> Result<T, E>,
26 ) -> Result<T, E> {
27 parser(self.kind, &self.raw)
28 }
29}
30
31impl RootExt for FrontMatter {}
32
33#[derive(Debug, Clone, Copy)]
34struct FrontMatterSettings {
35 max_lines: usize,
36}
37
38impl MarkdownItExt for FrontMatterSettings {}
39
40pub struct FrontMatterScanner;
41
42impl BlockRule for FrontMatterScanner {
43 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
44 if state.line != 0 {
45 return None;
46 }
47 if state.line_indent(state.line) != 0 {
48 return None;
49 }
50
51 let opener = state.get_line(0).trim_end();
52 let (kind, closer) = match opener {
53 "---" => (FrontMatterKind::Yaml, "---"),
54 "+++" => (FrontMatterKind::Toml, "+++"),
55 _ => return None,
56 };
57
58 let max_lines = state
59 .md
60 .ext
61 .get::<FrontMatterSettings>()
62 .map(|settings| settings.max_lines)
63 .unwrap_or(DEFAULT_MAX_LINES);
64
65 let line_limit = state.line_max.min(max_lines);
66 let mut end_line = 1;
67 while end_line < line_limit {
68 if state.line_indent(end_line) == 0 && state.get_line(end_line).trim_end() == closer {
69 let (raw, _) = state.get_lines(1, end_line, 0, false);
70 state.root_ext.insert(FrontMatter {
71 kind,
72 raw,
73 start_line: 0,
74 end_line,
75 });
76 return Some((Node::default(), end_line + 1));
77 }
78
79 end_line += 1;
80 }
81
82 None
83 }
84}
85
86pub fn add(md: &mut MarkdownIt) {
87 add_with_max_lines(md, DEFAULT_MAX_LINES);
88}
89
90pub fn add_with_max_lines(md: &mut MarkdownIt, max_lines: usize) {
91 md.ext.insert(FrontMatterSettings { max_lines });
92 md.block.add_rule::<FrontMatterScanner>().before_all();
93}
94
95pub fn set_max_lines(md: &mut MarkdownIt, max_lines: usize) {
96 md.ext.insert(FrontMatterSettings { max_lines });
97}