diff options
| author | Philipp A | 2019-11-09 14:59:37 +0100 |
|---|---|---|
| committer | Philipp A | 2019-11-09 14:59:37 +0100 |
| commit | 603fbf0b91c643cfa0921aadb6865ae9d7c094bf (patch) | |
| tree | 2061e0987d4e731464ddf9cd37766083bdd0d621 | |
| parent | df6b7645d845a022ca7eeba08b1ecb761a020195 (diff) | |
| download | rust-rst-603fbf0b91c643cfa0921aadb6865ae9d7c094bf.tar.bz2 | |
Simple indirect links
| -rw-r--r-- | src/parser/conversion/block.rs | 15 | ||||
| -rw-r--r-- | src/parser/conversion/inline.rs | 1 | ||||
| -rw-r--r-- | src/parser/simplify.rs | 188 | ||||
| -rw-r--r-- | src/parser/tests.rs | 6 | ||||
| -rw-r--r-- | src/renderer/html.rs | 44 |
5 files changed, 124 insertions, 130 deletions
diff --git a/src/parser/conversion/block.rs b/src/parser/conversion/block.rs index 9552539..0de86b5 100644 --- a/src/parser/conversion/block.rs +++ b/src/parser/conversion/block.rs @@ -74,21 +74,20 @@ fn convert_paragraph(pair: Pair<Rule>) -> Result<e::Paragraph, Error> { fn convert_target(pair: Pair<Rule>) -> Result<e::Target, Error> { - let mut attrs = a::Target { - anonymous: false, - ..Default::default() - }; + let mut elem: e::Target = Default::default(); + elem.extra_mut().anonymous = false; for p in pair.into_inner() { match p.as_rule() { Rule::target_name_uq | Rule::target_name_qu => { - attrs.refid = Some(p.as_str().into()); - attrs.refname.push(p.as_str().into()); + elem.ids_mut().push(p.as_str().into()); + elem.names_mut().push(p.as_str().into()); }, - Rule::link_target => attrs.refuri = Some(p.parse()?), + // TODO: also handle non-urls + Rule::link_target => elem.extra_mut().refuri = Some(p.parse()?), rule => panic!("Unexpected rule in target: {:?}", rule), } } - Ok(e::Target::new(Default::default(), attrs)) + Ok(elem) } fn convert_substitution_def(pair: Pair<Rule>) -> Result<e::SubstitutionDefinition, Error> { diff --git a/src/parser/conversion/inline.rs b/src/parser/conversion/inline.rs index c942732..94f99b4 100644 --- a/src/parser/conversion/inline.rs +++ b/src/parser/conversion/inline.rs @@ -2,7 +2,6 @@ use failure::Error; use pest::iterators::Pair; use crate::document_tree::{ - ExtraAttributes, elements as e, element_categories as c, extra_attributes as a, diff --git a/src/parser/simplify.rs b/src/parser/simplify.rs index f6f0e8a..ab79343 100644 --- a/src/parser/simplify.rs +++ b/src/parser/simplify.rs @@ -16,7 +16,7 @@ or: There’s also anonymous links and targets without names. -TODO: continue documenting how it’s done via http://svn.code.sf.net/p/docutils/code/trunk/docutils/docutils/transforms/references.py +TODO: continue documenting how it’s done via https://repo.or.cz/docutils.git/blob/HEAD:/docutils/docutils/transforms/references.py */ use std::collections::HashMap; @@ -25,9 +25,10 @@ use crate::url::Url; use crate::document_tree::{ Document, HasChildren, - attribute_types::{ID, NameToken}, - elements as e, + attribute_types::NameToken, + elements::{self as e, Element}, element_categories as c, + extra_attributes::ExtraAttributes, }; @@ -39,13 +40,13 @@ enum NamedTargetType { InternalLink, ExternalLink(Url), IndirectLink(NameToken), - SectionTitle + SectionTitle, } impl NamedTargetType { fn is_implicit_target(&self) -> bool { match self { NamedTargetType::SectionTitle => true, - _ => false + _ => false, } } } @@ -53,18 +54,42 @@ impl NamedTargetType { #[derive(Clone, Debug)] struct Substitution { content: e::ImageInline, - // If `ltrim` is true and the sibling before the reference is a text node, - // the text node gets right-trimmed. Same for `rtrim` with the sibling after - // getting left-trimmed. + /// If true and the sibling before the reference is a text node, + /// the text node gets right-trimmed. ltrim: bool, - rtrim: bool + /// Same as `ltrim` with the sibling after the reference. + rtrim: bool, } #[derive(Default, Debug)] struct TargetsCollected { named_targets: HashMap<NameToken, NamedTargetType>, - substitutions: HashMap<String, Substitution>, - normalized_substitutions: HashMap<String, Substitution> + substitutions: HashMap<NameToken, Substitution>, + normalized_substitutions: HashMap<String, Substitution>, +} +impl TargetsCollected { + fn target_url<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Url> { + // TODO: Check if the target would expand circularly + if refname.len() != 1 { + panic!("Expected exactly one name in a reference."); + } + let name = refname[0].clone(); + match self.named_targets.get(&name)? { + NamedTargetType::ExternalLink(url) => Some(url), + _ => unimplemented!(), + } + } + + fn substitution<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Substitution> { + // TODO: Check if the substitution would expand circularly + if refname.len() != 1 { + panic!("Expected exactly one name in a substitution reference."); + } + let name = refname[0].clone(); + self.substitutions.get(&name).or_else(|| { + self.normalized_substitutions.get(&name.0.to_lowercase()) + }) + } } trait ResolvableRefs { @@ -134,9 +159,7 @@ impl ResolvableRefs for c::SubStructure { match self { Topic(e) => sub_pop(&**e, refs), Sidebar(e) => sub_pop(&**e, refs), - Transition(e) => { - // TODO - }, + Transition(_) => {}, Section(e) => sub_pop(&**e, refs), BodyElement(e) => e.populate_targets(refs), } @@ -155,18 +178,14 @@ impl ResolvableRefs for c::SubStructure { impl ResolvableRefs for c::BodyElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use crate::document_tree::extra_attributes::ExtraAttributes; use c::BodyElement::*; match self { Paragraph(e) => sub_pop(&**e, refs), LiteralBlock(e) => sub_pop(&**e, refs), DoctestBlock(e) => sub_pop(&**e, refs), - MathBlock(e) => { - // TODO - }, + MathBlock(_) => {}, Rubric(e) => sub_pop(&**e, refs), SubstitutionDefinition(e) => { - use e::Element; let subst_content = if let [c::TextOrInlineElement::ImageInline(content)] = &e.children()[..] { content.as_ref() } else { @@ -177,30 +196,30 @@ impl ResolvableRefs for c::BodyElement { ltrim: e.extra().ltrim, rtrim: e.extra().rtrim }; - for NameToken(name) in e.names() { + for name in e.names() { if refs.substitutions.contains_key(name) { // TODO: Duplicate substitution name (level 3 system message). } // Intentionally overriding any previous values. refs.substitutions.insert(name.clone(), subst.clone()); - refs.normalized_substitutions.insert(name.to_lowercase(), subst.clone()); + refs.normalized_substitutions.insert(name.0.to_lowercase(), subst.clone()); } }, - Comment(e) => { - // TODO - }, - Pending(e) => { - // TODO + Comment(_) => {}, + Pending(_) => { + unimplemented!(); }, Target(e) => { - // TODO - }, - Raw(e) => { - // TODO - }, - Image(e) => { - // TODO + if let Some(uri) = &e.extra().refuri { + for name in e.names() { + refs.named_targets.insert(name.clone(), NamedTargetType::ExternalLink(uri.clone())); + } + } + // TODO: as is, people can only refer to the target directly containing the URL. + // add refid and refnames to some HashMap and follow those later. }, + Raw(_) => {}, + Image(_) => {}, Compound(e) => sub_pop(&**e, refs), Container(e) => sub_pop(&**e, refs), BulletList(e) => sub_sub_pop(&**e, refs), @@ -308,9 +327,7 @@ impl ResolvableRefs for c::TextOrInlineElement { fn populate_targets(&self, refs: &mut TargetsCollected) { use c::TextOrInlineElement::*; match self { - c::TextOrInlineElement::String(e) => { - // TODO - }, + String(_) => {}, Emphasis(e) => sub_pop(&**e, refs), Strong(e) => sub_pop(&**e, refs), Literal(e) => sub_pop(&**e, refs), @@ -326,18 +343,12 @@ impl ResolvableRefs for c::TextOrInlineElement { Inline(e) => sub_pop(&**e, refs), Problematic(e) => sub_pop(&**e, refs), Generated(e) => sub_pop(&**e, refs), - Math(e) => { - // TODO - }, - TargetInline(e) => { - // TODO + Math(_) => {}, + TargetInline(_) => { + unimplemented!(); }, - RawInline(e) => { - // TODO - }, - ImageInline(e) => { - // TODO - } + RawInline(_) => {}, + ImageInline(_) => {} } } fn resolve_refs(self, refs: &TargetsCollected) -> Self { @@ -347,39 +358,38 @@ impl ResolvableRefs for c::TextOrInlineElement { Emphasis(e) => sub_res(*e, refs).into(), Strong(e) => sub_res(*e, refs).into(), Literal(e) => sub_res(*e, refs).into(), - Reference(e) => sub_res(*e, refs).into(), + Reference(mut e) => { + if e.extra().refuri.is_none() { + if let Some(uri) = refs.target_url(&e.extra().refname) { + e.extra_mut().refuri = Some(uri.clone()); + } + } + (*e).into() + }, FootnoteReference(e) => sub_res(*e, refs).into(), CitationReference(e) => sub_res(*e, refs).into(), - SubstitutionReference(e) => { - use crate::document_tree::extra_attributes::ExtraAttributes; - if e.extra().refname.len() != 1 { - panic!("Expected exactly one name in a substitution reference."); - } - let name = e.extra().refname[0].0.clone(); - let substitution = refs.substitutions.get(&name).or_else(|| { - refs.normalized_substitutions.get(&name.to_lowercase()) - }); - match substitution { - Some(Substitution {content, ltrim, rtrim}) => { - // TODO: Check if the substitution would expand circularly - // (level 3 system message). - // TODO: Ltrim and rtrim. - ImageInline(Box::new(content.clone())) - }, - None => { - // Undefined substitution name (level 3 system message). - // TODO: This replaces the reference by a Problematic node. - // The corresponding SystemMessage node should go in a generated - // section with class "system-messages" at the end of the document. - use crate::document_tree::{Problematic, element_categories::TextOrInlineElement}; - let mut replacement: Box<Problematic> = Box::new(Default::default()); - replacement.children_mut().push( - TextOrInlineElement::String(Box::new(format!("|{}|", name))) - ); - // TODO: Create an ID for replacement for the system_message to reference. - // TODO: replacement.refid pointing to the system_message. - Problematic(replacement) + SubstitutionReference(e) => match refs.substitution(&e.extra().refname) { + Some(Substitution {content, ltrim, rtrim}) => { + // (level 3 system message). + // TODO: ltrim and rtrim. + if *ltrim || *rtrim { + dbg!(content, ltrim, rtrim); } + ImageInline(Box::new(content.clone())) + }, + None => { + // Undefined substitution name (level 3 system message). + // TODO: This replaces the reference by a Problematic node. + // The corresponding SystemMessage node should go in a generated + // section with class "system-messages" at the end of the document. + use crate::document_tree::Problematic; + let mut replacement: Box<Problematic> = Box::new(Default::default()); + replacement.children_mut().push( + c::TextOrInlineElement::String(Box::new(format!("|{}|", e.extra().refname[0].0))) + ); + // TODO: Create an ID for replacement for the system_message to reference. + // TODO: replacement.refid pointing to the system_message. + Problematic(replacement) } }, TitleReference(e) => sub_res(*e, refs).into(), @@ -528,24 +538,8 @@ impl ResolvableRefs for c::SubOptionListItem { } impl ResolvableRefs for c::SubOption { - fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubOption::*; - match self { - OptionString(e) => { - // TODO - }, - OptionArgument(e) => { - // TODO - }, - } - } - fn resolve_refs(self, refs: &TargetsCollected) -> Self { - use c::SubOption::*; - match self { - OptionString(e) => OptionString(e), - OptionArgument(e) => OptionArgument(e), - } - } + fn populate_targets(&self, _: &mut TargetsCollected) {} + fn resolve_refs(self, _: &TargetsCollected) -> Self { self } } impl ResolvableRefs for c::SubLineBlock { @@ -639,8 +633,8 @@ impl ResolvableRefs for c::SubTableGroup { fn populate_targets(&self, refs: &mut TargetsCollected) { use c::SubTableGroup::*; match self { - TableColspec(e) => { - // TODO + TableColspec(_) => { + unimplemented!(); }, TableHead(e) => { for c in e.children() { diff --git a/src/parser/tests.rs b/src/parser/tests.rs index b2ee623..c498159 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -53,7 +53,7 @@ Title }; } -#[allow(clippy::cyclomatic_complexity)] +#[allow(clippy::cognitive_complexity)] #[test] fn two_targets() { parses_to! { @@ -76,7 +76,7 @@ fn two_targets() { }; } -#[allow(clippy::cyclomatic_complexity)] +#[allow(clippy::cognitive_complexity)] #[test] fn admonitions() { parses_to! { @@ -111,7 +111,7 @@ fn admonitions() { // TODO: test substitutions // TODO: test images -#[allow(clippy::cyclomatic_complexity)] +#[allow(clippy::cognitive_complexity)] #[test] fn nested_lists() { parses_to! { diff --git a/src/renderer/html.rs b/src/renderer/html.rs index 876e21b..421d511 100644 --- a/src/renderer/html.rs +++ b/src/renderer/html.rs @@ -2,16 +2,18 @@ use std::io::Write; use failure::Error; +// use crate::url::Url; use crate::document_tree::{ Document, HasChildren, ExtraAttributes, elements as e, element_categories as c, + // extra_attributes as a, }; -static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; +// static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; pub fn render_html<W>(document: &Document, mut stream: W) -> Result<(), Error> where W: Write { document.render_html(stream.by_ref()) @@ -70,14 +72,14 @@ impl_html_render_cat!(StructuralSubElement { Title, Subtitle, Decoration, Docinf impl_html_render_simple!(Title => h1, Subtitle => h2); impl HTMLRender for e::Docinfo { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Like “YAML frontmatter” in Markdown unimplemented!(); } } impl HTMLRender for e::Decoration { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Header or footer unimplemented!(); } @@ -88,7 +90,7 @@ impl_html_render_simple!(Sidebar => aside, Section => section); impl_html_render_simple_nochildren!(Transition => hr); impl HTMLRender for e::Topic { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // A mini section with title unimplemented!(); } @@ -99,14 +101,14 @@ impl_html_render_simple!(Paragraph => p, LiteralBlock => pre, MathBlock => math, impl_html_render_simple_nochildren!(Image => img, Table => table); //TODO: after implementing the table, move it to elems with children impl HTMLRender for e::DoctestBlock { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // unimplemented!(); } } impl HTMLRender for e::SubstitutionDefinition { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // TODO: Should those be removed after resolving them Ok(()) } @@ -124,16 +126,16 @@ impl HTMLRender for e::Comment { } impl HTMLRender for e::Pending { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Will those be resolved by the time we get here? unimplemented!(); } } impl HTMLRender for e::Target { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Should be resolved by now - unimplemented!(); + Ok(()) } } @@ -147,13 +149,13 @@ impl HTMLRender for e::Raw { } impl HTMLRender for e::Footnote { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { unimplemented!(); } } impl HTMLRender for e::Citation { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { unimplemented!(); } } @@ -185,12 +187,12 @@ impl HTMLRender for e::Reference { let extra = self.extra(); write!(stream, "<a")?; if let Some(ref target) = extra.refuri { - write!(stream, "href=\"{}\"", target)?; + write!(stream, " href=\"{}\"", target)?; } - write!(stream, ">")?; if let Some(ref name) = extra.name { - write!(stream, "{}", name.0)?; + write!(stream, " title=\"{}\"", name.0)?; } + write!(stream, ">")?; for c in self.children() { (*c).render_html(stream)?; } @@ -200,21 +202,21 @@ impl HTMLRender for e::Reference { } impl HTMLRender for e::SubstitutionReference { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Will those be resolved by the time we get here? unimplemented!(); } } impl HTMLRender for e::Problematic { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Broken inline markup leads to insertion of this in docutils unimplemented!(); } } impl HTMLRender for e::Generated { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Section numbers and so on unimplemented!(); } @@ -239,21 +241,21 @@ impl_html_render_cat!(SubSidebar { Topic, Title, Subtitle, BodyElement }); impl_html_render_simple!(ListItem => li); impl HTMLRender for e::DefinitionListItem { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // Term→dt, Definition→dd, Classifier→??? unimplemented!(); } } impl HTMLRender for e::Field { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // FieldName→dt, FieldBody→dd unimplemented!(); } } impl HTMLRender for e::OptionListItem { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { // OptionGroup→dt(s), Description→dd unimplemented!(); } @@ -278,7 +280,7 @@ impl_html_render_cat!(SubFigure { Caption, Legend, BodyElement }); impl_html_render_simple!(Caption => caption); impl HTMLRender for e::Legend { - fn render_html<W>(&self, stream: &mut W) -> Result<(), Error> where W: Write { + fn render_html<W>(&self, _stream: &mut W) -> Result<(), Error> where W: Write { unimplemented!(); } } |
