aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/parser/conversion/block.rs33
-rw-r--r--src/parser/simplify.rs139
-rw-r--r--src/parser/tests.rs26
-rw-r--r--src/renderer/html.rs32
-rw-r--r--src/rst.pest7
5 files changed, 138 insertions, 99 deletions
diff --git a/src/parser/conversion/block.rs b/src/parser/conversion/block.rs
index 8659dcd..0c313f6 100644
--- a/src/parser/conversion/block.rs
+++ b/src/parser/conversion/block.rs
@@ -111,25 +111,22 @@ fn convert_replace(pair: Pair<Rule>) -> Result<Vec<c::TextOrInlineElement>, Erro
fn convert_image<I>(pair: Pair<Rule>) -> Result<I, Error> where I: Element + ExtraAttributes<a::Image> {
let mut pairs = pair.into_inner();
let mut image = I::with_extra(a::Image::new(
- pairs.next().unwrap().parse()?, // line
+ pairs.next().unwrap().as_str().trim().parse()?, // line
));
- if let Some(opt_block) = pairs.next() { // image_opt_block
- let options = opt_block.into_inner();
- for opt in options {
- let mut opt_iter = opt.into_inner();
- let opt_name = opt_iter.next().unwrap();
- let opt_val = opt_iter.next().unwrap();
- match opt_name.as_str() {
- "class" => image.classes_mut().push(opt_val.as_str().to_owned()),
- "name" => image.names_mut().push(opt_val.as_str().into()),
- "alt" => image.extra_mut().alt = Some(opt_val.as_str().to_owned()),
- "height" => image.extra_mut().height = Some(opt_val.parse()?),
- "width" => image.extra_mut().width = Some(opt_val.parse()?),
- "scale" => image.extra_mut().scale = Some(parse_scale(&opt_val)?),
- "align" => image.extra_mut().align = Some(opt_val.parse()?),
- "target" => image.extra_mut().target = Some(opt_val.parse()?),
- name => bail!("Unknown Image option {}", name),
- }
+ for opt in pairs {
+ let mut opt_iter = opt.into_inner();
+ let opt_name = opt_iter.next().unwrap();
+ let opt_val = opt_iter.next().unwrap();
+ match opt_name.as_str() {
+ "class" => image.classes_mut().push(opt_val.as_str().to_owned()),
+ "name" => image.names_mut().push(opt_val.as_str().into()),
+ "alt" => image.extra_mut().alt = Some(opt_val.as_str().to_owned()),
+ "height" => image.extra_mut().height = Some(opt_val.parse()?),
+ "width" => image.extra_mut().width = Some(opt_val.parse()?),
+ "scale" => image.extra_mut().scale = Some(parse_scale(&opt_val)?),
+ "align" => image.extra_mut().align = Some(opt_val.parse()?),
+ "target" => image.extra_mut().target = Some(opt_val.parse()?),
+ name => bail!("Unknown Image option {}", name),
}
}
Ok(image)
diff --git a/src/parser/simplify.rs b/src/parser/simplify.rs
index ab79343..cc169ee 100644
--- a/src/parser/simplify.rs
+++ b/src/parser/simplify.rs
@@ -53,7 +53,7 @@ impl NamedTargetType {
#[derive(Clone, Debug)]
struct Substitution {
- content: e::ImageInline,
+ content: Vec<c::TextOrInlineElement>,
/// If true and the sibling before the reference is a text node,
/// the text node gets right-trimmed.
ltrim: bool,
@@ -94,7 +94,7 @@ impl TargetsCollected {
trait ResolvableRefs {
fn populate_targets(&self, refs: &mut TargetsCollected);
- fn resolve_refs(self, refs: &TargetsCollected) -> Self;
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> where Self: Sized;
}
pub fn resolve_references(mut doc: Document) -> Document {
@@ -102,7 +102,7 @@ pub fn resolve_references(mut doc: Document) -> Document {
for c in doc.children() {
c.populate_targets(&mut references);
}
- let new: Vec<_> = doc.children_mut().drain(..).map(|c| c.resolve_refs(&references)).collect();
+ let new: Vec<_> = doc.children_mut().drain(..).flat_map(|c| c.resolve_refs(&references)).collect();
Document::with_children(new)
}
@@ -113,7 +113,7 @@ fn sub_pop<P, C>(parent: &P, refs: &mut TargetsCollected) where P: HasChildren<C
}
fn sub_res<P, C>(mut parent: P, refs: &TargetsCollected) -> P where P: e::Element + HasChildren<C>, C: ResolvableRefs {
- let new: Vec<_> = parent.children_mut().drain(..).map(|c| c.resolve_refs(refs)).collect();
+ let new: Vec<_> = parent.children_mut().drain(..).flat_map(|c| c.resolve_refs(refs)).collect();
parent.children_mut().extend(new);
parent
}
@@ -141,15 +141,15 @@ impl ResolvableRefs for c::StructuralSubElement {
SubStructure(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::StructuralSubElement::*;
- match self {
+ vec![match self {
Title(e) => sub_res(*e, refs).into(),
Subtitle(e) => sub_res(*e, refs).into(),
Decoration(e) => sub_res(*e, refs).into(),
Docinfo(e) => sub_res(*e, refs).into(),
- SubStructure(e) => e.resolve_refs(refs).into(),
- }
+ SubStructure(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
}
}
@@ -164,15 +164,15 @@ impl ResolvableRefs for c::SubStructure {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubStructure::*;
- match self {
+ vec![match self {
Topic(e) => sub_res(*e, refs).into(),
Sidebar(e) => sub_res(*e, refs).into(),
Transition(e) => Transition(e),
Section(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
- }
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
}
}
@@ -186,13 +186,8 @@ impl ResolvableRefs for c::BodyElement {
MathBlock(_) => {},
Rubric(e) => sub_pop(&**e, refs),
SubstitutionDefinition(e) => {
- let subst_content = if let [c::TextOrInlineElement::ImageInline(content)] = &e.children()[..] {
- content.as_ref()
- } else {
- panic!("Unexpected substitution contents.")
- };
let subst = Substitution {
- content: subst_content.clone(),
+ content: e.children().clone(),
ltrim: e.extra().ltrim,
rtrim: e.extra().rtrim
};
@@ -246,15 +241,15 @@ impl ResolvableRefs for c::BodyElement {
Table(e) => sub_pop(&**e, refs)
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::BodyElement::*;
- match self {
+ vec![match self {
Paragraph(e) => sub_res(*e, refs).into(),
LiteralBlock(e) => sub_res(*e, refs).into(),
DoctestBlock(e) => sub_res(*e, refs).into(),
MathBlock(e) => MathBlock(e),
Rubric(e) => sub_res(*e, refs).into(),
- SubstitutionDefinition(e) => SubstitutionDefinition(e),
+ SubstitutionDefinition(_) => return vec![],
Comment(e) => Comment(e),
Pending(e) => Pending(e),
Target(e) => Target(e),
@@ -284,7 +279,7 @@ impl ResolvableRefs for c::BodyElement {
SystemMessage(e) => sub_res(*e, refs).into(),
Figure(e) => sub_res(*e, refs).into(),
Table(e) => sub_res(*e, refs).into()
- }
+ }]
}
}
@@ -305,9 +300,9 @@ impl ResolvableRefs for c::BibliographicElement {
Field(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::BibliographicElement::*;
- match self {
+ vec![match self {
Author(e) => sub_res(*e, refs).into(),
Authors(e) => sub_res(*e, refs).into(),
Organization(e) => sub_res(*e, refs).into(),
@@ -319,7 +314,7 @@ impl ResolvableRefs for c::BibliographicElement {
Date(e) => sub_res(*e, refs).into(),
Copyright(e) => sub_res(*e, refs).into(),
Field(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -351,10 +346,10 @@ impl ResolvableRefs for c::TextOrInlineElement {
ImageInline(_) => {}
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::TextOrInlineElement::*;
- match self {
- c::TextOrInlineElement::String(e) => c::TextOrInlineElement::String(e),
+ vec![match self {
+ String(e) => String(e),
Emphasis(e) => sub_res(*e, refs).into(),
Strong(e) => sub_res(*e, refs).into(),
Literal(e) => sub_res(*e, refs).into(),
@@ -375,7 +370,7 @@ impl ResolvableRefs for c::TextOrInlineElement {
if *ltrim || *rtrim {
dbg!(content, ltrim, rtrim);
}
- ImageInline(Box::new(content.clone()))
+ return content.clone()
},
None => {
// Undefined substitution name (level 3 system message).
@@ -404,7 +399,7 @@ impl ResolvableRefs for c::TextOrInlineElement {
TargetInline(e) => TargetInline(e),
RawInline(e) => RawInline(e),
ImageInline(e) => ImageInline(e)
- }
+ }]
}
}
@@ -418,14 +413,14 @@ impl ResolvableRefs for c::AuthorInfo {
Contact(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::AuthorInfo::*;
- match self {
+ vec![match self {
Author(e) => sub_res(*e, refs).into(),
Organization(e) => sub_res(*e, refs).into(),
Address(e) => sub_res(*e, refs).into(),
Contact(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -437,12 +432,12 @@ impl ResolvableRefs for c::DecorationElement {
Footer(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::DecorationElement::*;
- match self {
+ vec![match self {
Header(e) => sub_res(*e, refs).into(),
Footer(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -454,11 +449,11 @@ impl ResolvableRefs for c::SubTopic {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubTopic::*;
match self {
- Title(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
+ Title(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
}
}
}
@@ -473,14 +468,14 @@ impl ResolvableRefs for c::SubSidebar {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubSidebar::*;
- match self {
+ vec![match self {
Topic(e) => sub_res(*e, refs).into(),
Title(e) => sub_res(*e, refs).into(),
Subtitle(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
- }
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
}
}
@@ -493,13 +488,13 @@ impl ResolvableRefs for c::SubDLItem {
Definition(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubDLItem::*;
- match self {
+ vec![match self {
Term(e) => sub_res(*e, refs).into(),
Classifier(e) => sub_res(*e, refs).into(),
Definition(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -511,12 +506,12 @@ impl ResolvableRefs for c::SubField {
FieldBody(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubField::*;
- match self {
+ vec![match self {
FieldName(e) => sub_res(*e, refs).into(),
FieldBody(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -528,18 +523,18 @@ impl ResolvableRefs for c::SubOptionListItem {
Description(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubOptionListItem::*;
- match self {
+ vec![match self {
OptionGroup(e) => sub_sub_res(*e, refs).into(),
Description(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
impl ResolvableRefs for c::SubOption {
fn populate_targets(&self, _: &mut TargetsCollected) {}
- fn resolve_refs(self, _: &TargetsCollected) -> Self { self }
+ fn resolve_refs(self, _: &TargetsCollected) -> Vec<Self> { vec![self] }
}
impl ResolvableRefs for c::SubLineBlock {
@@ -550,12 +545,12 @@ impl ResolvableRefs for c::SubLineBlock {
Line(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubLineBlock::*;
- match self {
+ vec![match self {
LineBlock(e) => sub_res(*e, refs).into(),
Line(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -567,11 +562,11 @@ impl ResolvableRefs for c::SubBlockQuote {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubBlockQuote::*;
match self {
- Attribution(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
+ Attribution(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
}
}
}
@@ -584,11 +579,11 @@ impl ResolvableRefs for c::SubFootnote {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubFootnote::*;
match self {
- Label(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
+ Label(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
}
}
}
@@ -602,13 +597,13 @@ impl ResolvableRefs for c::SubFigure {
BodyElement(e) => e.populate_targets(refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubFigure::*;
- match self {
+ vec![match self {
Caption(e) => sub_res(*e, refs).into(),
Legend(e) => sub_res(*e, refs).into(),
- BodyElement(e) => e.resolve_refs(refs).into(),
- }
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
}
}
@@ -620,12 +615,12 @@ impl ResolvableRefs for c::SubTable {
TableGroup(e) => sub_pop(&**e, refs),
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubTable::*;
- match self {
+ vec![match self {
Title(e) => sub_res(*e, refs).into(),
TableGroup(e) => sub_res(*e, refs).into(),
- }
+ }]
}
}
@@ -648,9 +643,9 @@ impl ResolvableRefs for c::SubTableGroup {
},
}
}
- fn resolve_refs(self, refs: &TargetsCollected) -> Self {
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
use c::SubTableGroup::*;
- match self {
+ vec![match self {
TableColspec(e) => TableColspec(e),
TableHead(mut e) => {
let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect();
@@ -662,6 +657,6 @@ impl ResolvableRefs for c::SubTableGroup {
e.children_mut().extend(new);
TableBody(e)
},
- }
+ }]
}
}
diff --git a/src/parser/tests.rs b/src/parser/tests.rs
index ee33c8a..075b750 100644
--- a/src/parser/tests.rs
+++ b/src/parser/tests.rs
@@ -134,6 +134,32 @@ A |subst| in-line
};
}
+
+#[allow(clippy::cognitive_complexity)]
+#[test]
+fn substitution_image() {
+ parses_to! {
+ parser: RstParser,
+ input: "\
+.. |subst| image:: thing.png
+ :target: foo.html
+",
+ rule: Rule::document,
+ tokens: [
+ substitution_def(0, 50, [
+ substitution_name(4, 9),
+ image(11, 50, [
+ line(18, 29, [ str(18, 28) ]),
+ image_option(32, 50, [
+ image_opt_name(33, 39),
+ line(40, 50, [ str(40, 49) ]),
+ ]),
+ ]),
+ ]),
+ ]
+ };
+}
+
// TODO: test images
#[allow(clippy::cognitive_complexity)]
diff --git a/src/renderer/html.rs b/src/renderer/html.rs
index 421d511..56e3175 100644
--- a/src/renderer/html.rs
+++ b/src/renderer/html.rs
@@ -9,7 +9,7 @@ use crate::document_tree::{
ExtraAttributes,
elements as e,
element_categories as c,
- // extra_attributes as a,
+ extra_attributes as a,
};
@@ -98,11 +98,33 @@ 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, EnumeratedList => ol, DefinitionList => dl, FieldList => dl, OptionList => pre, LineBlock => div, 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!(Image => img, Table => table); //TODO: after implementing the table, move it to elems with children
+impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children
+
+impl<I> HTMLRender for I where I: e::Element + a::ExtraAttributes<a::Image> {
+ fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write {
+ let extra = self.extra();
+ if let Some(ref target) = extra.target {
+ write!(stream, "<a href=\"{}\">", target)?;
+ }
+ write!(stream, "<img src=\"{}\"", extra.uri)?;
+ if let Some(ref alt) = extra.alt {
+ write!(stream, " alt=\"{}\"", alt)?;
+ }
+ // TODO: align: Option<AlignHV>
+ // TODO: height: Option<Measure>
+ // TODO: width: Option<Measure>
+ // TODO: scale: Option<u8>
+ write!(stream, ">")?;
+ if extra.target.is_some() {
+ write!(stream, "</a>")?;
+ }
+ Ok(())
+ }
+}
impl HTMLRender for e::DoctestBlock {
fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write {
- //
+ // TODO
unimplemented!();
}
}
@@ -173,7 +195,6 @@ impl HTMLRender for e::SystemMessage {
impl_html_render_cat!(TextOrInlineElement { String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math, TargetInline, RawInline, ImageInline });
impl_html_render_simple!(Emphasis => em, Strong => strong, Literal => code, FootnoteReference => a, CitationReference => a, TitleReference => a, Abbreviation => abbr, Acronym => acronym, Superscript => sup, Subscript => sub, Inline => span, Math => math, TargetInline => a);
-impl_html_render_simple_nochildren!(ImageInline => img);
impl HTMLRender for String {
fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write {
@@ -293,8 +314,7 @@ impl HTMLRender for e::Legend {
//TODO: render admonitions: Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning
//TODO: properly render tables
-//TODO: add reference target: Reference, FootnoteReference, CitationReference, TitleReference
+//TODO: add reference target: FootnoteReference, CitationReference, TitleReference
//TODO: add title: Abbr, Acronym
//TODO: convert math, set display attr
//TODO: add id: Rubric, Target, TargetInline
-//TODO: add src: Image, ImageInline
diff --git a/src/rst.pest b/src/rst.pest
index 96652a2..a88e3b3 100644
--- a/src/rst.pest
+++ b/src/rst.pest
@@ -72,7 +72,7 @@ paragraph = { inlines }
// Directives with options can have these or specific ones:
-common_option = { "class" | "name" }
+common_opt_name = { "class" | "name" }
// Replace. A directive only usable in substitutions.
@@ -82,8 +82,9 @@ replace = { ^"replace::" ~ line } // TODO multiline
image_directive = _{ ".." ~ PUSH(" "+) ~ image ~ DROP }
image = { ^"image::" ~ line ~ image_opt_block? }
-image_opt_block = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ ":" ~ image_option ~ ":" ~ line } //TODO: merge with other directives?
-image_option = { common_option | "alt" | "height" | "width" | "scale" | "align" | "target" }
+image_opt_block = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ image_option } //TODO: merge with other directives?
+image_option = { ":" ~ image_opt_name ~ ":" ~ line }
+image_opt_name = { common_opt_name | "alt" | "height" | "width" | "scale" | "align" | "target" }
// Admonition. A directive. The generic one has a title