From 2f897607e9ff84eb3c2b7659f777713bdd50756a Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 17 Feb 2018 11:29:20 +0100 Subject: [PATCH 01/23] WIP: Improve MathJax Support First commit in a branch dedicated to improving MathJax support. See issue #400. --- src/preprocess/mathjax.rs | 5 +++++ src/preprocess/mod.rs | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/preprocess/mathjax.rs diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs new file mode 100644 index 0000000000..6cbe98f902 --- /dev/null +++ b/src/preprocess/mathjax.rs @@ -0,0 +1,5 @@ +//! Preprocessor that converts mathematical expression into MathJax. +//! +//! This preprocessor takes inline expressions wrapped in `$`-pairs and block +//! expressions wrapped in `$$`-pairs and transform them into a valid MathJax +//! expression that does not interfere with the markdown parser. diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index 6f82c3381e..44563af049 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -3,6 +3,7 @@ pub use self::links::LinkPreprocessor; mod links; +mod mathjax; use book::Book; use config::Config; @@ -35,4 +36,4 @@ pub trait Preprocessor { /// Run this `Preprocessor`, allowing it to update the book before it is /// given to a renderer. fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()>; -} \ No newline at end of file +} From 643edbe87aa67ef4e2392208d0e55934f79e63a3 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 17 Feb 2018 11:52:44 +0100 Subject: [PATCH 02/23] Create a skeletal NOOP implementation of MathJaxPreprocessor --- src/preprocess/mathjax.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 6cbe98f902..538e018dec 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -3,3 +3,28 @@ //! This preprocessor takes inline expressions wrapped in `$`-pairs and block //! expressions wrapped in `$$`-pairs and transform them into a valid MathJax //! expression that does not interfere with the markdown parser. + +use errors::Result; + +use super::{Preprocessor, PreprocessorContext}; +use book::Book; + +/// a preprocessor for expanding `$`- and `$$`-pairs into valid MathJax expressions. +pub struct MathJaxPreprocessor; + +impl MathJaxPreprocessor { + /// Create a `MathJaxPreprocessor`. + pub fn new() -> Self { + MathJaxPreprocessor + } +} + +impl Preprocessor for MathJaxPreprocessor { + fn name(&self) -> &str { + "mathjax" + } + + fn run(&self, _ctx: &PreprocessorContext, _book: &mut Book) -> Result<()> { + Ok(()) + } +} From 7d0a1f197f6142d0901b6de228a8bf24dace60f1 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sun, 18 Feb 2018 08:12:31 +0100 Subject: [PATCH 03/23] Iterate over section without replacing mathematics --- src/preprocess/mathjax.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 538e018dec..75175571e0 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -7,7 +7,7 @@ use errors::Result; use super::{Preprocessor, PreprocessorContext}; -use book::Book; +use book::{Book, BookItem}; /// a preprocessor for expanding `$`- and `$$`-pairs into valid MathJax expressions. pub struct MathJaxPreprocessor; @@ -24,7 +24,20 @@ impl Preprocessor for MathJaxPreprocessor { "mathjax" } - fn run(&self, _ctx: &PreprocessorContext, _book: &mut Book) -> Result<()> { + fn run(&self, _ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { + book.for_each_mut(|section: &mut BookItem| { + if let BookItem::Chapter(ref mut chapter) = *section { + let content = replace_all_mathematics(&chapter.content); + chapter.content = content; + } + }); + Ok(()) } } + +fn replace_all_mathematics(content: &str) -> String { + let replaced = String::from(content); + + replaced +} From 16e22e1b03cbd7e4ce29cb1c5ea435b6a74e4c28 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sun, 18 Feb 2018 10:19:23 +0100 Subject: [PATCH 04/23] Create a skeletal find_mathematics function --- src/preprocess/mathjax.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 75175571e0..17cc7d6e1d 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -37,7 +37,33 @@ impl Preprocessor for MathJaxPreprocessor { } fn replace_all_mathematics(content: &str) -> String { - let replaced = String::from(content); + let mut previous_end_index = 0; + let mut replaced = String::new(); + + for _math in find_mathematics(content) { + unimplemented!(); + } + + replaced.push_str(&content[previous_end_index..]); replaced } + +fn find_mathematics(_content: &str) -> MathematicsIterator { + MathematicsIterator +} + +struct MathematicsIterator; + +impl Iterator for MathematicsIterator { + type Item = Mathematics; + + fn next(&mut self) -> Option { + None + } +} + +struct Mathematics { + start_index: usize, + end_index: usize, +} From 7657044d2fc7cb3a363a7ccbe63fec46ac733da8 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 19 Feb 2018 13:08:52 +0100 Subject: [PATCH 05/23] Describe different kind of mathematics We differentiate between inline and block. And the legacy style and the intended style. --- src/preprocess/mathjax.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 17cc7d6e1d..cffc70b302 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -66,4 +66,12 @@ impl Iterator for MathematicsIterator { struct Mathematics { start_index: usize, end_index: usize, + kind: Kind, +} + +enum Kind { + Inline, + Block, + LegacyInline, + LegacyBlock, } From b4f77eb3ab863bec7f87ddf7fffa4e2b4ac95efa Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 19 Feb 2018 13:20:20 +0100 Subject: [PATCH 06/23] Implement find_mathemtics loop inside replace_all This defers to Mathematics for a replacement which is not implemented yet. --- src/preprocess/mathjax.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index cffc70b302..c6e497a923 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -40,8 +40,10 @@ fn replace_all_mathematics(content: &str) -> String { let mut previous_end_index = 0; let mut replaced = String::new(); - for _math in find_mathematics(content) { - unimplemented!(); + for math in find_mathematics(content) { + replaced.push_str(&content[previous_end_index..math.start_index]); + replaced.push_str(math.replacement()); + previous_end_index = math.end_index; } replaced.push_str(&content[previous_end_index..]); @@ -75,3 +77,9 @@ enum Kind { LegacyInline, LegacyBlock, } + +impl Mathematics { + fn replacement(&self) -> String { + unimplemented!() + } +} From 784b814332c00131d55f49455db31c2951351e05 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 19 Feb 2018 14:13:25 +0100 Subject: [PATCH 07/23] Implement find_mathematics for inline maths --- src/preprocess/mathjax.rs | 58 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index c6e497a923..f35129b8b5 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -5,6 +5,7 @@ //! expression that does not interfere with the markdown parser. use errors::Result; +use regex::{CaptureMatches, Captures, Regex}; use super::{Preprocessor, PreprocessorContext}; use book::{Book, BookItem}; @@ -42,7 +43,7 @@ fn replace_all_mathematics(content: &str) -> String { for math in find_mathematics(content) { replaced.push_str(&content[previous_end_index..math.start_index]); - replaced.push_str(math.replacement()); + replaced.push_str(&math.replacement()); previous_end_index = math.end_index; } @@ -51,26 +52,40 @@ fn replace_all_mathematics(content: &str) -> String { replaced } -fn find_mathematics(_content: &str) -> MathematicsIterator { - MathematicsIterator +fn find_mathematics(content: &str) -> MathematicsIterator { + lazy_static! { + static ref REGEXP: Regex = Regex::new(r"(?x) # insignificant whitespace mode + \$ # a dollar sign + [^$]+ # followed by one or more things other than a dollar sign + \$ # followed by a dollar sign. + ").unwrap(); + } + MathematicsIterator(REGEXP.captures_iter(content)) } -struct MathematicsIterator; +struct MathematicsIterator<'a>(CaptureMatches<'a, 'a>); -impl Iterator for MathematicsIterator { +impl<'a> Iterator for MathematicsIterator<'a> { type Item = Mathematics; fn next(&mut self) -> Option { + for capture in &mut self.0 { + if let mathematics @ Some(_) = Mathematics::from_capture(capture) { + return mathematics; + } + } None } } +#[derive(Debug, PartialEq, Eq)] struct Mathematics { start_index: usize, end_index: usize, kind: Kind, } +#[derive(Debug, PartialEq, Eq)] enum Kind { Inline, Block, @@ -79,7 +94,40 @@ enum Kind { } impl Mathematics { + fn from_capture<'a>(captures: Captures<'a>) -> Option { + captures.get(0).map(|m| Mathematics { + start_index: m.start(), + end_index: m.end(), + kind: Kind::Inline, + }) + } + fn replacement(&self) -> String { unimplemented!() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_find_no_mathematics_in_regular_text() { + let content = "Text without mathematics"; + + assert_eq!(find_mathematics(content).count(), 0); + } + + #[test] + fn should_find_inline_mathematics() { + let content = "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$"; + + let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); + assert_eq!(result[0], Mathematics { + start_index: 21, + end_index: 44, + kind: Kind::Inline, + }) + } +} From 4aa0df1ef31f4c6ec0fa285a3cd8a1c0e6ee75ad Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 19 Feb 2018 15:03:04 +0100 Subject: [PATCH 08/23] Include mathematics text --- src/preprocess/mathjax.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index f35129b8b5..d58334c482 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -66,7 +66,7 @@ fn find_mathematics(content: &str) -> MathematicsIterator { struct MathematicsIterator<'a>(CaptureMatches<'a, 'a>); impl<'a> Iterator for MathematicsIterator<'a> { - type Item = Mathematics; + type Item = Mathematics<'a>; fn next(&mut self) -> Option { for capture in &mut self.0 { @@ -79,10 +79,11 @@ impl<'a> Iterator for MathematicsIterator<'a> { } #[derive(Debug, PartialEq, Eq)] -struct Mathematics { +struct Mathematics<'a> { start_index: usize, end_index: usize, kind: Kind, + text: &'a str, } #[derive(Debug, PartialEq, Eq)] @@ -93,12 +94,13 @@ enum Kind { LegacyBlock, } -impl Mathematics { - fn from_capture<'a>(captures: Captures<'a>) -> Option { +impl<'a> Mathematics<'a> { + fn from_capture(captures: Captures<'a>) -> Option { captures.get(0).map(|m| Mathematics { start_index: m.start(), end_index: m.end(), kind: Kind::Inline, + text: m.as_str(), }) } @@ -128,6 +130,7 @@ mod tests { start_index: 21, end_index: 44, kind: Kind::Inline, + text: "$a^{2} + b^{2} = c^{2}$", }) } } From 534dfb49cc9d7ec870dd6e1e0f2ba27c35372c6d Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 19 Feb 2018 19:50:12 +0100 Subject: [PATCH 09/23] Recognize both inline as block mathematics --- src/preprocess/mathjax.rs | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index d58334c482..42d52bcf48 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -55,9 +55,17 @@ fn replace_all_mathematics(content: &str) -> String { fn find_mathematics(content: &str) -> MathematicsIterator { lazy_static! { static ref REGEXP: Regex = Regex::new(r"(?x) # insignificant whitespace mode - \$ # a dollar sign - [^$]+ # followed by one or more things other than a dollar sign - \$ # followed by a dollar sign. + # Block mathematics is + (\$\$) # a double dollar sign + [^$]+ # followed by one or more things other than a dollar sign + (\$\$) # followed by a closing double dollar sign. + + | # or + + # Inline mathematics is + (\$) # a dollar sign + [^$]+ # followed by one or more things other than a dollar sign + (\$) # followed by a closing dollar sign. ").unwrap(); } MathematicsIterator(REGEXP.captures_iter(content)) @@ -92,14 +100,25 @@ enum Kind { Block, LegacyInline, LegacyBlock, + Unrecognized, + Unknown, } impl<'a> Mathematics<'a> { fn from_capture(captures: Captures<'a>) -> Option { + let kind = + captures.get(1).or(captures.get(3)) + .map(|delimiter| + match delimiter.as_str() { + "$$" => Kind::Block, + "$" => Kind::Inline, + _ => Kind::Unrecognized, + }); + captures.get(0).map(|m| Mathematics { start_index: m.start(), end_index: m.end(), - kind: Kind::Inline, + kind: kind.unwrap_or(Kind::Unknown), text: m.as_str(), }) } @@ -133,4 +152,18 @@ mod tests { text: "$a^{2} + b^{2} = c^{2}$", }) } + + #[test] + fn should_find_block_mathematics() { + let content = "Euler's identity: $$e^{i\\pi} + 1 = 0$$"; + + let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); + assert_eq!(result[0], Mathematics { + start_index: 18, + end_index: 38, + kind: Kind::Block, + text: "$$e^{i\\pi} + 1 = 0$$", + }) + } } From 2a05964dc03be75de49f7ab0ba627be6ebae4253 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Tue, 20 Feb 2018 08:31:14 +0100 Subject: [PATCH 10/23] Recognize both types of legacy mathematics --- src/preprocess/mathjax.rs | 72 ++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 42d52bcf48..bf4ddbac53 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -55,17 +55,33 @@ fn replace_all_mathematics(content: &str) -> String { fn find_mathematics(content: &str) -> MathematicsIterator { lazy_static! { static ref REGEXP: Regex = Regex::new(r"(?x) # insignificant whitespace mode - # Block mathematics is - (\$\$) # a double dollar sign - [^$]+ # followed by one or more things other than a dollar sign - (\$\$) # followed by a closing double dollar sign. + # Mathematics is - | # or + # Block mathematics is + (\$\$) # a double dollar sign + [^$]+ # followed by one or more things other than a dollar sign + (\$\$) # followed by a closing double dollar sign. - # Inline mathematics is - (\$) # a dollar sign - [^$]+ # followed by one or more things other than a dollar sign - (\$) # followed by a closing dollar sign. + | # or + + # Inline mathematics is + (\$) # a dollar sign + [^$]+ # followed by one or more things other than a dollar sign + (\$) # followed by a closing dollar sign. + + | # or + + # Legacy inline mathematics + (\\\\\() # An escaped opening bracket `\\(` + [^)]+ # followed by one or more other things TODO provide correct regexp. + (\\\\\)) # followed by a closing bracket `\\)` + + | # or + + # Legacy block mathematics + (\\\\\[) # An escaped opening bracket `\\[` + [^$]+ # followed by one ore more other things TODO provide correct regexp. + (\\\\\]) # followed by a closing bracket `\\]` ").unwrap(); } MathematicsIterator(REGEXP.captures_iter(content)) @@ -107,12 +123,14 @@ enum Kind { impl<'a> Mathematics<'a> { fn from_capture(captures: Captures<'a>) -> Option { let kind = - captures.get(1).or(captures.get(3)) + captures.get(1).or(captures.get(3)).or(captures.get(5)).or(captures.get(7)) .map(|delimiter| match delimiter.as_str() { - "$$" => Kind::Block, - "$" => Kind::Inline, - _ => Kind::Unrecognized, + "$$" => Kind::Block, + "$" => Kind::Inline, + "\\\\[" => Kind::LegacyBlock, + "\\\\(" => Kind::LegacyInline, + _ => Kind::Unrecognized, // Should never occur }); captures.get(0).map(|m| Mathematics { @@ -166,4 +184,32 @@ mod tests { text: "$$e^{i\\pi} + 1 = 0$$", }) } + + #[test] + fn should_find_legacy_inline_mathematics() { + let content = "Pythagorean theorem: \\\\(a^{2} + b^{2} = c^{2}\\\\)"; + + let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); + assert_eq!(result[0], Mathematics { + start_index: 21, + end_index: 48, + kind: Kind::LegacyInline, + text: "\\\\(a^{2} + b^{2} = c^{2}\\\\)", + }) + } + + #[test] + fn should_find_legacy_block_mathematics() { + let content = "Euler's identity: \\\\[e^{i\\pi} + 1 = 0\\\\]"; + + let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); + assert_eq!(result[0], Mathematics { + start_index: 18, + end_index: 40, + kind: Kind::LegacyBlock, + text: "\\\\[e^{i\\pi} + 1 = 0\\\\]", + }) + } } From 92259f16cd6decf00b375abdbc0e687095b3c72f Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Tue, 20 Feb 2018 13:46:51 +0100 Subject: [PATCH 11/23] Capture mathematics text in regular expression --- src/preprocess/mathjax.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index bf4ddbac53..0e9856f0e8 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -59,28 +59,28 @@ fn find_mathematics(content: &str) -> MathematicsIterator { # Block mathematics is (\$\$) # a double dollar sign - [^$]+ # followed by one or more things other than a dollar sign + ([^$]+) # followed by one or more things other than a dollar sign (\$\$) # followed by a closing double dollar sign. | # or # Inline mathematics is (\$) # a dollar sign - [^$]+ # followed by one or more things other than a dollar sign + ([^$]+) # followed by one or more things other than a dollar sign (\$) # followed by a closing dollar sign. | # or # Legacy inline mathematics (\\\\\() # An escaped opening bracket `\\(` - [^)]+ # followed by one or more other things TODO provide correct regexp. + ([^)]+) # followed by one or more other things TODO provide correct regexp. (\\\\\)) # followed by a closing bracket `\\)` | # or # Legacy block mathematics (\\\\\[) # An escaped opening bracket `\\[` - [^$]+ # followed by one ore more other things TODO provide correct regexp. + ([^$]+) # followed by one ore more other things TODO provide correct regexp. (\\\\\]) # followed by a closing bracket `\\]` ").unwrap(); } @@ -123,7 +123,7 @@ enum Kind { impl<'a> Mathematics<'a> { fn from_capture(captures: Captures<'a>) -> Option { let kind = - captures.get(1).or(captures.get(3)).or(captures.get(5)).or(captures.get(7)) + captures.get(1).or(captures.get(4)).or(captures.get(7)).or(captures.get(10)) .map(|delimiter| match delimiter.as_str() { "$$" => Kind::Block, From e5be09d1fccd29d9d726a51a28eee14b73b5eba7 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Tue, 20 Feb 2018 13:51:07 +0100 Subject: [PATCH 12/23] Reveal kind of mathematics earlier This is so we can strip the delimiters from the mathematics text --- src/preprocess/mathjax.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 0e9856f0e8..58f02e881e 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -116,7 +116,6 @@ enum Kind { Block, LegacyInline, LegacyBlock, - Unrecognized, Unknown, } @@ -129,14 +128,14 @@ impl<'a> Mathematics<'a> { "$$" => Kind::Block, "$" => Kind::Inline, "\\\\[" => Kind::LegacyBlock, - "\\\\(" => Kind::LegacyInline, - _ => Kind::Unrecognized, // Should never occur - }); + _ => Kind::LegacyInline, + }) + .unwrap_or(Kind::Unknown); captures.get(0).map(|m| Mathematics { start_index: m.start(), end_index: m.end(), - kind: kind.unwrap_or(Kind::Unknown), + kind: kind, text: m.as_str(), }) } From 121f63854ace720451ad0869b0f7431dd23c9687 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Tue, 20 Feb 2018 14:02:59 +0100 Subject: [PATCH 13/23] Remove delimiters from mathematics text --- src/preprocess/mathjax.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 58f02e881e..57aab11d42 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -110,7 +110,7 @@ struct Mathematics<'a> { text: &'a str, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] enum Kind { Inline, Block, @@ -136,7 +136,7 @@ impl<'a> Mathematics<'a> { start_index: m.start(), end_index: m.end(), kind: kind, - text: m.as_str(), + text: kind.text(m.as_str()), }) } @@ -145,6 +145,19 @@ impl<'a> Mathematics<'a> { } } +impl Kind { + fn text<'a>(&self, delimited_text: &'a str) -> &'a str { + let end = delimited_text.len(); + match *self { + Kind::Block => &delimited_text[2..end-2], + Kind::Inline => &delimited_text[1..end-1], + Kind::LegacyBlock => &delimited_text[3..end-3], + Kind::LegacyInline => &delimited_text[3..end-3], + Kind::Unknown => &delimited_text[..] + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -166,7 +179,7 @@ mod tests { start_index: 21, end_index: 44, kind: Kind::Inline, - text: "$a^{2} + b^{2} = c^{2}$", + text: "a^{2} + b^{2} = c^{2}", }) } @@ -180,7 +193,7 @@ mod tests { start_index: 18, end_index: 38, kind: Kind::Block, - text: "$$e^{i\\pi} + 1 = 0$$", + text: "e^{i\\pi} + 1 = 0", }) } @@ -194,7 +207,7 @@ mod tests { start_index: 21, end_index: 48, kind: Kind::LegacyInline, - text: "\\\\(a^{2} + b^{2} = c^{2}\\\\)", + text: "a^{2} + b^{2} = c^{2}", }) } @@ -208,7 +221,7 @@ mod tests { start_index: 18, end_index: 40, kind: Kind::LegacyBlock, - text: "\\\\[e^{i\\pi} + 1 = 0\\\\]", + text: "e^{i\\pi} + 1 = 0", }) } } From ea4f998aeaece5cbf9b9d9e40488b2718ef8fcb4 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Tue, 20 Feb 2018 14:46:00 +0100 Subject: [PATCH 14/23] Do actual replacement of text --- src/preprocess/mathjax.rs | 42 +++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 57aab11d42..ca4eb38781 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -116,7 +116,6 @@ enum Kind { Block, LegacyInline, LegacyBlock, - Unknown, } impl<'a> Mathematics<'a> { @@ -130,7 +129,7 @@ impl<'a> Mathematics<'a> { "\\\\[" => Kind::LegacyBlock, _ => Kind::LegacyInline, }) - .unwrap_or(Kind::Unknown); + .expect("captured mathematics should have opening delimiter at the provided indices"); captures.get(0).map(|m| Mathematics { start_index: m.start(), @@ -141,7 +140,20 @@ impl<'a> Mathematics<'a> { } fn replacement(&self) -> String { - unimplemented!() + let mut replacement = String::new(); + match self.kind { + Kind::Block | Kind::LegacyBlock => { + replacement.push_str("
$$"); + replacement.push_str(self.text); + replacement.push_str("$$
"); + }, + Kind::Inline | Kind::LegacyInline => { + replacement.push_str("$"); + replacement.push_str(self.text); + replacement.push_str("$"); + }, + } + replacement } } @@ -153,7 +165,6 @@ impl Kind { Kind::Inline => &delimited_text[1..end-1], Kind::LegacyBlock => &delimited_text[3..end-3], Kind::LegacyInline => &delimited_text[3..end-3], - Kind::Unknown => &delimited_text[..] } } } @@ -174,6 +185,7 @@ mod tests { let content = "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$"; let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); assert_eq!(result[0], Mathematics { start_index: 21, @@ -188,6 +200,7 @@ mod tests { let content = "Euler's identity: $$e^{i\\pi} + 1 = 0$$"; let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); assert_eq!(result[0], Mathematics { start_index: 18, @@ -202,6 +215,7 @@ mod tests { let content = "Pythagorean theorem: \\\\(a^{2} + b^{2} = c^{2}\\\\)"; let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); assert_eq!(result[0], Mathematics { start_index: 21, @@ -216,6 +230,7 @@ mod tests { let content = "Euler's identity: \\\\[e^{i\\pi} + 1 = 0\\\\]"; let result = find_mathematics(content).collect::>(); + assert_eq!(result.len(), 1); assert_eq!(result[0], Mathematics { start_index: 18, @@ -224,4 +239,23 @@ mod tests { text: "e^{i\\pi} + 1 = 0", }) } + + #[test] + fn should_replace_inline_mathematics() { + let content = "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$"; + + let result = replace_all_mathematics(content); + + assert_eq!(result, "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$") + } + + #[test] + fn should_replace_block_mathematics() { + let content = "Euler's identity: $$e^{i\\pi} + 1 = 0$$"; + + let result = replace_all_mathematics(content); + + assert_eq!(result, "Euler's identity:
$$e^{i\\pi} + 1 = 0$$
") + } + } From a55e640e08a4a9b8faa54de3907710e8dc072e9d Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Thu, 22 Feb 2018 06:47:26 +0100 Subject: [PATCH 15/23] Use raw strings to prevent excessive exscaping --- src/preprocess/mathjax.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index ca4eb38781..2c8c60111f 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -124,10 +124,10 @@ impl<'a> Mathematics<'a> { captures.get(1).or(captures.get(4)).or(captures.get(7)).or(captures.get(10)) .map(|delimiter| match delimiter.as_str() { - "$$" => Kind::Block, - "$" => Kind::Inline, - "\\\\[" => Kind::LegacyBlock, - _ => Kind::LegacyInline, + "$$" => Kind::Block, + "$" => Kind::Inline, + r"\\[" => Kind::LegacyBlock, + _ => Kind::LegacyInline, }) .expect("captured mathematics should have opening delimiter at the provided indices"); @@ -212,7 +212,7 @@ mod tests { #[test] fn should_find_legacy_inline_mathematics() { - let content = "Pythagorean theorem: \\\\(a^{2} + b^{2} = c^{2}\\\\)"; + let content = r"Pythagorean theorem: \\(a^{2} + b^{2} = c^{2}\\)"; let result = find_mathematics(content).collect::>(); @@ -227,7 +227,7 @@ mod tests { #[test] fn should_find_legacy_block_mathematics() { - let content = "Euler's identity: \\\\[e^{i\\pi} + 1 = 0\\\\]"; + let content = r"Euler's identity: \\[e^{i\pi} + 1 = 0\\]"; let result = find_mathematics(content).collect::>(); From 1b0abf7403a662dd9ff62ecb78be2fbe7310e05d Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Thu, 22 Feb 2018 07:01:02 +0100 Subject: [PATCH 16/23] Use the format! macro instead of push_str --- src/preprocess/mathjax.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 2c8c60111f..ba43776d82 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -140,19 +140,14 @@ impl<'a> Mathematics<'a> { } fn replacement(&self) -> String { - let mut replacement = String::new(); - match self.kind { + let replacement: String = match self.kind { Kind::Block | Kind::LegacyBlock => { - replacement.push_str("
$$"); - replacement.push_str(self.text); - replacement.push_str("$$
"); + format!("
$${}$$
", self.text) }, Kind::Inline | Kind::LegacyInline => { - replacement.push_str("$"); - replacement.push_str(self.text); - replacement.push_str("$"); + format!("${}$", self.text) }, - } + }; replacement } } From e68bee5b0ff2a024af92419761461cc21593dc04 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Thu, 22 Feb 2018 07:08:22 +0100 Subject: [PATCH 17/23] Demote text method on kind to auxiliary function --- src/preprocess/mathjax.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index ba43776d82..342915c539 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -135,7 +135,7 @@ impl<'a> Mathematics<'a> { start_index: m.start(), end_index: m.end(), kind: kind, - text: kind.text(m.as_str()), + text: strip_delimiters_from_delimited_text(&kind, m.as_str()), }) } @@ -152,15 +152,13 @@ impl<'a> Mathematics<'a> { } } -impl Kind { - fn text<'a>(&self, delimited_text: &'a str) -> &'a str { - let end = delimited_text.len(); - match *self { - Kind::Block => &delimited_text[2..end-2], - Kind::Inline => &delimited_text[1..end-1], - Kind::LegacyBlock => &delimited_text[3..end-3], - Kind::LegacyInline => &delimited_text[3..end-3], - } +fn strip_delimiters_from_delimited_text<'a>(kind: &Kind, delimited_text: &'a str) -> &'a str { + let end = delimited_text.len(); + match *kind { + Kind::Block => &delimited_text[2..end-2], + Kind::Inline => &delimited_text[1..end-1], + Kind::LegacyBlock => &delimited_text[3..end-3], + Kind::LegacyInline => &delimited_text[3..end-3], } } From 16481260c8d4f42a6bcfe3d6e6805515c4f540a8 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 24 Feb 2018 22:10:27 +0100 Subject: [PATCH 18/23] Don't capture the mathematics text --- src/preprocess/mathjax.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 342915c539..8e871859d6 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -59,28 +59,28 @@ fn find_mathematics(content: &str) -> MathematicsIterator { # Block mathematics is (\$\$) # a double dollar sign - ([^$]+) # followed by one or more things other than a dollar sign + [^$]+ # followed by one or more things other than a dollar sign (\$\$) # followed by a closing double dollar sign. | # or # Inline mathematics is (\$) # a dollar sign - ([^$]+) # followed by one or more things other than a dollar sign + [^$]+ # followed by one or more things other than a dollar sign (\$) # followed by a closing dollar sign. | # or # Legacy inline mathematics (\\\\\() # An escaped opening bracket `\\(` - ([^)]+) # followed by one or more other things TODO provide correct regexp. + [^)]+ # followed by one or more other things TODO provide correct regexp. (\\\\\)) # followed by a closing bracket `\\)` | # or # Legacy block mathematics (\\\\\[) # An escaped opening bracket `\\[` - ([^$]+) # followed by one ore more other things TODO provide correct regexp. + [^$]+ # followed by one ore more other things TODO provide correct regexp. (\\\\\]) # followed by a closing bracket `\\]` ").unwrap(); } @@ -121,7 +121,7 @@ enum Kind { impl<'a> Mathematics<'a> { fn from_capture(captures: Captures<'a>) -> Option { let kind = - captures.get(1).or(captures.get(4)).or(captures.get(7)).or(captures.get(10)) + captures.get(1).or(captures.get(3)).or(captures.get(5)).or(captures.get(7)) .map(|delimiter| match delimiter.as_str() { "$$" => Kind::Block, From 2512b87b5a40d693989fc2e564418d08e67dbe87 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Mon, 26 Feb 2018 16:18:44 +0100 Subject: [PATCH 19/23] Correct regexp for mathematics --- src/preprocess/mathjax.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 8e871859d6..32d5e5f289 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -59,28 +59,36 @@ fn find_mathematics(content: &str) -> MathematicsIterator { # Block mathematics is (\$\$) # a double dollar sign - [^$]+ # followed by one or more things other than a dollar sign + (?: # followed by one or more + [^$] # things other than a dollar sign + | # or + \\\$ # an escaped dollar sign + )+ (\$\$) # followed by a closing double dollar sign. | # or # Inline mathematics is (\$) # a dollar sign - [^$]+ # followed by one or more things other than a dollar sign + (?: # followed by one or more + [^$] # things other than a dollar sign + | # or + \\\$ # an escaped dollar sign + )+ (\$) # followed by a closing dollar sign. | # or # Legacy inline mathematics (\\\\\() # An escaped opening bracket `\\(` - [^)]+ # followed by one or more other things TODO provide correct regexp. + .+? # followed by one or more other things, but lazily (\\\\\)) # followed by a closing bracket `\\)` | # or # Legacy block mathematics (\\\\\[) # An escaped opening bracket `\\[` - [^$]+ # followed by one ore more other things TODO provide correct regexp. + .+? # followed by one ore more other things, but lazily (\\\\\]) # followed by a closing bracket `\\]` ").unwrap(); } From 1871b87dcea7f1f78220da5360f633395e85be99 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 3 Mar 2018 16:07:31 +0100 Subject: [PATCH 20/23] Test text with a single dollar sign --- src/preprocess/mathjax.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 32d5e5f289..030d890989 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -181,6 +181,14 @@ mod tests { assert_eq!(find_mathematics(content).count(), 0); } + #[test] + fn should_find_no_mathematics_in_regular_text_with_a_single_dollar_sign() { + let content = "Text with a single $ mathematics"; + + assert_eq!(find_mathematics(content).count(), 0); + } + + #[test] fn should_find_inline_mathematics() { let content = "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$"; From 92b5fad127097c7a4f177359060b6dedbd461bfc Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 3 Mar 2018 16:10:03 +0100 Subject: [PATCH 21/23] Detect mathematics over multiple lines --- src/preprocess/mathjax.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 030d890989..955b2a522a 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -188,6 +188,12 @@ mod tests { assert_eq!(find_mathematics(content).count(), 0); } + #[test] + fn should_find_mathematics_spanning_over_multiple_lines() { + let content = "Mathematics $a +\n b$ over multiple lines"; + + assert_eq!(find_mathematics(content).count(), 1); + } #[test] fn should_find_inline_mathematics() { From 629c778d3388a566b0f421b549730f869e1dee08 Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 3 Mar 2018 16:12:02 +0100 Subject: [PATCH 22/23] Test non-matching delimiters --- src/preprocess/mathjax.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index 955b2a522a..af4246eb58 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -188,6 +188,14 @@ mod tests { assert_eq!(find_mathematics(content).count(), 0); } + + #[test] + fn should_find_no_mathematics_when_delimiters_do_not_match() { + let content = "$$Text with a non matching delimiters mathematics\\]"; + + assert_eq!(find_mathematics(content).count(), 0); + } + #[test] fn should_find_mathematics_spanning_over_multiple_lines() { let content = "Mathematics $a +\n b$ over multiple lines"; From 7540a3ea59faf6fcd1e95babc4757f2f457093ca Mon Sep 17 00:00:00 2001 From: Daan van Berkel Date: Sat, 3 Mar 2018 16:17:32 +0100 Subject: [PATCH 23/23] Run `cargo fmt` on preprocess/mathjax.rs --- src/preprocess/mathjax.rs | 105 ++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/src/preprocess/mathjax.rs b/src/preprocess/mathjax.rs index af4246eb58..b40d6305b5 100644 --- a/src/preprocess/mathjax.rs +++ b/src/preprocess/mathjax.rs @@ -128,15 +128,17 @@ enum Kind { impl<'a> Mathematics<'a> { fn from_capture(captures: Captures<'a>) -> Option { - let kind = - captures.get(1).or(captures.get(3)).or(captures.get(5)).or(captures.get(7)) - .map(|delimiter| - match delimiter.as_str() { - "$$" => Kind::Block, - "$" => Kind::Inline, - r"\\[" => Kind::LegacyBlock, - _ => Kind::LegacyInline, - }) + let kind = captures + .get(1) + .or(captures.get(3)) + .or(captures.get(5)) + .or(captures.get(7)) + .map(|delimiter| match delimiter.as_str() { + "$$" => Kind::Block, + "$" => Kind::Inline, + r"\\[" => Kind::LegacyBlock, + _ => Kind::LegacyInline, + }) .expect("captured mathematics should have opening delimiter at the provided indices"); captures.get(0).map(|m| Mathematics { @@ -149,12 +151,12 @@ impl<'a> Mathematics<'a> { fn replacement(&self) -> String { let replacement: String = match self.kind { - Kind::Block | Kind::LegacyBlock => { + Kind::Block | Kind::LegacyBlock => { format!("
$${}$$
", self.text) - }, + } Kind::Inline | Kind::LegacyInline => { format!("${}$", self.text) - }, + } }; replacement } @@ -163,10 +165,10 @@ impl<'a> Mathematics<'a> { fn strip_delimiters_from_delimited_text<'a>(kind: &Kind, delimited_text: &'a str) -> &'a str { let end = delimited_text.len(); match *kind { - Kind::Block => &delimited_text[2..end-2], - Kind::Inline => &delimited_text[1..end-1], - Kind::LegacyBlock => &delimited_text[3..end-3], - Kind::LegacyInline => &delimited_text[3..end-3], + Kind::Block => &delimited_text[2..end - 2], + Kind::Inline => &delimited_text[1..end - 1], + Kind::LegacyBlock => &delimited_text[3..end - 3], + Kind::LegacyInline => &delimited_text[3..end - 3], } } @@ -188,7 +190,6 @@ mod tests { assert_eq!(find_mathematics(content).count(), 0); } - #[test] fn should_find_no_mathematics_when_delimiters_do_not_match() { let content = "$$Text with a non matching delimiters mathematics\\]"; @@ -210,12 +211,15 @@ mod tests { let result = find_mathematics(content).collect::>(); assert_eq!(result.len(), 1); - assert_eq!(result[0], Mathematics { - start_index: 21, - end_index: 44, - kind: Kind::Inline, - text: "a^{2} + b^{2} = c^{2}", - }) + assert_eq!( + result[0], + Mathematics { + start_index: 21, + end_index: 44, + kind: Kind::Inline, + text: "a^{2} + b^{2} = c^{2}", + } + ) } #[test] @@ -225,12 +229,15 @@ mod tests { let result = find_mathematics(content).collect::>(); assert_eq!(result.len(), 1); - assert_eq!(result[0], Mathematics { - start_index: 18, - end_index: 38, - kind: Kind::Block, - text: "e^{i\\pi} + 1 = 0", - }) + assert_eq!( + result[0], + Mathematics { + start_index: 18, + end_index: 38, + kind: Kind::Block, + text: "e^{i\\pi} + 1 = 0", + } + ) } #[test] @@ -240,12 +247,15 @@ mod tests { let result = find_mathematics(content).collect::>(); assert_eq!(result.len(), 1); - assert_eq!(result[0], Mathematics { - start_index: 21, - end_index: 48, - kind: Kind::LegacyInline, - text: "a^{2} + b^{2} = c^{2}", - }) + assert_eq!( + result[0], + Mathematics { + start_index: 21, + end_index: 48, + kind: Kind::LegacyInline, + text: "a^{2} + b^{2} = c^{2}", + } + ) } #[test] @@ -255,12 +265,15 @@ mod tests { let result = find_mathematics(content).collect::>(); assert_eq!(result.len(), 1); - assert_eq!(result[0], Mathematics { - start_index: 18, - end_index: 40, - kind: Kind::LegacyBlock, - text: "e^{i\\pi} + 1 = 0", - }) + assert_eq!( + result[0], + Mathematics { + start_index: 18, + end_index: 40, + kind: Kind::LegacyBlock, + text: "e^{i\\pi} + 1 = 0", + } + ) } #[test] @@ -269,7 +282,10 @@ mod tests { let result = replace_all_mathematics(content); - assert_eq!(result, "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$") + assert_eq!( + result, + "Pythagorean theorem: $a^{2} + b^{2} = c^{2}$" + ) } #[test] @@ -278,7 +294,10 @@ mod tests { let result = replace_all_mathematics(content); - assert_eq!(result, "Euler's identity:
$$e^{i\\pi} + 1 = 0$$
") + assert_eq!( + result, + "Euler's identity:
$$e^{i\\pi} + 1 = 0$$
" + ) } }