| 1 | //! Add source mapping to resulting HTML, looks like this: `<stuff data-sourcepos="1:1-2:3">`. |
| 2 | //! ```rust |
| 3 | //! let md = &mut markdown_it::MarkdownIt::new(); |
| 4 | //! markdown_it::plugins::cmark::add(md); |
| 5 | //! markdown_it::plugins::sourcepos::add(md); |
| 6 | //! |
| 7 | //! let html = md.parse("# hello").render(); |
| 8 | //! assert_eq!(html.trim(), r#"<h1 data-sourcepos="1:1-1:7">hello</h1>"#); |
| 9 | //! ``` |
| 10 | use crate::common::sourcemap::SourceWithLineStarts; |
| 11 | use crate::parser::block::builtin::BlockParserRule; |
| 12 | use crate::parser::core::{CoreRule, Root}; |
| 13 | use crate::parser::inline::builtin::InlineParserRule; |
| 14 | use crate::{MarkdownIt, Node}; |
| 15 | |
| 16 | pub fn add(md: &mut MarkdownIt) { |
| 17 | md.add_rule::<SyntaxPosRule>() |
| 18 | .after::<BlockParserRule>() |
| 19 | .after::<InlineParserRule>(); |
| 20 | } |
| 21 | |
| 22 | #[doc(hidden)] |
| 23 | pub struct SyntaxPosRule; |
| 24 | impl CoreRule for SyntaxPosRule { |
| 25 | fn run(root: &mut Node, _: &MarkdownIt) { |
| 26 | let source = root.cast::<Root>().unwrap().content.as_str(); |
| 27 | let mapping = SourceWithLineStarts::new(source); |
| 28 | |
| 29 | root.walk_mut(|node, _| { |
| 30 | if let Some(map) = node.srcmap { |
| 31 | let ((startline, startcol), (endline, endcol)) = map.get_positions(&mapping); |
| 32 | node.attrs.push(("data-sourcepos", format!("{}:{}-{}:{}", startline, startcol, endline, endcol))); |
| 33 | } |
| 34 | }); |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | |
| 39 | #[cfg(test)] |
| 40 | mod tests { |
| 41 | #[test] |
| 42 | fn header_test() { |
| 43 | // same as doctest, keep in sync! |
| 44 | // used for code coverage and quicker rust-analyzer hints |
| 45 | let md = &mut crate::MarkdownIt::new(); |
| 46 | crate::plugins::cmark::add(md); |
| 47 | crate::plugins::sourcepos::add(md); |
| 48 | |
| 49 | let html = md.parse("# hello").render(); |
| 50 | assert_eq!(html.trim(), r#"<h1 data-sourcepos="1:1-1:7">hello</h1>"#); |
| 51 | } |
| 52 | } |