Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//!
//! ```rust
//! use comrak::{Arena, parse_document, format_html, Options};
//! use comrak::nodes::{NodeValue};
//! use comrak::nodes::NodeValue;
//!
//! # fn main() {
//! let arena = Arena::new();
Expand Down Expand Up @@ -91,6 +91,7 @@ pub use html::format_document as format_html;
pub use html::format_document_with_plugins as format_html_with_plugins;
#[doc(inline)]
pub use html::Anchorizer;
pub use nodes::Node;
pub use parser::options;
pub use parser::{parse_document, Options, ResolvedReference};
pub use typed_arena::Arena;
Expand Down
25 changes: 14 additions & 11 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,17 +608,10 @@ impl NodeValue {
}
}

pub(crate) fn accepts_lines(&self) -> bool {
matches!(
*self,
NodeValue::Paragraph
| NodeValue::Heading(..)
| NodeValue::CodeBlock(..)
| NodeValue::Subtext
)
}

pub(crate) fn xml_node_name(&self) -> &'static str {
/// Returns the name this kind of node gets in an XML document. Follows
/// the CommonMark DTD for nodes from the spec, otherwise we just vibe in a
/// stable way.
pub fn xml_node_name(&self) -> &'static str {
match *self {
NodeValue::Document => "document",
NodeValue::BlockQuote => "block_quote",
Expand Down Expand Up @@ -666,6 +659,16 @@ impl NodeValue {
NodeValue::Subtext => "subtext",
}
}

pub(crate) fn accepts_lines(&self) -> bool {
matches!(
*self,
NodeValue::Paragraph
| NodeValue::Heading(..)
| NodeValue::CodeBlock(..)
| NodeValue::Subtext
)
}
}

/// A single node in the CommonMark AST.
Expand Down
11 changes: 9 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,11 @@ where
}
nl.tight = self.determine_list_tight(node);
}
NodeValue::FootnoteDefinition(_) => {
if let Some(candidate_end) = self.fix_zero_end_columns(node) {
ast.sourcepos.end = candidate_end;
}
}
_ => (),
}

Expand Down Expand Up @@ -1768,8 +1773,10 @@ where
};
nfd.name = fd.name.to_string();
nfd.total_references = fd.total_references;
self.root.append(fd.node);
} else {
if !self.options.parse.leave_footnote_definitions {
self.root.append(fd.node);
}
} else if !self.options.parse.leave_footnote_definitions {
fd.node.detach();
}
}
Expand Down
48 changes: 47 additions & 1 deletion src/parser/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ pub struct Extension<'c> {
/// let mut options = Options::default();
/// options.extension.front_matter_delimiter = Some("---".to_owned());
/// let arena = Arena::new();
/// let input ="---\nlayout: post\n---\nText\n";
/// let input = "---\nlayout: post\n---\nText\n";
/// let root = parse_document(&arena, input, &options);
/// let mut buf = String::new();
/// format_commonmark(&root, &options, &mut buf);
Expand Down Expand Up @@ -697,8 +697,54 @@ pub struct Parse<'c> {
/// "<h1>Cool input!</h1>\n<p>Wow look at this cool \
/// <a href=\"https://www.rust-lang.org/\" title=\"The Rust Language\">link</a>. \
/// A [broken link] renders as text.</p>\n");
/// ```
#[cfg_attr(feature = "arbitrary", arbitrary(default))]
pub broken_link_callback: Option<Arc<dyn BrokenLinkCallback + 'c>>,

/// Leave footnote definitions in place in the document tree, rather than
/// reordering them to the end. This will also cause unreferenced footnote
/// definitions to remain in the tree, rather than being removed.
///
/// Comrak's default formatters expect this option to be turned off, so use
/// with care if you use the default formatters.
///
/// ```rust
/// # use comrak::{Arena, parse_document, Node, Options};
/// let mut options = Options::default();
/// options.extension.footnotes = true;
/// let arena = Arena::new();
/// let input = concat!(
/// "Remember burning a CD?[^cd]\n",
/// "\n",
/// "[^cd]: In the Old Days, a 4x burner was considered good.\n",
/// "\n",
/// "[^dvd]: And DVD-RWs? Those were something else.\n",
/// "\n",
/// "Me neither.",
/// );
///
/// fn node_kinds<'a>(doc: Node<'a>) -> Vec<&'static str> {
/// doc.descendants().map(|n| n.data().value.xml_node_name()).collect()
/// }
///
/// let root = parse_document(&arena, input, &options);
/// assert_eq!(
/// node_kinds(root),
/// &["document", "paragraph", "text", "footnote_reference", "paragraph", "text",
/// "footnote_definition", "paragraph", "text"],
/// );
///
/// options.parse.leave_footnote_definitions = true;
///
/// let root = parse_document(&arena, input, &options);
/// assert_eq!(
/// node_kinds(root),
/// &["document", "paragraph", "text", "footnote_reference", "footnote_definition",
/// "paragraph", "text", "footnote_definition", "paragraph", "text", "paragraph", "text"],
/// );
/// ```
#[cfg_attr(feature = "bon", builder(default))]
pub leave_footnote_definitions: bool,
}

/// The type of the callback used when a reference link is encountered with no
Expand Down
57 changes: 46 additions & 11 deletions src/tests/footnotes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,27 +255,62 @@ fn sourcepos() {
[extension.footnotes],
"Here is a footnote reference.[^1]\n"
"\n"
"[^1]: Here is the footnote.\n"
"\n"
"Here is a longer footnote reference.[^ref]\n"
"\n"
"[^1]: Here is the footnote.\n"
"[^ref]: Here is another footnote.\n",
(document (1:1-6:33) [
(document (1:1-7:33) [
(paragraph (1:1-1:33) [
(text (1:1-1:29) "Here is a footnote reference.")
(footnote_reference (1:30-1:33))
])
(paragraph (3:1-3:42) [
(text (3:1-3:36) "Here is a longer footnote reference.")
(footnote_reference (3:37-3:42))
(paragraph (5:1-5:42) [
(text (5:1-5:36) "Here is a longer footnote reference.")
(footnote_reference (5:37-5:42))
])
(footnote_definition (5:1-5:27) [
(paragraph (5:7-5:27) [
(text (5:7-5:27) "Here is the footnote.")
(footnote_definition (3:1-3:27) [
(paragraph (3:7-3:27) [
(text (3:7-3:27) "Here is the footnote.")
])
])
(footnote_definition (6:1-6:33) [
(paragraph (6:9-6:33) [
(text (6:9-6:33) "Here is another footnote.")
(footnote_definition (7:1-7:33) [
(paragraph (7:9-7:33) [
(text (7:9-7:33) "Here is another footnote.")
])
])
])
);
}

#[test]
fn leave_footnote_definitions() {
assert_ast_match!(
[extension.footnotes, parse.leave_footnote_definitions],
"Here is a footnote reference.[^1]\n"
"\n"
"[^1]: Here is the footnote.\n"
"\n"
"Here is a longer footnote reference.[^ref]\n"
"\n"
"[^ref]: Here is another footnote.\n",
(document (1:1-7:33) [
(paragraph (1:1-1:33) [
(text (1:1-1:29) "Here is a footnote reference.")
(footnote_reference (1:30-1:33))
])
(footnote_definition (3:1-3:27) [
(paragraph (3:7-3:27) [
(text (3:7-3:27) "Here is the footnote.")
])
])
(paragraph (5:1-5:42) [
(text (5:1-5:36) "Here is a longer footnote reference.")
(footnote_reference (5:37-5:42))
])
(footnote_definition (7:1-7:33) [
(paragraph (7:9-7:33) [
(text (7:9-7:33) "Here is another footnote.")
])
])
])
Expand Down