1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
//! A [markdown_it] plugin for parsing front matter
//!
//! ```
//! let parser = &mut markdown_it::MarkdownIt::new();
//! markdown_it_front_matter::add(parser);
//! let node = parser.parse("---\nfoo: bar\n---\n");
//! ```
use markdown_it::parser::block::{BlockRule, BlockState};
use markdown_it::parser::core::Root;
use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
#[derive(Debug)]
/// AST node for front-matter
pub struct FrontMatter {
pub content: String,
}
impl NodeValue for FrontMatter {
fn render(&self, _node: &Node, _fmt: &mut dyn Renderer) {
// simply bypass the front-matter in HTML output
}
}
/// Add the front-matter extension to the markdown parser
pub fn add(md: &mut MarkdownIt) {
// insert this rule into block subparser
md.block.add_rule::<FrontMatterBlockScanner>().before_all();
}
/// An extension for the block subparser.
struct FrontMatterBlockScanner;
impl BlockRule for FrontMatterBlockScanner {
fn run(state: &mut BlockState) -> Option<(Node, usize)> {
// check the parent is the document Root
if !state.node.is::<Root>() {
return None;
}
// check we are on the first line of the document
if state.line != 0 {
return None;
}
// check line starts with opening dashes
let opening = state
.get_line(state.line)
.chars()
.take_while(|c| *c == '-')
.collect::<String>();
if !opening.starts_with("---") {
return None;
}
// Search for the end of the block
let mut next_line = state.line;
loop {
next_line += 1;
if next_line >= state.line_max {
return None;
}
let line = state.get_line(next_line);
if line.starts_with(&opening) {
break;
}
}
// get the content of the block
let (content, _) = state.get_lines(state.line + 1, next_line, 0, true);
// return new node and number of lines it occupies
Some((Node::new(FrontMatter { content }), next_line + 1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let parser = &mut markdown_it::MarkdownIt::new();
add(parser);
let node = parser.parse("---\nfoo: bar\n---\nhallo\n");
// println!("{:#?}", ast.children.first());
assert!(node.children.first().unwrap().is::<FrontMatter>());
let text = node.render();
assert_eq!(text, "hallo\n")
}
}