ferris/block_rule.rs
1// Replaces `(\/)-------(\/)` with a nice picture.
2
3use markdown_it::parser::block::{BlockRule, BlockState};
4use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
5
6const CRAB_CLAW : &str = r#"(\/)"#;
7const CRAB_URL : &str = "https://upload.wikimedia.org/wikipedia/commons/0/0f/Original_Ferris.svg";
8
9#[derive(Debug)]
10// This is a structure that represents your custom Node in AST.
11pub struct BlockFerris;
12
13impl NodeValue for BlockFerris {
14 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
15 // build attributes for `div`
16 let mut attrs_div = node.attrs.clone();
17 attrs_div.push(("class", "ferris-block".into()));
18
19 // build attributes for `img`
20 let attrs_img = vec![("src", CRAB_URL.into())];
21
22 fmt.cr(); // linebreak, multiples get merged
23 fmt.open("div", &attrs_div); // opening tag, `<div>`
24 fmt.self_close("img", &attrs_img); // `<img>`
25 fmt.close("div"); // closing tag, `</div>`
26 fmt.cr();
27 }
28}
29
30// This is an extension for the block subparser.
31struct FerrisBlockScanner;
32
33impl BlockRule for FerrisBlockScanner {
34 // This is a custom function that will be invoked on every line
35 // in a block context.
36 //
37 // It should get a line number `state.line` and report if your
38 // custom structure appears there.
39 //
40 // If custom structure is found, it:
41 // - creates a new `Node` in AST
42 // - increments `state.line` to a position after this node
43 // - returns true
44 //
45 // In "silent mode" (when `silent=true`) you aren't allowed to
46 // create any nodes, should only increment `state.line`.
47 //
48 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
49 // get contents of a line number `state.line` and check it
50 let line = state.get_line(state.line).trim();
51 if !line.starts_with(CRAB_CLAW) { return None; }
52 if !line.ends_with(CRAB_CLAW) { return None; }
53
54 // require any number of `-` in between, but no less than 4
55 if line.len() < CRAB_CLAW.len() * 2 + 4 { return None; }
56
57 // and make sure no other characters are present there
58 let dashes = &line[CRAB_CLAW.len()..line.len()-CRAB_CLAW.len()];
59 if dashes.chars().any(|c| c != '-') { return None; }
60
61 // return new node and number of lines it occupies
62 Some((
63 Node::new(BlockFerris),
64 1,
65 ))
66 }
67}
68
69pub fn add(md: &mut MarkdownIt) {
70 // insert this rule into block subparser
71 md.block.add_rule::<FerrisBlockScanner>();
72}