diff --git a/src/cm.rs b/src/cm.rs index 21b59728..afcf5f95 100644 --- a/src/cm.rs +++ b/src/cm.rs @@ -366,6 +366,7 @@ impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> { NodeValue::HtmlInline(ref literal) => { self.format_html_inline(literal.as_bytes(), entering) } + NodeValue::Raw(ref literal) => self.format_raw(literal.as_bytes(), entering), NodeValue::Strong => { if parent_node.is_none() || !matches!(parent_node.unwrap().data.borrow().value, NodeValue::Strong) @@ -686,6 +687,12 @@ impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> { } } + fn format_raw(&mut self, literal: &[u8], entering: bool) { + if entering { + self.write_all(literal).unwrap(); + } + } + fn format_strong(&mut self) { write!(self, "**").unwrap(); } diff --git a/src/html.rs b/src/html.rs index 5b3dbcd6..01badf82 100644 --- a/src/html.rs +++ b/src/html.rs @@ -380,6 +380,9 @@ where | NodeValue::HtmlInline(ref literal) => { self.escape(literal.as_bytes())?; } + NodeValue::Raw(ref literal) => { + self.output.write_all(literal.as_bytes())?; + } NodeValue::LineBreak | NodeValue::SoftBreak => { self.output.write_all(b" ")?; } @@ -757,6 +760,12 @@ where } } } + NodeValue::Raw(ref literal) => { + // No sourcepos. + if entering { + self.output.write_all(literal.as_bytes())?; + } + } NodeValue::Strong => { // Unreliable sourcepos. let parent_node = node.parent(); diff --git a/src/nodes.rs b/src/nodes.rs index cd749c1e..a034246d 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -127,6 +127,9 @@ pub enum NodeValue { /// **Inline**. [Raw HTML](https://github.github.com/gfm/#raw-html) contained inline. HtmlInline(String), + /// **Inline**. A Raw text. + Raw(String), + /// **Inline**. [Emphasized](https://github.github.com/gfm/#emphasis-and-strong-emphasis) /// text. Emph, @@ -511,6 +514,7 @@ impl NodeValue { NodeValue::Strong => "strong", NodeValue::Code(..) => "code", NodeValue::HtmlInline(..) => "html_inline", + NodeValue::Raw(..) => "raw", NodeValue::Strikethrough => "strikethrough", NodeValue::FrontMatter(_) => "frontmatter", NodeValue::TaskItem { .. } => "taskitem", diff --git a/src/tests.rs b/src/tests.rs index 68cf08e8..af8c1c31 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -21,6 +21,7 @@ mod multiline_block_quotes; mod options; mod pathological; mod plugins; +mod raw; mod regressions; mod rewriter; mod shortcodes; diff --git a/src/tests/api.rs b/src/tests/api.rs index dd82d7b5..b726a55e 100644 --- a/src/tests/api.rs +++ b/src/tests/api.rs @@ -239,6 +239,9 @@ fn exercise_full_api() { nodes::NodeValue::HtmlInline(html) => { let _: &String = html; } + nodes::NodeValue::Raw(text) => { + let _: &String = text; + } nodes::NodeValue::Emph => {} nodes::NodeValue::Strong => {} nodes::NodeValue::Strikethrough => {} diff --git a/src/tests/raw.rs b/src/tests/raw.rs new file mode 100644 index 00000000..806634d1 --- /dev/null +++ b/src/tests/raw.rs @@ -0,0 +1,77 @@ +use std::cell::RefCell; + +use crate::{ + arena_tree::Node, + nodes::{Ast, NodeValue}, +}; + +use super::*; + +#[test] +fn raw_node() { + let user_input = "User input: "; + let system_input_inline = "System Inline input: "; + let system_input_block = "System Block input: "; + let input = user_input.to_owned() + system_input_inline + "\n\n" + system_input_block + "\n"; + + let mut options = Options::default(); + options.render.escape = true; + options.render.unsafe_ = false; + options.extension.tagfilter = true; + + let arena = Arena::new(); + let root = parse_document(&arena, user_input, &options); + let raw_ast_inline = Ast::new( + NodeValue::Raw(system_input_inline.to_string()), + (0, 0).into(), + ); + let raw_node_inline = arena.alloc(Node::new(RefCell::new(raw_ast_inline))); + root.first_child() + .unwrap() + .last_child() + .unwrap() + .insert_after(raw_node_inline); + let raw_ast_block = Ast::new( + NodeValue::Raw(system_input_block.to_string()), + (0, 0).into(), + ); + let raw_node_block = arena.alloc(Node::new(RefCell::new(raw_ast_block))); + root.first_child().unwrap().insert_after(raw_node_block); + + let mut output = vec![]; + html::format_document(root, &options, &mut output).unwrap(); + compare_strs( + &String::from_utf8(output).unwrap(), + concat!( + "

User input: <iframe></iframe>", + "System Inline input:

\n", + "System Block input: " + ), + "html", + &input, + ); + + let mut md = vec![]; + cm::format_document_with_plugins(root, &options, &mut md, &Plugins::default()).unwrap(); + compare_strs(&String::from_utf8(md).unwrap(), &input, "cm", &input); + + let mut xml = vec![]; + crate::xml::format_document(root, &options, &mut xml).unwrap(); + compare_strs( + &String::from_utf8(xml).unwrap(), + concat!( + "\n", + "\n", + "\n", + " \n", + " User input: \n", + " <iframe>\n", + " </iframe>\n", + " System Inline input: <iframe></iframe>\n", + " \n", + " System Block input: <iframe></iframe>\n\n" + ), + "xml", + &input, + ); +} diff --git a/src/xml.rs b/src/xml.rs index 30b5080c..06fa3848 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -89,7 +89,8 @@ impl<'o, 'c> XmlFormatter<'o, 'c> { match node.data.borrow().value { NodeValue::Text(ref literal) | NodeValue::Code(NodeCode { ref literal, .. }) - | NodeValue::HtmlInline(ref literal) => { + | NodeValue::HtmlInline(ref literal) + | NodeValue::Raw(ref literal) => { self.escape(literal.as_bytes())?; } NodeValue::LineBreak | NodeValue::SoftBreak => { @@ -148,7 +149,8 @@ impl<'o, 'c> XmlFormatter<'o, 'c> { NodeValue::Text(ref literal) | NodeValue::Code(NodeCode { ref literal, .. }) | NodeValue::HtmlBlock(NodeHtmlBlock { ref literal, .. }) - | NodeValue::HtmlInline(ref literal) => { + | NodeValue::HtmlInline(ref literal) + | NodeValue::Raw(ref literal) => { self.output.write_all(b" xml:space=\"preserve\">")?; self.escape(literal.as_bytes())?; write!(self.output, "