diff --git a/Cargo.lock b/Cargo.lock index 6fc7192..205ec40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "snekdown" -version = "0.3.1" +version = "0.4.0" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index d015370..e52a1b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.3.1" +version = "0.4.0" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" diff --git a/src/format/html.rs b/src/format/html.rs index 5b75040..fc5260f 100644 --- a/src/format/html.rs +++ b/src/format/html.rs @@ -35,6 +35,7 @@ impl ToHtml for Inline { match self { Inline::Text(text) => text.to_html(), Inline::Ruler(ruler) => ruler.to_html(), + Inline::Anchor(anchor) => anchor.to_html(), } } } @@ -119,7 +120,12 @@ impl ToHtml for Section { impl ToHtml for Header { fn to_html(&self) -> String { - format!("{1}", self.size, self.line.to_html()) + format!( + "{2}", + self.size, + encode_attribute(self.anchor.as_str()), + self.line.to_html() + ) } } @@ -352,3 +358,13 @@ impl ToHtml for Placeholder { } } } + +impl ToHtml for Anchor { + fn to_html(&self) -> String { + format!( + "{}", + encode_attribute(self.reference.as_str()), + self.description.to_html() + ) + } +} diff --git a/src/parsing/elements.rs b/src/parsing/elements.rs index 6d4654c..41191c9 100644 --- a/src/parsing/elements.rs +++ b/src/parsing/elements.rs @@ -46,6 +46,7 @@ pub enum Block { pub enum Inline { Text(Text), Ruler(Ruler), + Anchor(Anchor), } #[derive(Clone, Debug)] @@ -66,6 +67,7 @@ pub struct Section { pub struct Header { pub(crate) size: u8, pub(crate) line: Inline, + pub(crate) anchor: String, } #[derive(Clone, Debug)] @@ -200,6 +202,12 @@ pub struct Placeholder { pub(crate) value: Option, } +#[derive(Clone, Debug)] +pub struct Anchor { + pub(crate) description: Box, + pub(crate) reference: String, +} + // implementations impl Document { @@ -246,7 +254,7 @@ impl Document { list.ordered = true; self.elements.iter().for_each(|e| match e { Block::Section(sec) => { - let mut item = ListItem::new(sec.header.line.clone(), 1, true); + let mut item = ListItem::new(Inline::Anchor(sec.header.get_anchor()), 1, true); item.children.append(&mut sec.get_toc_list().items); list.add_item(item); } @@ -299,7 +307,7 @@ impl Section { let mut list = List::new(); self.elements.iter().for_each(|e| { if let Block::Section(sec) = e { - let mut item = ListItem::new(sec.header.line.clone(), 1, true); + let mut item = ListItem::new(Inline::Anchor(sec.header.get_anchor()), 1, true); item.children.append(&mut sec.get_toc_list().items); list.add_item(item); } @@ -309,6 +317,23 @@ impl Section { } } +impl Header { + pub fn new(content: Inline, anchor: String) -> Self { + Self { + size: 0, + anchor, + line: content, + } + } + + pub fn get_anchor(&self) -> Anchor { + Anchor { + description: Box::new(self.line.clone()), + reference: self.anchor.clone(), + } + } +} + impl Paragraph { pub fn new() -> Self { Self { diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 445590b..9af03b6 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -455,10 +455,14 @@ impl Parser { /// parses the header of a section fn parse_header(&mut self) -> Result { - Ok(Header { - size: 0, - line: self.parse_inline()?, - }) + let start_index = self.index; + let line = self.parse_inline()?; + let mut anchor = String::new(); + self.text[start_index..self.index] + .iter() + .for_each(|e| anchor.push(*e)); + anchor.retain(|c| !c.is_whitespace()); + Ok(Header::new(line, anchor)) } /// parses a code block