Files
webpage/crates/lib/md-footnote/src/lib.rs
2025-11-06 21:16:09 -08:00

90 lines
2.4 KiB
Rust

//! 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<String, usize>,
def_to_refs: HashMap<usize, Vec<usize>>,
}
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<usize> {
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<usize> {
match self.def_to_refs.get(&def_id) {
Some(ids) => ids.clone(),
None => Vec::new(),
}
}
}