| 1 | // Replaces `(\/)-------(\/)` with a nice picture. |
| 2 | |
| 3 | use markdown_it::parser::block::{BlockRule, BlockState}; |
| 4 | use markdown_it::{MarkdownIt, Node, NodeValue, Renderer}; |
| 5 | |
| 6 | const CRAB_CLAW : &str = r#"(\/)"#; |
| 7 | const 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. |
| 11 | pub struct BlockFerris; |
| 12 | |
| 13 | impl 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. |
| 31 | struct FerrisBlockScanner; |
| 32 | |
| 33 | impl 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 | |
| 69 | pub fn add(md: &mut MarkdownIt) { |
| 70 | // insert this rule into block subparser |
| 71 | md.block.add_rule::<FerrisBlockScanner>(); |
| 72 | } |