| 1 | use markdown_it::parser::inline::{InlineRule, InlineState}; |
| 2 | use markdown_it::{Node, NodeValue, Renderer}; |
| 3 | |
| 4 | /// Custom autolink for relative paths (./path and ../path) |
| 5 | #[derive(Debug)] |
| 6 | pub struct RelativeAutolink { |
| 7 | pub href: String, |
| 8 | } |
| 9 | |
| 10 | impl NodeValue for RelativeAutolink { |
| 11 | fn render(&self, _node: &Node, fmt: &mut dyn Renderer) { |
| 12 | fmt.open("a", &[("href", self.href.clone())]); |
| 13 | fmt.text(&self.href); |
| 14 | fmt.close("a"); |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | /// Parse relative path autolinks: <./path> and <../path> |
| 19 | pub struct Rule; |
| 20 | |
| 21 | impl InlineRule for Rule { |
| 22 | const MARKER: char = '<'; |
| 23 | |
| 24 | fn run(state: &mut InlineState) -> Option<(Node, usize)> { |
| 25 | let input = &state.src[state.pos..state.pos_max]; |
| 26 | |
| 27 | if !input.starts_with('<') { |
| 28 | return None; |
| 29 | } |
| 30 | |
| 31 | let after_bracket = &input[1..]; |
| 32 | |
| 33 | if !after_bracket.starts_with("./") && !after_bracket.starts_with("../") { |
| 34 | return None; |
| 35 | } |
| 36 | |
| 37 | let end = after_bracket.find('>')?; |
| 38 | let href = &after_bracket[..end]; |
| 39 | let node = Node::new(RelativeAutolink { |
| 40 | href: href.to_string(), |
| 41 | }); |
| 42 | |
| 43 | // Return node and number of characters consumed (including < and >) |
| 44 | Some((node, end + 2)) |
| 45 | } |
| 46 | } |