feat: parse Latex environment
This commit is contained in:
parent
e009e1c199
commit
d19b95523a
195
src/elements/latex.rs
Normal file
195
src/elements/latex.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
use nom::{
|
||||
bytes::complete::{tag, take_until},
|
||||
sequence::delimited,
|
||||
IResult,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LatexEnvironment<'a> {
|
||||
pub contents: Cow<'a, str>,
|
||||
pub argument: Cow<'a, str>,
|
||||
pub inline: bool,
|
||||
}
|
||||
|
||||
impl<'a> LatexEnvironment<'a> {
|
||||
#[inline]
|
||||
pub fn parse(input: &str) -> Option<(&str, LatexEnvironment)> {
|
||||
let bytes = input.as_bytes();
|
||||
match (bytes[0], bytes[1]) {
|
||||
(b'\\', b'[') => parse_internal_delimited(input, "\\[", "\\]", false).ok(),
|
||||
(b'\\', b'(') => parse_internal_delimited(input, "\\(", "\\)", true).ok(),
|
||||
(b'\\', _) => parse_internal_environment(input).ok(),
|
||||
(b'$', b'$') => parse_internal_delimited(input, "$$", "$$", false).ok(),
|
||||
(b'$', _) => parse_internal_delimited(input, "$", "$", true).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> LatexEnvironment<'static> {
|
||||
LatexEnvironment {
|
||||
contents: self.contents.into_owned().into(),
|
||||
argument: self.argument.into_owned().into(),
|
||||
inline: self.inline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_internal_delimited<'a>(
|
||||
input: &'a str,
|
||||
starts: &str,
|
||||
ends: &str,
|
||||
inline: bool,
|
||||
) -> IResult<&'a str, LatexEnvironment<'a>, ()> {
|
||||
let (input, contents) = delimited(tag(starts), take_until(ends), tag(ends))(input)?;
|
||||
Ok((
|
||||
input,
|
||||
LatexEnvironment {
|
||||
contents: contents.trim().into(),
|
||||
argument: "".into(),
|
||||
inline: inline,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_internal_environment(input: &str) -> IResult<&str, LatexEnvironment, ()> {
|
||||
let (input, argument) = delimited(tag("\\begin{"), take_until("}"), tag("}"))(input)?;
|
||||
let end = &format!("\\end{{{}}}", argument)[..];
|
||||
let (input, contents) = take_until(end)(input)?;
|
||||
let (input, _) = tag(end)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
LatexEnvironment {
|
||||
contents: contents.trim().into(),
|
||||
argument: argument.into(),
|
||||
inline: false,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_environment() {
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("\\[\n\\frac{1}{3}\n\n\\]"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "\\frac{1}{3}".into(),
|
||||
argument: "".into(),
|
||||
inline: false,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("$$\n42!\n\n$$"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "42!".into(),
|
||||
argument: "".into(),
|
||||
inline: false,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse(
|
||||
r#"\begin{equation}
|
||||
\int^{a}_{b}\,f(x)\,dx
|
||||
\end{equation}"#
|
||||
),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "\\int^{a}_{b}\\,f(x)\\,dx".into(),
|
||||
argument: "equation".into(),
|
||||
inline: false,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse(
|
||||
r#"\begin{equation}
|
||||
\int^{a}_{b}\,f(x)\,dx
|
||||
\end{align}"#
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_inline() {
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("$\\frac{1}{3}$"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "\\frac{1}{3}".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("\\(\\frac{1}{3}\\)"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "\\frac{1}{3}".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("$ text with spaces $"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "text with spaces".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("\\( text with spaces \\)"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "text with spaces".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("$ LaTeXxxx$"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "LaTeXxxx".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
LatexEnvironment::parse("\\(b\nol\nd\\)"),
|
||||
Some((
|
||||
"",
|
||||
LatexEnvironment {
|
||||
contents: "b\nol\nd".into(),
|
||||
argument: "".into(),
|
||||
inline: true,
|
||||
}
|
||||
))
|
||||
);
|
||||
assert_eq!(LatexEnvironment::parse("$$b\nol\nd*"), None);
|
||||
assert_eq!(LatexEnvironment::parse("$b\nol\nd*"), None);
|
||||
}
|
|
@ -13,6 +13,7 @@ pub(crate) mod fn_ref;
|
|||
pub(crate) mod inline_call;
|
||||
pub(crate) mod inline_src;
|
||||
pub(crate) mod keyword;
|
||||
pub(crate) mod latex;
|
||||
pub(crate) mod link;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod macros;
|
||||
|
@ -41,6 +42,7 @@ pub use self::{
|
|||
inline_call::InlineCall,
|
||||
inline_src::InlineSrc,
|
||||
keyword::{BabelCall, Keyword},
|
||||
latex::LatexEnvironment,
|
||||
link::Link,
|
||||
list::{List, ListItem},
|
||||
macros::Macros,
|
||||
|
@ -85,6 +87,7 @@ pub enum Element<'a> {
|
|||
Link(Link<'a>),
|
||||
List(List),
|
||||
ListItem(ListItem<'a>),
|
||||
LatexEnvironment(LatexEnvironment<'a>),
|
||||
Macros(Macros<'a>),
|
||||
Snippet(Snippet<'a>),
|
||||
Text { value: Cow<'a, str> },
|
||||
|
@ -137,6 +140,7 @@ impl Element<'_> {
|
|||
use Element::*;
|
||||
|
||||
match self {
|
||||
LatexEnvironment(e) => LatexEnvironment(e.into_owned()),
|
||||
SpecialBlock(e) => SpecialBlock(e.into_owned()),
|
||||
QuoteBlock(e) => QuoteBlock(e.into_owned()),
|
||||
CenterBlock(e) => CenterBlock(e.into_owned()),
|
||||
|
|
|
@ -77,6 +77,19 @@ impl HtmlHandler<Error> for DefaultHtmlHandler {
|
|||
write!(w, "<ul>")?;
|
||||
}
|
||||
}
|
||||
Element::LatexEnvironment(latex_inline) => {
|
||||
if latex_inline.inline {
|
||||
write!(&mut w, "\\({}\\)", latex_inline.contents)?
|
||||
} else if latex_inline.argument == "" {
|
||||
write!(&mut w, "\n\\[{}\\]\n", latex_inline.contents)?
|
||||
} else {
|
||||
write!(
|
||||
&mut w,
|
||||
"\n\\begin{{{0}}}{1}\\end{{{0}}}\n",
|
||||
latex_inline.argument, latex_inline.contents
|
||||
)?
|
||||
}
|
||||
}
|
||||
Element::Italic => write!(w, "<i>")?,
|
||||
Element::ListItem(_) => write!(w, "<li>")?,
|
||||
Element::Paragraph { .. } => write!(w, "<p>")?,
|
||||
|
|
|
@ -51,6 +51,19 @@ impl OrgHandler<Error> for DefaultOrgHandler {
|
|||
}
|
||||
write!(&mut w, "{}", list_item.bullet)?;
|
||||
}
|
||||
Element::LatexEnvironment(latex_inline) => {
|
||||
if latex_inline.inline {
|
||||
write!(&mut w, "${}$", latex_inline.contents)?
|
||||
} else if latex_inline.argument == "" {
|
||||
write!(&mut w, "\\[{}\\]", latex_inline.contents)?
|
||||
} else {
|
||||
write!(
|
||||
&mut w,
|
||||
"\\begin{{{0}}}{1}\\end{{{0}}}",
|
||||
latex_inline.argument, latex_inline.contents
|
||||
)?
|
||||
}
|
||||
}
|
||||
Element::Paragraph { .. } => (),
|
||||
Element::Section => (),
|
||||
Element::Strike => write!(w, "+")?,
|
||||
|
|
|
@ -10,8 +10,8 @@ use crate::config::ParseConfig;
|
|||
use crate::elements::{
|
||||
block::RawBlock, emphasis::Emphasis, keyword::RawKeyword, radio_target::parse_radio_target,
|
||||
Clock, Comment, Cookie, Drawer, DynBlock, Element, FixedWidth, FnDef, FnRef, InlineCall,
|
||||
InlineSrc, Link, List, ListItem, Macros, Rule, Snippet, Table, TableCell, TableRow, Target,
|
||||
Timestamp, Title,
|
||||
InlineSrc, LatexEnvironment, Link, List, ListItem, Macros, Rule, Snippet, Table, TableCell,
|
||||
TableRow, Target, Timestamp, Title,
|
||||
};
|
||||
use crate::parse::combinators::lines_while;
|
||||
|
||||
|
@ -287,10 +287,7 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
|
|||
arena.append(clock, parent);
|
||||
Some(tail)
|
||||
}
|
||||
b'\'' => {
|
||||
// TODO: LaTeX environment
|
||||
None
|
||||
}
|
||||
b'\'' => None,
|
||||
b'-' => {
|
||||
if let Some((tail, rule)) = Rule::parse(contents) {
|
||||
arena.append(rule, parent);
|
||||
|
@ -486,6 +483,11 @@ pub fn parse_inline<'a, T: ElementArena<'a>>(
|
|||
Some(tail)
|
||||
}
|
||||
}
|
||||
b'$' | b'\\' => {
|
||||
let (tail, latex) = LatexEnvironment::parse(contents)?;
|
||||
arena.append(Element::LatexEnvironment(latex), parent);
|
||||
Some(tail)
|
||||
}
|
||||
b'*' | b'+' | b'/' | b'_' | b'=' | b'~' => {
|
||||
let (tail, emphasis) = Emphasis::parse(contents, byte)?;
|
||||
let (element, content) = emphasis.into_element();
|
||||
|
|
|
@ -83,11 +83,7 @@ impl Org<'_> {
|
|||
}
|
||||
|
||||
for child in children {
|
||||
expect_element!(
|
||||
child,
|
||||
"Headline",
|
||||
Element::Headline { .. }
|
||||
);
|
||||
expect_element!(child, "Headline", Element::Headline { .. });
|
||||
}
|
||||
}
|
||||
Element::Headline { .. } => {
|
||||
|
@ -107,11 +103,7 @@ impl Org<'_> {
|
|||
}
|
||||
|
||||
for child in children {
|
||||
expect_element!(
|
||||
child,
|
||||
"Headline",
|
||||
Element::Headline { .. }
|
||||
);
|
||||
expect_element!(child, "Headline", Element::Headline { .. });
|
||||
}
|
||||
}
|
||||
Element::Title(title) => {
|
||||
|
@ -191,7 +183,8 @@ impl Org<'_> {
|
|||
| Element::DynBlock(_) => {
|
||||
expect_children!(node_id);
|
||||
}
|
||||
Element::ListItem(_)
|
||||
Element::LatexEnvironment(_)
|
||||
| Element::ListItem(_)
|
||||
| Element::Drawer(_)
|
||||
| Element::TableCell(_)
|
||||
| Element::Table(_) => (),
|
||||
|
|
Loading…
Reference in a new issue