markdown_it/plugins/extra/
heading_anchors.rs1use std::fmt::Debug;
18
19use crate::parser::core::CoreRule;
20use crate::parser::extset::MarkdownItExt;
21use crate::plugins::cmark::block::heading::ATXHeading;
22use crate::plugins::cmark::block::lheading::SetextHeader;
23use crate::{MarkdownIt, Node};
24
25pub fn add(md: &mut MarkdownIt, slugify: fn (&str) -> String) {
26 md.ext.insert(SlugifyFunction(slugify));
27 md.add_rule::<AddHeadingAnchors>();
28}
29
30pub fn simple_slugify_fn(s: &str) -> String {
33 s.chars().map(|x| {
34 if x.is_alphanumeric() {
35 x.to_ascii_lowercase()
36 } else {
37 '-'
38 }
39 }).collect()
40}
41
42#[derive(Clone, Copy)]
43struct SlugifyFunction(fn (&str) -> String);
44impl MarkdownItExt for SlugifyFunction {}
45
46impl Default for SlugifyFunction {
47 fn default() -> Self {
48 Self(simple_slugify_fn)
49 }
50}
51
52impl Debug for SlugifyFunction {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("SlugifyFunction").finish()
55 }
56}
57
58pub struct AddHeadingAnchors;
59impl CoreRule for AddHeadingAnchors {
60 fn run(root: &mut Node, md: &MarkdownIt) {
61 let slugify = md.ext.get::<SlugifyFunction>().copied().unwrap_or_default().0;
62
63 root.walk_mut(|node, _| {
64 if node.is::<ATXHeading>() || node.is::<SetextHeader>() {
65 node.attrs.push(("id", slugify(&node.collect_text())));
66 }
67 });
68 }
69}