//! A [markdown_it] plugin for parsing footnotes //! //! ``` //! let parser = &mut markdown_it::MarkdownIt::new(); //! md_footnote::add(parser); //! let node = parser.parse("[^note]\n\n[^note]: A footnote\n"); //! ``` use std::collections::HashMap; use markdown_it::{MarkdownIt, parser::extset::RootExt}; pub mod back_refs; pub mod collect; pub mod definitions; pub mod inline; pub mod references; // Silence lints #[cfg(test)] use md_dev as _; #[cfg(test)] use testing as _; /// Add the full footnote plugin to the parser pub fn add(md: &mut MarkdownIt) { definitions::add(md); references::add(md); inline::add(md); collect::add(md); back_refs::add(md); } #[derive(Debug, Default)] /// The set of parsed footnote definition labels, /// stored in the root node. pub struct FootnoteMap { def_counter: usize, ref_counter: usize, label_to_def: HashMap, def_to_refs: HashMap>, } impl RootExt for FootnoteMap {} impl FootnoteMap { /// Create an ID for the definition, /// or return None if a definition already exists for the label pub fn add_def(&mut self, label: &str) -> Option { if self.label_to_def.contains_key(label) { return None; } self.def_counter += 1; self.label_to_def .insert(String::from(label), self.def_counter); Some(self.def_counter) } /// Create an ID for the reference and return (def_id, ref_id), /// or return None if no definition exists for the label pub fn add_ref(&mut self, label: &str) -> Option<(usize, usize)> { match self.label_to_def.get(label) { Some(def_id) => { self.ref_counter += 1; // self.def_to_refs.get_mut(&def_id).unwrap().push(self.ref_counter); match self.def_to_refs.get_mut(def_id) { Some(refs) => refs.push(self.ref_counter), None => { self.def_to_refs.insert(*def_id, vec![self.ref_counter]); } } Some((*def_id, self.ref_counter)) } None => None, } } /// Add an inline definition and return (def_id, ref_id) pub fn add_inline_def(&mut self) -> (usize, usize) { self.def_counter += 1; self.ref_counter += 1; self.def_to_refs .insert(self.def_counter, vec![self.ref_counter]); (self.def_counter, self.ref_counter) } /// return the IDs of all references to the given definition ID pub fn referenced_by(&self, def_id: usize) -> Vec { match self.def_to_refs.get(&def_id) { Some(ids) => ids.clone(), None => Vec::new(), } } }