Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore inline sourcepos as experimental. #444

Merged
merged 2 commits into from
Jul 12, 2024
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
130 changes: 96 additions & 34 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,31 +725,43 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::Text(ref literal) => {
// No sourcepos.
// Nowhere to put sourcepos.
if entering {
self.escape(literal.as_bytes())?;
}
}
NodeValue::LineBreak => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<br />\n")?;
self.output.write_all(b"<br")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" />\n")?;
}
}
NodeValue::SoftBreak => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
if self.options.render.hardbreaks {
self.output.write_all(b"<br />\n")?;
self.output.write_all(b"<br")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" />\n")?;
} else {
self.output.write_all(b"\n")?;
}
}
}
NodeValue::Code(NodeCode { ref literal, .. }) => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<code>")?;
self.output.write_all(b"<code")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
self.escape(literal.as_bytes())?;
self.output.write_all(b"</code>")?;
}
Expand All @@ -771,47 +783,67 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::Strong => {
// No sourcepos.
// Unreliable sourcepos.
let parent_node = node.parent();
if !self.options.render.gfm_quirks
|| (parent_node.is_none()
|| !matches!(parent_node.unwrap().data.borrow().value, NodeValue::Strong))
{
if entering {
self.output.write_all(b"<strong>")?;
self.output.write_all(b"<strong")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</strong>")?;
}
}
}
NodeValue::Emph => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<em>")?;
self.output.write_all(b"<em")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</em>")?;
}
}
NodeValue::Strikethrough => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<del>")?;
self.output.write_all(b"<del")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</del>")?;
}
}
NodeValue::Superscript => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<sup>")?;
self.output.write_all(b"<sup")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</sup>")?;
}
}
NodeValue::Link(ref nl) => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<a href=\"")?;
self.output.write_all(b"<a")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" href=\"")?;
let url = nl.url.as_bytes();
if self.options.render.unsafe_ || !dangerous_url(url) {
self.escape_href(url)?;
Expand All @@ -826,9 +858,13 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::Image(ref nl) => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<img src=\"")?;
self.output.write_all(b"<img")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" src=\"")?;
let url = nl.url.as_bytes();
if self.options.render.unsafe_ || !dangerous_url(url) {
self.escape_href(url)?;
Expand All @@ -845,6 +881,7 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
#[cfg(feature = "shortcodes")]
NodeValue::ShortCode(ref nsc) => {
// Nowhere to put sourcepos.
if entering {
self.output.write_all(nsc.emoji.as_bytes())?;
}
Expand Down Expand Up @@ -941,14 +978,17 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::FootnoteDefinition(ref nfd) => {
// No sourcepos.
if entering {
if self.footnote_ix == 0 {
self.output.write_all(b"<section")?;
self.render_sourcepos(node)?;
self.output
.write_all(b"<section class=\"footnotes\" data-footnotes>\n<ol>\n")?;
.write_all(b" class=\"footnotes\" data-footnotes>\n<ol>\n")?;
}
self.footnote_ix += 1;
self.output.write_all(b"<li id=\"fn-")?;
self.output.write_all(b"<li")?;
self.render_sourcepos(node)?;
self.output.write_all(b" id=\"fn-")?;
self.escape_href(nfd.name.as_bytes())?;
self.output.write_all(b"\">")?;
} else {
Expand All @@ -959,14 +999,19 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::FootnoteReference(ref nfr) => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
let mut ref_id = format!("fnref-{}", nfr.name);
if nfr.ref_num > 1 {
ref_id = format!("{}-{}", ref_id, nfr.ref_num);
}

self.output.write_all(b"<sup")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output
.write_all(b"<sup class=\"footnote-ref\"><a href=\"#fn-")?;
.write_all(b" class=\"footnote-ref\"><a href=\"#fn-")?;
self.escape_href(nfr.name.as_bytes())?;
self.output.write_all(b"\" id=\"")?;
self.escape_href(ref_id.as_bytes())?;
Expand Down Expand Up @@ -1004,10 +1049,14 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::Escaped => {
// No sourcepos.
// Unreliable sourcepos.
if self.options.render.escaped_char_spans {
if entering {
self.output.write_all(b"<span data-escaped-char>")?;
self.output.write_all(b"<span data-escaped-char")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</span>")?;
}
Expand All @@ -1024,9 +1073,13 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::WikiLink(ref nl) => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<a href=\"")?;
self.output.write_all(b"<a")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" href=\"")?;
let url = nl.url.as_bytes();
if self.options.render.unsafe_ || !dangerous_url(url) {
self.escape_href(url)?;
Expand All @@ -1038,23 +1091,31 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
}
}
NodeValue::Underline => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<u>")?;
self.output.write_all(b"<u")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</u>")?;
}
}
NodeValue::SpoileredText => {
// No sourcepos.
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<span class=\"spoiler\">")?;
self.output.write_all(b"<span")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b" class=\"spoiler\">")?;
} else {
self.output.write_all(b"</span>")?;
}
}
NodeValue::EscapedTag(ref net) => {
// No sourcepos.
// Nowhere to put sourcepos.
self.output.write_all(net.as_bytes())?;
}
}
Expand Down Expand Up @@ -1114,7 +1175,8 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {

tag_attributes.push((String::from("data-math-style"), String::from(style_attr)));

if self.options.render.sourcepos {
// Unreliable sourcepos.
if self.options.render.experimental_inline_sourcepos && self.options.render.sourcepos {
let ast = node.data.borrow();
tag_attributes.push(("data-sourcepos".to_string(), ast.sourcepos.to_string()));
}
Expand Down
24 changes: 20 additions & 4 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,19 +761,35 @@ pub struct RenderOptions {
/// extensions. The description lists extension still has issues; see
/// <https://github.com/kivikakk/comrak/blob/3bb6d4ce/src/tests/description_lists.rs#L60-L125>.
///
/// Sourcepos information is **not** reliable for inlines. See
/// <https://github.com/kivikakk/comrak/pull/439> for a discussion.
/// Sourcepos information is **not** reliable for inlines, and is not
/// included in HTML without also setting [`experimental_inline_sourcepos`].
/// See <https://github.com/kivikakk/comrak/pull/439> for a discussion.
///
/// ```rust
/// # use comrak::{markdown_to_commonmark_xml, Options};
/// let mut options = Options::default();
/// options.render.sourcepos = true;
/// let input = "Hello *world*!";
/// let input = "## Hello world!";
/// let xml = markdown_to_commonmark_xml(input, &options);
/// assert!(xml.contains("<emph sourcepos=\"1:7-1:13\">"));
/// assert!(xml.contains("<text sourcepos=\"1:4-1:15\" xml:space=\"preserve\">"));
/// ```
pub sourcepos: bool,

/// Include inline sourcepos in HTML output, which is known to have issues.
/// See <https://github.com/kivikakk/comrak/pull/439> for a discussion.
/// ```rust
/// # use comrak::{markdown_to_html, Options};
/// let mut options = Options::default();
/// options.render.sourcepos = true;
/// let input = "Hello *world*!";
/// assert_eq!(markdown_to_html(input, &options),
/// "<p data-sourcepos=\"1:1-1:14\">Hello <em>world</em>!</p>\n");
/// options.render.experimental_inline_sourcepos = true;
/// assert_eq!(markdown_to_html(input, &options),
/// "<p data-sourcepos=\"1:1-1:14\">Hello <em data-sourcepos=\"1:7-1:13\">world</em>!</p>\n");
/// ```
pub experimental_inline_sourcepos: bool,

/// Wrap escaped characters in a `<span>` to allow any
/// post-processing to recognize them.
///
Expand Down