diff options
| author | Philipp A | 2020-11-01 16:46:16 +0100 |
|---|---|---|
| committer | GitHub | 2020-11-01 16:46:16 +0100 |
| commit | 44e9d2b79f307d9b741a606d5acd2fe6166efa5f (patch) | |
| tree | 0ae04d6262c74081ac3a1a9d4259a8db156d7d89 | |
| parent | 89d123ec6586b9d0e48c2f99f11575b254b46e72 (diff) | |
| download | rust-rst-44e9d2b79f307d9b741a606d5acd2fe6166efa5f.tar.bz2 | |
Implement literal blocks (#28)
| -rw-r--r-- | README.rst | 10 | ||||
| -rw-r--r-- | parser/src/conversion/block.rs | 40 | ||||
| -rw-r--r-- | parser/src/rst.pest | 17 | ||||
| -rw-r--r-- | parser/src/tests.rs | 40 | ||||
| -rw-r--r-- | renderer/src/html.rs | 23 | ||||
| -rw-r--r-- | renderer/src/html/tests.rs | 11 |
6 files changed, 110 insertions, 31 deletions
@@ -19,6 +19,16 @@ Currently it can convert a subset of rST (e.g. this README) to HTML5: # or cargo run -- README.rst +The contained packages are: + +:: + + document_tree + ├── renderer + │ └──────┐ + └── parser │ + └──────┴── rst + This project is dual-licensed under Apache 2.0 and MIT. .. _Docutils Document Tree: http://docutils.sourceforge.net/docs/ref/doctree.html diff --git a/parser/src/conversion/block.rs b/parser/src/conversion/block.rs index e23cce2..626bc20 100644 --- a/parser/src/conversion/block.rs +++ b/parser/src/conversion/block.rs @@ -53,9 +53,10 @@ fn convert_body_elem(pair: Pair<Rule>) -> Result<c::BodyElement, Error> { Rule::admonition_gen => convert_admonition_gen(pair)?.into(), Rule::image => convert_image::<e::Image>(pair)?.into(), Rule::bullet_list => convert_bullet_list(pair)?.into(), - Rule::code_directive => convert_code_directive(pair)?.into(), - Rule::raw_directive => convert_raw_directive(pair)?.into(), - Rule::block_comment => convert_comment(pair)?.into(), + Rule::literal_block => convert_literal_block(pair).into(), + Rule::code_directive => convert_code_directive(pair).into(), + Rule::raw_directive => convert_raw_directive(pair).into(), + Rule::block_comment => convert_comment(pair).into(), rule => unimplemented!("unhandled rule {:?}", rule), }) } @@ -204,25 +205,34 @@ fn convert_bullet_item(pair: Pair<Rule>) -> Result<e::ListItem, Error> { Ok(e::ListItem::with_children(children)) } -fn convert_code_directive(pair: Pair<Rule>) -> Result<e::LiteralBlock, Error> { +fn convert_literal_block(pair: Pair<Rule>) -> e::LiteralBlock { + convert_literal_lines(pair.into_inner().next().unwrap()) +} + +fn convert_literal_lines(pair: Pair<Rule>) -> e::LiteralBlock { + let children = pair.into_inner().map(|l| match l.as_rule() { + Rule::literal_line => l.as_str(), + Rule::literal_line_blank => "\n", + _ => unreachable!(), + }.into()).collect(); + return e::LiteralBlock::with_children(children); +} + +fn convert_code_directive(pair: Pair<Rule>) -> e::LiteralBlock { let mut iter = pair.into_inner(); let (lang, code) = match (iter.next().unwrap(), iter.next()) { (lang, Some(code)) => (Some(lang), code), (code, None) => (None, code), }; - let children = code.into_inner().map(|l| match l.as_rule() { - Rule::code_line => l.as_str(), - Rule::code_line_blank => "\n", - _ => unreachable!(), - }.into()).collect(); - let mut code_block = e::LiteralBlock::with_children(children); + let mut code_block = convert_literal_lines(code); + code_block.classes_mut().push("code".to_owned()); if let Some(lang) = lang { code_block.classes_mut().push(lang.as_str().to_owned()); }; - Ok(code_block) + code_block } -fn convert_raw_directive(pair: Pair<Rule>) -> Result<e::Raw, Error> { +fn convert_raw_directive(pair: Pair<Rule>) -> e::Raw { let mut iter = pair.into_inner(); let format = iter.next().unwrap(); let block = iter.next().unwrap(); @@ -233,14 +243,14 @@ fn convert_raw_directive(pair: Pair<Rule>) -> Result<e::Raw, Error> { }.into()).collect(); let mut raw_block = e::Raw::with_children(children); raw_block.extra_mut().format.push(at::NameToken(format.as_str().to_owned())); - Ok(raw_block) + raw_block } -fn convert_comment(pair: Pair<Rule>) -> Result<e::Comment, Error> { +fn convert_comment(pair: Pair<Rule>) -> e::Comment { let lines = pair.into_inner().map(|l| match l.as_rule() { Rule::comment_line_blank => "\n", Rule::comment_line => l.as_str(), _ => unreachable!(), }.into()).collect(); - Ok(e::Comment::with_children(lines)) + e::Comment::with_children(lines) } diff --git a/parser/src/rst.pest b/parser/src/rst.pest index ed0236e..d9fb668 100644 --- a/parser/src/rst.pest +++ b/parser/src/rst.pest @@ -11,7 +11,6 @@ block = _{ PEEK[..] ~ hanging_block } // This is the list of all block-level elements // They’re defined hanging, i.e. without the first PEEK[..] -// This is d hanging_block = _{ substitution_def | image_directive @@ -20,6 +19,7 @@ hanging_block = _{ | admonition | admonition_gen | target + | literal_block // Comments should be below the directives to try to match them first, but // above the title that will interpret ".." as a title marker. | block_comment @@ -62,6 +62,14 @@ blist_body = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ hanging_block ~ block* } // paragraph. A block type. paragraph = { inlines } +// literal_block +literal_block = { + "::" ~ " "* ~ NEWLINE ~ + blank_line+ ~ PUSH(" "+) ~ literal_lines ~ DROP +} +literal_lines = { literal_line ~ (literal_line_blank* ~ PEEK[..] ~ literal_line)* } +literal_line_blank = { " "* ~ NEWLINE } +literal_line = { (!NEWLINE ~ ANY)+ ~ NEWLINE } /* Directives: http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#directives * .. name:: arguments ~ :options: ~ blank_line+ ~ content @@ -84,16 +92,13 @@ image_opt_block = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ image_option } //TODO: merg image_option = { ":" ~ image_opt_name ~ ":" ~ line } image_opt_name = { common_opt_name | "alt" | "height" | "width" | "scale" | "align" | "target" } -// Code block. A directive +// Code block. A directive that allows adding a language to a literal block code_directive = { ".." ~ PUSH(" "+) ~ "code" ~ "-block"? ~ "::" ~ (" "+ ~ source)? ~ NEWLINE ~ - blank_line+ ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ code_block ~ DROP + blank_line+ ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ literal_lines ~ DROP } source = { (!NEWLINE ~ ANY)+ } -code_block = { code_line ~ (code_line_blank* ~ PEEK[..] ~ code_line)* } -code_line_blank = { " "* ~ NEWLINE } -code_line = { (!NEWLINE ~ ANY)+ ~ NEWLINE } // Raw block. A directive diff --git a/parser/src/tests.rs b/parser/src/tests.rs index 5a9ee9a..ffe0ee7 100644 --- a/parser/src/tests.rs +++ b/parser/src/tests.rs @@ -150,7 +150,35 @@ fn admonitions() { #[allow(clippy::cognitive_complexity)] #[test] -fn code() { +fn literal_block() { + parses_to! { + parser: RstParser, + input: "\ +:: + + print('x') + + # second line + +The end +", + rule: Rule::document, + tokens: [ + literal_block(0, 36, [ + literal_lines(7, 36, [ + literal_line(7, 18), + literal_line_blank(18, 19), + literal_line(22, 36), + ]), + ]), + paragraph(37, 44, [ str(37, 44) ]), + ] + }; +} + +#[allow(clippy::cognitive_complexity)] +#[test] +fn code_directive() { parses_to! { parser: RstParser, input: "\ @@ -169,14 +197,14 @@ The end rule: Rule::document, tokens: [ code_directive(0, 26, [ - code_block(14, 26, [ code_line(14, 26) ]), + literal_lines(14, 26, [ literal_line(14, 26) ]), ]), code_directive(27, 83, [ source(43, 49), - code_block(54, 83, [ - code_line(54, 65), - code_line_blank(65, 66), - code_line(69, 83), + literal_lines(54, 83, [ + literal_line(54, 65), + literal_line_blank(65, 66), + literal_line(69, 83), ]), ]), paragraph(84, 91, [ str(84, 91) ]), diff --git a/renderer/src/html.rs b/renderer/src/html.rs index bb7c42e..4244349 100644 --- a/renderer/src/html.rs +++ b/renderer/src/html.rs @@ -172,7 +172,7 @@ impl HTMLRender for e::Topic { } impl_html_render_cat!(BodyElement { Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, Compound, Container, BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table }); -impl_html_render_simple!(Paragraph => p, LiteralBlock => pre, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul["\n"], EnumeratedList => ol["\n"], DefinitionList => dl["\n"], FieldList => dl["\n"], OptionList => pre, LineBlock => div["\n"], BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure); +impl_html_render_simple!(Paragraph => p, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul["\n"], EnumeratedList => ol["\n"], DefinitionList => dl["\n"], FieldList => dl["\n"], OptionList => pre, LineBlock => div["\n"], BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure); impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children // circumvent E0119 @@ -199,6 +199,27 @@ impl<I> HTMLRender for I where I: e::Element + a::ExtraAttributes<a::Image> + IM } } +impl HTMLRender for e::LiteralBlock { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + let mut cls_iter = self.classes().iter(); + let is_code = cls_iter.next() == Some(&"code".to_owned()); + write!(renderer.stream, "<pre>")?; + if is_code { // TODO: support those classes not being at the start + if let Some(lang) = cls_iter.next() { + write!(renderer.stream, "<code class=\"language-{}\">", lang)?; + } else { + write!(renderer.stream, "<code>")?; + } + } + for c in self.children() { + (*c).render_html(renderer)?; + } + if is_code { write!(renderer.stream, "</code>")?; } + write!(renderer.stream, "</pre>")?; + Ok(()) + } +} + impl HTMLRender for e::DoctestBlock { fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { // TODO diff --git a/renderer/src/html/tests.rs b/renderer/src/html/tests.rs index 6387238..a4ec633 100644 --- a/renderer/src/html/tests.rs +++ b/renderer/src/html/tests.rs @@ -100,8 +100,13 @@ hello ``foo.map(|world| world + 42)`` .. code:: foo.map(|world| world + 42) + +:: + + hay! |x| ", "<p>hello <code>foo.map(|world| world + 42)</code></p> -<pre>foo.map(|world| world + 42)\n</pre>"); +<pre><code>foo.map(|world| world + 42)\n</code></pre> +<pre>hay! |x|\n</pre>"); } /* @@ -305,11 +310,11 @@ fn code() { # comment ", "\ -<pre class=\"python\">def foo(): +<pre><code class=\"language-python\">def foo(): print('Hi!') # comment -</pre>\ +</code></pre>\ "); } |
