aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp A2020-11-01 16:46:16 +0100
committerGitHub2020-11-01 16:46:16 +0100
commit44e9d2b79f307d9b741a606d5acd2fe6166efa5f (patch)
tree0ae04d6262c74081ac3a1a9d4259a8db156d7d89
parent89d123ec6586b9d0e48c2f99f11575b254b46e72 (diff)
downloadrust-rst-44e9d2b79f307d9b741a606d5acd2fe6166efa5f.tar.bz2
Implement literal blocks (#28)
-rw-r--r--README.rst10
-rw-r--r--parser/src/conversion/block.rs40
-rw-r--r--parser/src/rst.pest17
-rw-r--r--parser/src/tests.rs40
-rw-r--r--renderer/src/html.rs23
-rw-r--r--renderer/src/html/tests.rs11
6 files changed, 110 insertions, 31 deletions
diff --git a/README.rst b/README.rst
index d8a9f5d..630025e 100644
--- a/README.rst
+++ b/README.rst
@@ -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>\
");
}