Skip to content

Commit

Permalink
Merge branch 'main' of github.com:kivikakk/comrak into make-comrak-ex…
Browse files Browse the repository at this point in the history
…t-options-non-exhaustive
  • Loading branch information
CosmicHorrorDev committed May 21, 2023
2 parents 0bf11c7 + 127da5c commit bdb030e
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 73 deletions.
13 changes: 7 additions & 6 deletions src/cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,11 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
NodeValue::Table(..) => self.format_table(entering),
NodeValue::TableRow(..) => self.format_table_row(entering),
NodeValue::TableCell => self.format_table_cell(node, entering),
NodeValue::FootnoteDefinition(_) => self.format_footnote_definition(entering),
NodeValue::FootnoteReference(ref r) => {
self.format_footnote_reference(r.as_bytes(), entering)
NodeValue::FootnoteDefinition(ref nfd) => {
self.format_footnote_definition(&nfd.name, entering)
}
NodeValue::FootnoteReference(ref nfr) => {
self.format_footnote_reference(nfr.name.as_bytes(), entering)
}
};
true
Expand Down Expand Up @@ -738,11 +740,10 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
}
}
}
fn format_footnote_definition(&mut self, entering: bool) {
fn format_footnote_definition(&mut self, name: &str, entering: bool) {
if entering {
self.footnote_ix += 1;
let footnote_ix = self.footnote_ix;
writeln!(self, "[^{}]:", footnote_ix).unwrap();
writeln!(self, "[^{}]:", name).unwrap();
write!(self.prefix, " ").unwrap();
} else {
let new_len = self.prefix.len() - 4;
Expand Down
58 changes: 39 additions & 19 deletions src/html.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! The HTML renderer for the CommonMark AST, as well as helper functions.
use crate::ctype::isspace;
use crate::nodes::{AstNode, ListType, NodeCode, NodeValue, TableAlignment};
use crate::nodes::{
AstNode, ListType, NodeCode, NodeFootnoteDefinition, NodeValue, TableAlignment,
};
use crate::parser::{ComrakOptions, ComrakPlugins};
use crate::scanners;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -701,13 +703,13 @@ impl<'o> HtmlFormatter<'o> {
self.render_sourcepos(node)?;
self.output.write_all(b">")?;
} else {
if matches!(
node.parent().unwrap().data.borrow().value,
NodeValue::FootnoteDefinition(..)
) && node.next_sibling().is_none()
if let NodeValue::FootnoteDefinition(nfd) =
&node.parent().unwrap().data.borrow().value
{
self.output.write_all(b" ")?;
self.put_footnote_backref()?;
if node.next_sibling().is_none() {
self.output.write_all(b" ")?;
self.put_footnote_backref(nfd)?;
}
}
self.output.write_all(b"</p>\n")?;
}
Expand Down Expand Up @@ -931,7 +933,7 @@ impl<'o> HtmlFormatter<'o> {
self.output.write_all(b"</td>")?;
}
}
NodeValue::FootnoteDefinition(_) => {
NodeValue::FootnoteDefinition(ref nfd) => {
if entering {
if self.footnote_ix == 0 {
self.output.write_all(b"<section")?;
Expand All @@ -942,21 +944,27 @@ impl<'o> HtmlFormatter<'o> {
self.footnote_ix += 1;
self.output.write_all(b"<li")?;
self.render_sourcepos(node)?;
writeln!(self.output, " id=\"fn-{}\">", self.footnote_ix)?;
writeln!(self.output, " id=\"fn-{}\">", nfd.name)?;
} else {
if self.put_footnote_backref()? {
if self.put_footnote_backref(nfd)? {
self.output.write_all(b"\n")?;
}
self.output.write_all(b"</li>\n")?;
}
}
NodeValue::FootnoteReference(ref r) => {
NodeValue::FootnoteReference(ref nfr) => {
if entering {
let mut ref_id = format!("fnref-{}", nfr.name);

self.output.write_all(b"<sup")?;
self.render_sourcepos(node)?;

if nfr.ref_num > 1 {
ref_id = format!("{}-{}", ref_id, nfr.ref_num);
}
write!(
self.output, " class=\"footnote-ref\"><a href=\"#fn-{}\" id=\"fnref-{}\" data-footnote-ref>{}</a></sup>",
r, r, r
self.output, " class=\"footnote-ref\"><a href=\"#fn-{}\" id=\"{}\" data-footnote-ref>{}</a></sup>",
nfr.name, ref_id, nfr.ix,
)?;
}
}
Expand Down Expand Up @@ -993,17 +1001,29 @@ impl<'o> HtmlFormatter<'o> {
Ok(())
}

fn put_footnote_backref(&mut self) -> io::Result<bool> {
fn put_footnote_backref(&mut self, nfd: &NodeFootnoteDefinition) -> io::Result<bool> {
if self.written_footnote_ix >= self.footnote_ix {
return Ok(false);
}

self.written_footnote_ix = self.footnote_ix;
write!(
self.output,
"<a href=\"#fnref-{}\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a>",
self.footnote_ix
)?;

let mut ref_suffix = String::new();
let mut superscript = String::new();

for ref_num in 1..=nfd.total_references {
if ref_num > 1 {
ref_suffix = format!("-{}", ref_num);
superscript = format!("<sup class=\"footnote-ref\">{}</sup>", ref_num);
write!(self.output, " ")?;
}

write!(
self.output,
"<a href=\"#fnref-{}{}\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"{}{}\" aria-label=\"Back to reference {}{}\">↩{}</a>",
nfd.name, ref_suffix, self.footnote_ix, ref_suffix, self.footnote_ix, ref_suffix, superscript
)?;
}
Ok(true)
}
}
31 changes: 27 additions & 4 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub enum NodeValue {

/// **Block**. A footnote definition. The `String` is the footnote's name.
/// Contains other **blocks**.
FootnoteDefinition(String),
FootnoteDefinition(NodeFootnoteDefinition),

/// **Block**. A [table](https://github.github.com/gfm/#tables-extension-) per the GFM spec.
/// Contains table rows.
Expand Down Expand Up @@ -145,8 +145,8 @@ pub enum NodeValue {
/// **Inline**. An [image](https://github.github.com/gfm/#images).
Image(NodeLink),

/// **Inline**. A footnote reference; the `String` is the referent footnote's name.
FootnoteReference(String),
/// **Inline**. A footnote reference.
FootnoteReference(NodeFootnoteReference),

#[cfg(feature = "shortcodes")]
/// **Inline**. An Emoji character generated from a shortcode. Enable with feature "shortcodes".
Expand Down Expand Up @@ -329,6 +329,29 @@ pub struct NodeHtmlBlock {
pub literal: String,
}

/// The metadata of a footnote definition.
#[derive(Debug, Default, Clone)]
pub struct NodeFootnoteDefinition {
/// The name of the footnote.
pub name: String,

/// Total number of references to this footnote
pub total_references: u32,
}

/// The metadata of a footnote reference.
#[derive(Debug, Default, Clone)]
pub struct NodeFootnoteReference {
/// The name of the footnote.
pub name: String,

/// The index of reference to the same footnote
pub ref_num: u32,

/// The index of the footnote in the document.
pub ix: u32,
}

impl NodeValue {
/// Indicates whether this node is a block node or inline node.
pub fn block(&self) -> bool {
Expand Down Expand Up @@ -422,7 +445,7 @@ impl NodeValue {
NodeValue::FrontMatter(_) => "frontmatter",
NodeValue::TaskItem { .. } => "taskitem",
NodeValue::Superscript => "superscript",
NodeValue::FootnoteReference(_) => "footnote_reference",
NodeValue::FootnoteReference(..) => "footnote_reference",
#[cfg(feature = "shortcodes")]
NodeValue::ShortCode(_) => "shortcode",
}
Expand Down
8 changes: 6 additions & 2 deletions src/parser/inlines.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::arena_tree::Node;
use crate::ctype::{ispunct, isspace};
use crate::entity;
use crate::nodes::{Ast, AstNode, NodeCode, NodeLink, NodeValue, Sourcepos};
use crate::nodes::{Ast, AstNode, NodeCode, NodeFootnoteReference, NodeLink, NodeValue, Sourcepos};
#[cfg(feature = "shortcodes")]
use crate::parser::shortcodes::NodeShortCode;
use crate::parser::{
Expand Down Expand Up @@ -1229,7 +1229,11 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
let text = text.unwrap();
if text.len() > 1 && text.as_bytes()[0] == b'^' {
let inl = self.make_inline(
NodeValue::FootnoteReference(text[1..].to_string()),
NodeValue::FootnoteReference(NodeFootnoteReference {
name: text[1..].to_string(),
ref_num: 0,
ix: 0,
}),
// Overridden immediately below.
self.pos,
self.pos,
Expand Down
39 changes: 26 additions & 13 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::adapters::SyntaxHighlighterAdapter;
use crate::arena_tree::Node;
use crate::ctype::{isdigit, isspace};
use crate::entity;
use crate::nodes::{self, Sourcepos};
use crate::nodes::{self, NodeFootnoteDefinition, Sourcepos};
use crate::nodes::{
Ast, AstNode, ListDelimType, ListType, NodeCodeBlock, NodeDescriptionItem, NodeHeading,
NodeHtmlBlock, NodeList, NodeValue,
Expand Down Expand Up @@ -259,7 +259,7 @@ pub struct ComrakExtensionOptions {
/// let mut options = ComrakOptions::default();
/// options.extension.footnotes = true;
/// assert_eq!(markdown_to_html("Hi[^x].\n\n[^x]: A greeting.\n", &options),
/// "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup>.</p>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>A greeting. <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n</li>\n</ol>\n</section>\n");
/// "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn-x\" id=\"fnref-x\" data-footnote-ref>1</a></sup>.</p>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-x\">\n<p>A greeting. <a href=\"#fnref-x\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n</li>\n</ol>\n</section>\n");
/// ```
pub footnotes: bool,

Expand Down Expand Up @@ -603,6 +603,8 @@ pub struct Reference {
struct FootnoteDefinition<'a> {
ix: Option<u32>,
node: &'a AstNode<'a>,
name: String,
total_references: u32,
}

impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
Expand Down Expand Up @@ -1083,7 +1085,10 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
self.advance_offset(line, offset, false);
*container = self.add_child(
container,
NodeValue::FootnoteDefinition(str::from_utf8(c).unwrap().to_string()),
NodeValue::FootnoteDefinition(NodeFootnoteDefinition {
name: str::from_utf8(c).unwrap().to_string(),
total_references: 0,
}),
self.first_nonspace + 1,
);
container.data.borrow_mut().internal_offset = matched;
Expand Down Expand Up @@ -1761,10 +1766,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
let mut v = map.into_values().collect::<Vec<_>>();
v.sort_unstable_by(|a, b| a.ix.cmp(&b.ix));
for f in v {
if let Some(ix) = f.ix {
if f.ix.is_some() {
match f.node.data.borrow_mut().value {
NodeValue::FootnoteDefinition(ref mut name) => {
*name = format!("{}", ix);
NodeValue::FootnoteDefinition(ref mut nfd) => {
nfd.name = f.name.to_string();
nfd.total_references = f.total_references;
}
_ => unreachable!(),
}
Expand All @@ -1779,11 +1785,16 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
map: &mut HashMap<String, FootnoteDefinition<'a>>,
) {
match node.data.borrow().value {
NodeValue::FootnoteDefinition(ref name) => {
NodeValue::FootnoteDefinition(ref nfd) => {
node.detach();
map.insert(
strings::normalize_label(name),
FootnoteDefinition { ix: None, node },
strings::normalize_label(&nfd.name),
FootnoteDefinition {
ix: None,
node,
name: strings::normalize_label(&nfd.name),
total_references: 0,
},
);
}
_ => {
Expand All @@ -1802,8 +1813,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
let mut ast = node.data.borrow_mut();
let mut replace = None;
match ast.value {
NodeValue::FootnoteReference(ref mut name) => {
if let Some(ref mut footnote) = map.get_mut(name) {
NodeValue::FootnoteReference(ref mut nfr) => {
if let Some(ref mut footnote) = map.get_mut(&nfr.name) {
let ix = match footnote.ix {
Some(ix) => ix,
None => {
Expand All @@ -1812,9 +1823,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
*ixp
}
};
*name = format!("{}", ix);
footnote.total_references += 1;
nfr.ref_num = footnote.total_references;
nfr.ix = ix;
} else {
replace = Some(name.clone());
replace = Some(nfr.name.clone());
}
}
_ => {
Expand Down
10 changes: 6 additions & 4 deletions src/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ fn exercise_full_api() {
let _: bool = nh.setext;
}
nodes::NodeValue::ThematicBreak => {}
nodes::NodeValue::FootnoteDefinition(name) => {
let _: &String = name;
nodes::NodeValue::FootnoteDefinition(nfd) => {
let _: &String = &nfd.name;
let _: u32 = nfd.total_references;
}
nodes::NodeValue::Table(aligns) => {
let _: &Vec<nodes::TableAlignment> = aligns;
Expand Down Expand Up @@ -207,8 +208,9 @@ fn exercise_full_api() {
nodes::NodeValue::ShortCode(ne) => {
let _: &str = ne.shortcode();
}
nodes::NodeValue::FootnoteReference(name) => {
let _: &String = name;
nodes::NodeValue::FootnoteReference(nfr) => {
let _: String = nfr.name;
let _: u32 = nfr.ix;
}
}
}
Loading

0 comments on commit bdb030e

Please sign in to comment.