diff --git a/Cargo.lock b/Cargo.lock index d35c823..b295075 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -777,7 +777,7 @@ dependencies = [ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "single 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "str-concat 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "str-concat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2055,7 +2055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "str-concat" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2799,7 +2799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum str-concat 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "64fdc54cf225600eb1177c62d93662b4eb2ed25153523c018244be9322adff1e" +"checksum str-concat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3468939e48401c4fe3cdf5e5cef50951c2808ed549d1467fde249f1fcb602634" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum string_cache 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" "checksum string_cache_codegen 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" diff --git a/Cargo.toml b/Cargo.toml index 0c56fe1..f154668 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] pulldown-cmark = "0.4.0" -str-concat = "0.1.4" +str-concat = "0.2.0" structopt = "0.2.10" void = "1.0.2" boolinator = "2.4" diff --git a/examples/functionality/theorem.md b/examples/functionality/theorem.md new file mode 100644 index 0000000..132d23f --- /dev/null +++ b/examples/functionality/theorem.md @@ -0,0 +1,20 @@ +# Theorems + +{definition} +> A natural number `$ n` is called prime if it has exactly two divisors. + +{lemma, title="Euclid"} +> If `$ p` is prime and divides `$ ab` then it divides `$ a` or `$ b`. + +{corollary, title="Prime factorization"} +> Each number has a canonical decomposition into prime factors. + +{theorem, title="The main theorem"} +> There is an infinite number of prime numbers. + +{proof} +> Assume `$ p` to be the largest prime number. Define +> ```$$ +> P' = 1+ \prod^p_{i=0, i prime} i +> ``` +> Then `$ P'` is not divisible by any prime. But it has at least two divisors, one and itself, so there must a prime divisor larger than `$ p`. This is a contradiction. diff --git a/src/backend/latex/complex/math.rs b/src/backend/latex/complex/math.rs index 6e3f863..b917a47 100644 --- a/src/backend/latex/complex/math.rs +++ b/src/backend/latex/complex/math.rs @@ -1,11 +1,11 @@ +use std::borrow::Cow; use std::io::Write; -use crate::backend::latex::InlineEnvironment; use crate::backend::{Backend, CodeGenUnit}; use crate::config::Config; use crate::error::Result; use crate::frontend::range::WithRange; -use crate::generator::event::{Equation, Event}; +use crate::generator::event::{Event, MathBlock, MathBlockKind}; use crate::generator::Generator; #[derive(Debug)] @@ -30,23 +30,25 @@ impl<'a> CodeGenUnit<'a, ()> for InlineMathGen { } #[derive(Debug)] -pub struct EquationGen<'a> { - inline_fig: InlineEnvironment<'a>, +pub struct MathBlockGen<'a> { + _label: Option>>, + kind: MathBlockKind, } -impl<'a> CodeGenUnit<'a, Equation<'a>> for EquationGen<'a> { +impl<'a> CodeGenUnit<'a, MathBlock<'a>> for MathBlockGen<'a> { fn new( - _cfg: &Config, eq: WithRange>, + _cfg: &Config, eq: WithRange>, gen: &mut Generator<'a, impl Backend<'a>, impl Write>, ) -> Result { - let WithRange(Equation { label, caption }, _range) = eq; - let inline_fig = InlineEnvironment::new_figure(label, caption); + let WithRange(MathBlock { kind, label, caption }, _range) = eq; let out = gen.get_out(); - inline_fig.write_begin(&mut *out)?; - writeln!(out, "\\begin{{align*}}")?; + match kind { + MathBlockKind::Equation => writeln!(out, "\\begin{{align*}}")?, + MathBlockKind::NumberedEquation => writeln!(out, "\\begin{{align}}")?, + } - Ok(EquationGen { inline_fig }) + Ok(MathBlockGen { _label: label, kind }) } fn finish( @@ -54,38 +56,10 @@ impl<'a> CodeGenUnit<'a, Equation<'a>> for EquationGen<'a> { _peek: Option>>, ) -> Result<()> { let out = gen.get_out(); - writeln!(out, "\\end{{align*}}")?; - self.inline_fig.write_end(out)?; - Ok(()) - } -} - -#[derive(Debug)] -pub struct NumberedEquationGen<'a> { - inline_fig: InlineEnvironment<'a>, -} - -impl<'a> CodeGenUnit<'a, Equation<'a>> for NumberedEquationGen<'a> { - fn new( - _cfg: &Config, eq: WithRange>, - gen: &mut Generator<'a, impl Backend<'a>, impl Write>, - ) -> Result { - let WithRange(Equation { label, caption }, _range) = eq; - let inline_fig = InlineEnvironment::new_figure(label, caption); - let out = gen.get_out(); - inline_fig.write_begin(&mut *out)?; - - writeln!(out, "\\begin{{align}}")?; - Ok(NumberedEquationGen { inline_fig }) - } - - fn finish( - self, gen: &'_ mut Generator<'a, impl Backend<'a>, impl Write>, - _peek: Option>>, - ) -> Result<()> { - let out = gen.get_out(); - writeln!(out, "\\end{{align}}")?; - self.inline_fig.write_end(out)?; + match self.kind { + MathBlockKind::Equation => writeln!(out, "\\end{{align*}}")?, + MathBlockKind::NumberedEquation => writeln!(out, "\\end{{align}}")?, + } Ok(()) } } diff --git a/src/backend/latex/complex/mod.rs b/src/backend/latex/complex/mod.rs index 110d7f5..1c9985e 100644 --- a/src/backend/latex/complex/mod.rs +++ b/src/backend/latex/complex/mod.rs @@ -12,6 +12,7 @@ mod math; mod paragraph; mod rule; mod table; +mod proof; pub use self::blockquote::BlockQuoteGen; pub use self::codeblock::CodeBlockGen; @@ -23,7 +24,8 @@ pub use self::htmlblock::HtmlBlockGen; pub use self::inline::{InlineCodeGen, InlineEmphasisGen, InlineStrikethroughGen, InlineStrongGen}; pub use self::link::{InterLinkWithContentGen, UrlWithContentGen}; pub use self::list::{EnumerateGen, ItemGen, ListGen}; -pub use self::math::{EquationGen, InlineMathGen, NumberedEquationGen}; +pub use self::math::{InlineMathGen, MathBlockGen}; pub use self::paragraph::ParagraphGen; +pub use self::proof::ProofGen; pub use self::rule::{RuleGen, BeamerRuleGen}; pub use self::table::{TableCellGen, TableGen, TableHeadGen, TableRowGen}; diff --git a/src/backend/latex/complex/proof.rs b/src/backend/latex/complex/proof.rs new file mode 100644 index 0000000..07084fd --- /dev/null +++ b/src/backend/latex/complex/proof.rs @@ -0,0 +1,57 @@ +use std::io::Write; + +use crate::backend::{Backend, CodeGenUnit}; +use crate::config::Config; +use crate::error::Result; +use crate::frontend::range::WithRange; +use crate::generator::event::{Event, Proof, ProofKind}; +use crate::generator::Generator; + +trait ContextName { + fn context_name(&self) -> &'static str; +} + +impl ContextName for ProofKind { + fn context_name(&self) -> &'static str { + match self { + ProofKind::Corollary => "amsthm-corollary", + ProofKind::Definition => "amsthm-definition", + ProofKind::Lemma => "amsthm-lemma", + ProofKind::Proof => "proof", + ProofKind::Theorem=> "amsthm-theorem", + } + } +} + +#[derive(Debug)] +pub struct ProofGen { + kind: ProofKind, +} + +impl<'a> CodeGenUnit<'a, Proof<'a>> for ProofGen { + fn new( + _cfg: &'a Config, proof: WithRange>, + gen: &mut Generator<'a, impl Backend<'a>, impl Write>, + ) -> Result { + let WithRange(element, _range) = proof; + let out = gen.get_out(); + write!(out, "\\begin{{{}}}", element.kind.context_name())?; + if let Some(WithRange(title, _)) = element.title { + write!(out, "[{}]", title)?; + } + if let Some(WithRange(label, _)) = element.label { + write!(out, "\\label{{{}}}", label)?; + } + Ok(ProofGen { + kind: element.kind, + }) + } + + fn finish( + self, gen: &mut Generator<'a, impl Backend<'a>, impl Write>, + _peek: Option>>, + ) -> Result<()> { + write!(gen.get_out(), "\\end{{{}}}", self.kind.context_name())?; + Ok(()) + } +} diff --git a/src/backend/latex/document/article.rs b/src/backend/latex/document/article.rs index d33dc99..fdaea27 100644 --- a/src/backend/latex/document/article.rs +++ b/src/backend/latex/document/article.rs @@ -57,9 +57,9 @@ impl<'a> Backend<'a> for Article { type InlineCode = latex::InlineCodeGen; type InlineMath = latex::InlineMathGen; - type Equation = latex::EquationGen<'a>; - type NumberedEquation = latex::NumberedEquationGen<'a>; + type MathBlock = latex::MathBlockGen<'a>; type Graphviz = latex::GraphvizGen<'a>; + type Proof = latex::ProofGen; fn new() -> Self { Article diff --git a/src/backend/latex/document/beamer.rs b/src/backend/latex/document/beamer.rs index 47ac39d..e2331d5 100644 --- a/src/backend/latex/document/beamer.rs +++ b/src/backend/latex/document/beamer.rs @@ -134,9 +134,9 @@ impl<'a> Backend<'a> for Beamer { type InlineCode = latex::InlineCodeGen; type InlineMath = latex::InlineMathGen; - type Equation = latex::EquationGen<'a>; - type NumberedEquation = latex::NumberedEquationGen<'a>; + type MathBlock = latex::MathBlockGen<'a>; type Graphviz = latex::GraphvizGen<'a>; + type Proof = latex::ProofGen; fn new() -> Self { Beamer { diff --git a/src/backend/latex/document/report.rs b/src/backend/latex/document/report.rs index 78a0d69..36d3d16 100644 --- a/src/backend/latex/document/report.rs +++ b/src/backend/latex/document/report.rs @@ -57,9 +57,9 @@ impl<'a> Backend<'a> for Report { type InlineCode = latex::InlineCodeGen; type InlineMath = latex::InlineMathGen; - type Equation = latex::EquationGen<'a>; - type NumberedEquation = latex::NumberedEquationGen<'a>; + type MathBlock = latex::MathBlockGen<'a>; type Graphviz = latex::GraphvizGen<'a>; + type Proof = latex::ProofGen; fn new() -> Self { Report diff --git a/src/backend/latex/document/thesis.rs b/src/backend/latex/document/thesis.rs index 332d429..7f51bc0 100644 --- a/src/backend/latex/document/thesis.rs +++ b/src/backend/latex/document/thesis.rs @@ -65,9 +65,9 @@ impl<'a> Backend<'a> for Thesis { type InlineCode = latex::InlineCodeGen; type InlineMath = latex::InlineMathGen; - type Equation = latex::EquationGen<'a>; - type NumberedEquation = latex::NumberedEquationGen<'a>; + type MathBlock = latex::MathBlockGen<'a>; type Graphviz = latex::GraphvizGen<'a>; + type Proof = latex::ProofGen; fn new() -> Self { Thesis diff --git a/src/backend/latex/mod.rs b/src/backend/latex/mod.rs index 10dde17..bfa5eac 100644 --- a/src/backend/latex/mod.rs +++ b/src/backend/latex/mod.rs @@ -38,7 +38,6 @@ use self::complex::{ BookHeaderGen, CodeBlockGen, EnumerateGen, - EquationGen, FigureGen, FootnoteDefinitionGen, GraphvizGen, @@ -53,8 +52,9 @@ use self::complex::{ InterLinkWithContentGen, ItemGen, ListGen, - NumberedEquationGen, + MathBlockGen, ParagraphGen, + ProofGen, RuleGen, BeamerRuleGen, TableCellGen, diff --git a/src/backend/latex/preamble.rs b/src/backend/latex/preamble.rs index e3c1555..5b68432 100644 --- a/src/backend/latex/preamble.rs +++ b/src/backend/latex/preamble.rs @@ -64,6 +64,7 @@ pub fn write_packages(cfg: &Config, out: &mut impl Write) -> Result<()> { writeln!(out, "\\usepackage{{environ}}")?; writeln!(out, "\\usepackage{{amssymb}}")?; writeln!(out, "\\usepackage{{amsmath}}")?; + writeln!(out, "\\usepackage{{amsthm}}")?; writeln!(out, "\\usepackage{{stmaryrd}}")?; writeln!(out, "\\usepackage[gen]{{eurosym}}")?; writeln!(out, "\\usepackage[normalem]{{ulem}}")?; @@ -100,6 +101,7 @@ pub fn write_fixes(cfg: &Config, out: &mut impl Write) -> Result<()> { writeln!(out, "{}", IMAGE_WITH_TEXT)?; writeln!(out, "{}", SCALE_TIKZ_PICTURE_TO_WIDTH)?; writeln!(out, "{}", TABULARX)?; + writeln!(out, "{}", AMSTHM_DEFAULTS)?; // TODO: figures inline? https://tex.stackexchange.com/a/11342 last codeblock // with package float and `[H]` @@ -524,3 +526,14 @@ pub const TABULARX: &str = r#" \newcolumntype{R}{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}}X} \renewcommand\tabularxcolumn[1]{m{#1}} "#; + +// TODO: i18n? How to interact with this smartly? +pub const AMSTHM_DEFAULTS: &str = r#" +\newcounter{common-thm-ctr} +\theoremstyle{plain} +\newtheorem{amsthm-theorem}[common-thm-ctr]{Theorem} +\newtheorem{amsthm-lemma}[common-thm-ctr]{Lemma} +\newtheorem{amsthm-corollary}[common-thm-ctr]{Corollary} +\theoremstyle{amsthm-definition} +\newtheorem{amsthm-definition}[common-thm-ctr]{Definition} +"#; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 8bc6b2b..85eb33c 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -14,7 +14,6 @@ use crate::generator::event::{ BiberReference, CodeBlock, Enumerate, - Equation, Event, Figure, FootnoteDefinition, @@ -24,10 +23,12 @@ use crate::generator::event::{ Image, Svg, InterLink, + MathBlock, Pdf, Table, TaskListMarker, Url, + Proof, }; use crate::generator::{Generator, Stack}; @@ -92,9 +93,9 @@ pub trait Backend<'a>: Sized + Debug { type InlineCode: StatefulCodeGenUnit<'a, Self, ()>; type InlineMath: StatefulCodeGenUnit<'a, Self, ()>; - type Equation: StatefulCodeGenUnit<'a, Self, Equation<'a>>; - type NumberedEquation: StatefulCodeGenUnit<'a, Self, Equation<'a>>; + type MathBlock: StatefulCodeGenUnit<'a, Self, MathBlock<'a>>; type Graphviz: StatefulCodeGenUnit<'a, Self, Graphviz<'a>>; + type Proof: StatefulCodeGenUnit<'a, Self, Proof<'a>>; fn new() -> Self; fn gen_preamble(&mut self, cfg: &Config, out: &mut impl Write, diagnostics: &Diagnostics<'a>) -> FatalResult<()>; diff --git a/src/cskvp.rs b/src/cskvp.rs index 979e891..ecc3ab4 100644 --- a/src/cskvp.rs +++ b/src/cskvp.rs @@ -160,6 +160,14 @@ impl<'a> Cskvp<'a> { self.double.remove(key) } + pub fn take_single(&mut self) -> Option>> { + if self.single.is_empty() { + None + } else { + Some(self.single.remove(0)) + } + } + /// Removes all elements from `self`. /// /// This can be used before dropping `Cskvp` to omit all "unused attribute" warnings. diff --git a/src/frontend/concat.rs b/src/frontend/concat.rs index d2e4f6d..3ffb6b0 100644 --- a/src/frontend/concat.rs +++ b/src/frontend/concat.rs @@ -42,7 +42,7 @@ impl<'a> Iterator for Concat<'a> { match t { Cow::Borrowed(b) => match next { - Cow::Borrowed(next) => match str_concat::concat(b, next) { + Cow::Borrowed(next) => match unsafe { str_concat::concat(b, next) } { Ok(res) => t = Cow::Borrowed(res), Err(_) => t = Cow::Owned(b.to_string() + next), }, diff --git a/src/frontend/event.rs b/src/frontend/event.rs index 2787c6a..905a6ba 100644 --- a/src/frontend/event.rs +++ b/src/frontend/event.rs @@ -93,9 +93,9 @@ pub enum Tag<'a> { InlineCode, InlineMath, - Equation(Equation<'a>), - NumberedEquation(Equation<'a>), + MathBlock(MathBlock<'a>), Graphviz(Graphviz<'a>), + Proof(Proof<'a>), } #[derive(Debug, Clone)] @@ -148,11 +148,18 @@ pub struct Include<'a> { } #[derive(Debug, Clone)] -pub struct Equation<'a> { +pub struct MathBlock<'a> { + pub kind: MathBlockKind, pub label: Option>>, pub caption: Option>>, } +#[derive(Debug, Clone)] +pub enum MathBlockKind { + Equation, + NumberedEquation, +} + #[derive(Debug, Clone)] pub struct Graphviz<'a> { pub label: Option>>, @@ -161,3 +168,19 @@ pub struct Graphviz<'a> { pub width: Option>>, pub height: Option>>, } + +#[derive(Debug, Clone)] +pub struct Proof<'a> { + pub kind: ProofKind, + pub label: Option>>, + pub title: Option>>, +} + +#[derive(Debug, Clone)] +pub enum ProofKind { + Corollary, + Definition, + Theorem, + Lemma, + Proof, +} diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 6ee1731..6aeedd3 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -365,12 +365,19 @@ impl<'a> Frontend<'a> { let mut cskvp = cskvp.unwrap_or_default(); let tag = match &*language { "equation" | "$$" => { - Tag::Equation(Equation { label: cskvp.take_label(), caption: cskvp.take_caption() }) + Tag::MathBlock (MathBlock { + kind: MathBlockKind::Equation, + label: cskvp.take_label(), + caption: cskvp.take_caption(), + }) + }, + "numberedequation" | "$$$" => { + Tag::MathBlock(MathBlock { + kind: MathBlockKind::NumberedEquation, + label: cskvp.take_label(), + caption: cskvp.take_caption(), + }) }, - "numberedequation" | "$$$" => Tag::NumberedEquation(Equation { - label: cskvp.take_label(), - caption: cskvp.take_caption(), - }), "graphviz" => { let graphviz = Graphviz { label: cskvp.take_label(), @@ -496,6 +503,7 @@ impl<'a> Frontend<'a> { // otherwise create label event match self.parser.peek().unwrap() { WithRange(CmarkEvent::Start(CmarkTag::Header(_)), _) + | WithRange(CmarkEvent::Start(CmarkTag::BlockQuote), _) | WithRange(CmarkEvent::Start(CmarkTag::CodeBlock(_)), _) | WithRange(CmarkEvent::Start(CmarkTag::Table(_)), _) | WithRange(CmarkEvent::Start(CmarkTag::Image(..)), _) => { @@ -535,6 +543,7 @@ impl<'a> Frontend<'a> { let figure = match cskvp.take_figure().map(|f| f.element()).unwrap_or(self.cfg.figures) { false => None, true => match &next_element { + CmarkEvent::Start(CmarkTag::BlockQuote) => None, CmarkEvent::Start(CmarkTag::Table(_)) => Some(Tag::TableFigure(Figure { caption: cskvp.take_caption(), label: cskvp.take_label(), @@ -553,6 +562,9 @@ impl<'a> Frontend<'a> { CmarkEvent::Start(CmarkTag::Header(label)) => { self.convert_header(WithRange(label, next_range), Some(cskvp)) }, + CmarkEvent::Start(CmarkTag::BlockQuote) => { + self.convert_block_quote(next_range, cskvp) + }, CmarkEvent::Start(CmarkTag::CodeBlock(lang)) => { self.convert_code_block(WithRange(lang, next_range), Some(cskvp)) }, @@ -649,6 +661,45 @@ impl<'a> Frontend<'a> { self.buffer.push_back(WithRange(Event::End(tag), range)); } + fn convert_block_quote(&mut self, range: SourceRange, mut cskvp: Cskvp<'a>) { + let prefix = cskvp.take_single(); + + let kind = if let Some(prefix) = prefix { + match prefix.as_ref().element().as_ref() { + "corollary" => ProofKind::Corollary, + "definition" => ProofKind::Definition, + "lemma" => ProofKind::Lemma, + "proof" => ProofKind::Proof, + "theorem" => ProofKind::Theorem, + other => { + self.diagnostics + .error(format!("unknown quote environment {:?}", other)) + .with_error_section(prefix.range(), "type defined here") + .emit(); + return; + } + } + } else { + self.diagnostics + .error("quote environment has no type") + .with_error_section(range, "configuration defined here") + .help("try indicating the type, the configuration could look like") + .help(" {proof}") + .emit(); + return; + }; + + let tag = Tag::Proof(Proof { + kind, + label: cskvp.take_label(), + title: cskvp.take_double("title"), + }); + + self.buffer.push_back(WithRange(Event::Start(tag.clone()), range)); + self.convert_until_end_inclusive(|t| if let CmarkTag::BlockQuote = t { true } else { false }); + self.buffer.push_back(WithRange(Event::End(tag), range)); + } + fn convert_table( &mut self, WithRange(alignment, range): WithRange>, cskvp: Option>, ) { diff --git a/src/generator/code_gen_units.rs b/src/generator/code_gen_units.rs index f710a90..28875eb 100644 --- a/src/generator/code_gen_units.rs +++ b/src/generator/code_gen_units.rs @@ -35,9 +35,9 @@ pub enum StackElement<'a, B: Backend<'a>> { InlineStrikethrough(B::InlineStrikethrough), InlineCode(B::InlineCode), InlineMath(B::InlineMath), - Equation(B::Equation), - NumberedEquation(B::NumberedEquation), + MathBlock(B::MathBlock), Graphviz(B::Graphviz), + Proof(B::Proof), // resolve context Context(Context, Arc>), @@ -73,9 +73,9 @@ impl<'a, B: Backend<'a>> StackElement<'a, B> { Tag::InlineStrikethrough => Ok(InlineStrikethrough(B::InlineStrikethrough::new(cfg, WithRange((), range), gen)?)), Tag::InlineCode => Ok(InlineCode(B::InlineCode::new(cfg, WithRange((), range), gen)?)), Tag::InlineMath => Ok(InlineMath(B::InlineMath::new(cfg, WithRange((), range), gen)?)), - Tag::Equation(equation) => Ok(Equation(B::Equation::new(cfg, WithRange(equation, range), gen)?)), - Tag::NumberedEquation(equation) => Ok(NumberedEquation(B::NumberedEquation::new(cfg, WithRange(equation, range), gen)?)), + Tag::MathBlock(block) => Ok(MathBlock(B::MathBlock::new(cfg, WithRange(block, range), gen)?)), Tag::Graphviz(graphviz) => Ok(Graphviz(B::Graphviz::new(cfg, WithRange(graphviz, range), gen)?)), + Tag::Proof(proof) => Ok(Proof(B::Proof::new(cfg, WithRange(proof, range), gen)?)), } } @@ -104,9 +104,9 @@ impl<'a, B: Backend<'a>> StackElement<'a, B> { InlineStrikethrough(s) => s.output_redirect(), InlineCode(s) => s.output_redirect(), InlineMath(s) => s.output_redirect(), - Equation(s) => s.output_redirect(), - NumberedEquation(s) => s.output_redirect(), + MathBlock(s) => s.output_redirect(), Graphviz(s) => s.output_redirect(), + Proof(s) => s.output_redirect(), Context(..) => None, } @@ -137,9 +137,9 @@ impl<'a, B: Backend<'a>> StackElement<'a, B> { (InlineStrikethrough(s), Tag::InlineStrikethrough) => s.finish(gen, peek), (InlineCode(s), Tag::InlineCode) => s.finish(gen, peek), (InlineMath(s), Tag::InlineMath) => s.finish(gen, peek), - (Equation(s), Tag::Equation(_)) => s.finish(gen, peek), - (NumberedEquation(s), Tag::NumberedEquation(_)) => s.finish(gen, peek), + (MathBlock(s), Tag::MathBlock(_)) => s.finish(gen, peek), (Graphviz(s), Tag::Graphviz(_)) => s.finish(gen, peek), + (Proof(s), Tag::Proof(_)) => s.finish(gen, peek), (state, tag) => unreachable!("invalid end tag {:?}, expected {:?}", tag, state), } } @@ -176,16 +176,9 @@ impl<'a, B: Backend<'a>> StackElement<'a, B> { } } - pub fn is_equation(&self) -> bool { + pub fn is_math_block(&self) -> bool { match self { - Equation(_) => true, - _ => false, - } - } - - pub fn is_numbered_equation(&self) -> bool { - match self { - NumberedEquation(_) => true, + MathBlock(_) => true, _ => false, } } @@ -196,7 +189,7 @@ impl<'a, B: Backend<'a>> StackElement<'a, B> { } pub fn is_math(&self) -> bool { - self.is_equation() || self.is_numbered_equation() || self.is_inline_math() + self.is_math_block() || self.is_inline_math() } #[allow(dead_code)] diff --git a/src/generator/event.rs b/src/generator/event.rs index 49ec3f9..6ed91b4 100644 --- a/src/generator/event.rs +++ b/src/generator/event.rs @@ -12,16 +12,19 @@ pub use crate::frontend::{ BiberReference, CodeBlock, Enumerate, - Equation, Figure, FootnoteDefinition, FootnoteReference, Graphviz, Header, InterLink, + MathBlock, + MathBlockKind, Table, TaskListMarker, Url, + Proof, + ProofKind, }; pub use pulldown_cmark::Alignment; diff --git a/src/generator/mod.rs b/src/generator/mod.rs index cdce08c..66b4dfb 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -90,11 +90,15 @@ impl<'a, B: Backend<'a>, W: Write> Generator<'a, B, W> { }; let events = self.get_events(markdown, context, input); if let Some(template) = self.template.take() { - let body_index = - template.find("\nHERADOCBODY\n").expect("HERADOCBODY not found in template"); - self.default_out.write_all(&template.as_bytes()[..body_index])?; + let body_index = template + .find("\nHERADOCBODY\n") + .expect("HERADOCBODY not found in template on"); + + let head = &template.as_bytes()[..body_index]; + let tail = &template.as_bytes()[body_index + "\nHERADOCBODY\n".len()..]; + self.default_out.write_all(head)?; self.generate_body(events)?; - self.default_out.write_all(&template.as_bytes()[body_index + "\nHERADOCBODY\n".len()..])?; + self.default_out.write_all(tail)?; } else { let diagnostics = Arc::clone(&events.diagnostics); self.backend.gen_preamble(self.cfg, &mut self.default_out, &diagnostics)?; diff --git a/src/main.rs b/src/main.rs index 5e60829..6e45bc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![forbid(unsafe_code)] // groups #![warn(nonstandard_style)] #![warn(rust_2018_idioms)] @@ -84,7 +83,8 @@ fn main() { }; let tmpdir = TempDir::new("heradoc").expect("can't create tempdir"); - let cfg = Config::new(args, infile, file, &tmpdir); + let tmpdir = core::mem::ManuallyDrop::new(tmpdir); + let cfg = Config::new(args, infile, file, &*tmpdir); if cfg.out_dir != cfg.temp_dir { // While initializing the config, some files may already be downloaded. // Thus we must only clear the output directory if it's not a temporary directory. @@ -99,12 +99,12 @@ fn main() { let tex_file = File::create(&tex_path).expect("can't create temporary tex file"); gen(&cfg, markdown, tex_file); - pdflatex(&tmpdir, &cfg); + pdflatex(&*tmpdir, &cfg); if cfg.bibliography.is_some() { - biber(&tmpdir); - pdflatex(&tmpdir, &cfg); + biber(&*tmpdir); + pdflatex(&*tmpdir, &cfg); } - pdflatex(&tmpdir, &cfg); + pdflatex(&*tmpdir, &cfg); let mut pdf = File::open(tmpdir.path().join("document.pdf")) .expect("unable to open generated pdf"); io::copy(&mut pdf, &mut cfg.output.to_write()).expect("can't write to output"); diff --git a/test.md b/test.md index ba7e179..0a35da4 100644 --- a/test.md +++ b/test.md @@ -43,6 +43,8 @@ Reference to [#test-heading]. [include examples/functionality/math.md] +[include examples/functionality/theorem.md] + [include examples/functionality/latex-block.md] [appendix]