diff options
| author | Philipp A | 2019-11-09 19:35:16 +0100 |
|---|---|---|
| committer | Philipp A | 2019-11-09 19:35:16 +0100 |
| commit | 2ddff4fbbfbf4593260b96e57e7f975d2d003714 (patch) | |
| tree | f9aeb61c3f0cf59d44d2e447b00bd5019e188eac | |
| parent | 955e179e8e78dfbc5ef9e4e3400413b5351e5d6b (diff) | |
| download | rust-rst-2ddff4fbbfbf4593260b96e57e7f975d2d003714.tar.bz2 | |
Finish substitution implementation
| -rw-r--r-- | src/parser/conversion/block.rs | 33 | ||||
| -rw-r--r-- | src/parser/simplify.rs | 139 | ||||
| -rw-r--r-- | src/parser/tests.rs | 26 | ||||
| -rw-r--r-- | src/renderer/html.rs | 32 | ||||
| -rw-r--r-- | src/rst.pest | 7 |
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 |
