Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for serializing math nodes as markdown #148

Merged
merged 8 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions mdast_util_to_markdown/src/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ pub struct Options {
/// Whether to join definitions without a blank line (`bool`, default:
/// `false`).
pub tight_definitions: bool,
/// (`bool`, default: `true`) — whether to support math (text) with a single dollar.
/// Single dollars work in Pandoc and many other places, but often interfere with “normal”
/// dollars in text. If you turn this off, you can still use two or more dollars for text math
pub single_dollar_text_math: bool,
}

impl Default for Options {
Expand All @@ -92,6 +96,7 @@ impl Default for Options {
setext: false,
strong: '*',
tight_definitions: false,
single_dollar_text_math: true,
}
}
}
bnchi marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions mdast_util_to_markdown/src/construct_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,24 @@ pub enum ConstructName {
/// ^^^
/// ```
TitleQuote,
/// Math (flow).
///
/// ```markdown
/// > | $$
/// ^^
/// > | a
/// ^
/// > | $$
/// ^^
/// ```
MathFlow,
/// Math (flow) meta flag.
///
/// ```markdown
/// > | $$a
/// ^
/// | b
/// | $$
/// ```
MathFlowMeta,
bnchi marked this conversation as resolved.
Show resolved Hide resolved
}
82 changes: 82 additions & 0 deletions mdast_util_to_markdown/src/handle/inline_math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! JS equivalent: https://github.com/syntax-tree/mdast-util-math/blob/main/lib/index.js#L241
wooorm marked this conversation as resolved.
Show resolved Hide resolved

use super::Handle;
use crate::state::{Info, State};
use alloc::format;
use markdown::{
mdast::{InlineMath, Node},
message::Message,
};
use regex::Regex;

impl Handle for InlineMath {
fn handle(
&self,
state: &mut State,
_info: &Info,
_parent: Option<&Node>,
_node: &Node,
) -> Result<alloc::string::String, Message> {
let mut size: usize = if !state.options.single_dollar_text_math {
2
} else {
1
};

let pattern = format!("(^|[^$]){}([^$]|$)", "\\$".repeat(size));
let mut dollar_sign_match = Regex::new(&pattern).unwrap();
while dollar_sign_match.is_match(&self.value) {
size += 1;
let pattern = format!("(^|[^$]){}([^$]|$)", "\\$".repeat(size));
dollar_sign_match = Regex::new(&pattern).unwrap();
}

let sequence = "$".repeat(size);

let no_whitespaces = !self.value.chars().all(char::is_whitespace);
let starts_with_whitespace = self.value.starts_with(char::is_whitespace);
let ends_with_whitespace = self.value.ends_with(char::is_whitespace);
let starts_with_dollar = self.value.starts_with('$');
let ends_with_dollar = self.value.ends_with('$');

let mut value = self.value.clone();
if no_whitespaces
&& ((starts_with_whitespace && ends_with_whitespace)
|| starts_with_dollar
|| ends_with_dollar)
{
value = format!(" {} ", value);
}

for pattern in &mut state.r#unsafe {
if !pattern.at_break {
continue;
}

State::compile_pattern(pattern);

if let Some(regex) = &pattern.compiled {
while let Some(m) = regex.find(&value) {
let position = m.start();

let position = if position > 0
&& &value[position..m.len()] == "\n"
&& &value[position - 1..position] == "\r"
{
position - 1
} else {
position
};

value.replace_range(position..m.start() + 1, " ");
}
}
}

Ok(format!("{}{}{}", sequence, value, sequence))
}
}

pub fn peek_inline_math() -> char {
'$'
}
46 changes: 46 additions & 0 deletions mdast_util_to_markdown/src/handle/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! JS equivalent: https://github.com/syntax-tree/mdast-util-math/blob/main/lib/index.js#L204

use super::Handle;
use crate::{
construct_name::ConstructName,
state::{Info, State},
util::{longest_char_streak::longest_char_streak, safe::SafeConfig},
};
use alloc::string::String;
use markdown::{
mdast::{Math, Node},
message::Message,
};

impl Handle for Math {
fn handle(
&self,
state: &mut State,
_info: &Info,
_parent: Option<&Node>,
_node: &Node,
) -> Result<alloc::string::String, Message> {
let sequence = "$".repeat((longest_char_streak(&self.value, '$') + 1).max(2));
state.enter(ConstructName::MathFlow);

let mut value = String::new();
value.push_str(&sequence);

if let Some(meta) = &self.meta {
state.enter(ConstructName::MathFlowMeta);
value.push_str(&state.safe(meta, &SafeConfig::new(&value, "\n", Some('$'))));
state.exit();
}

value.push('\n');

if !self.value.is_empty() {
value.push_str(&self.value);
value.push('\n');
}

value.push_str(&sequence);
state.exit();
Ok(value)
}
}
2 changes: 2 additions & 0 deletions mdast_util_to_markdown/src/handle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ pub mod html;
pub mod image;
pub mod image_reference;
pub mod inline_code;
pub mod inline_math;
pub mod link;
pub mod link_reference;
mod list;
mod list_item;
mod math;
mod paragraph;
mod root;
pub mod strong;
Expand Down
1 change: 1 addition & 0 deletions mdast_util_to_markdown/src/handle/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn phrasing(child: &Node) -> bool {
| Node::Emphasis(_)
| Node::ImageReference(_)
| Node::Image(_)
| Node::InlineMath(_)
bnchi marked this conversation as resolved.
Show resolved Hide resolved
| Node::InlineCode(_)
| Node::LinkReference(_)
| Node::Link(_)
Expand Down
10 changes: 7 additions & 3 deletions mdast_util_to_markdown/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use crate::{
construct_name::ConstructName,
handle::{
emphasis::peek_emphasis, html::peek_html, image::peek_image,
image_reference::peek_image_reference, inline_code::peek_inline_code, link::peek_link,
link_reference::peek_link_reference, strong::peek_strong, Handle,
image_reference::peek_image_reference, inline_code::peek_inline_code,
inline_math::peek_inline_math, link::peek_link, link_reference::peek_link_reference,
strong::peek_strong, Handle,
},
r#unsafe::Unsafe,
util::{
Expand Down Expand Up @@ -322,6 +323,8 @@ impl<'a> State<'a> {
Node::Strong(strong) => strong.handle(self, info, parent, node),
Node::Text(text) => text.handle(self, info, parent, node),
Node::ThematicBreak(thematic_break) => thematic_break.handle(self, info, parent, node),
Node::Math(math) => math.handle(self, info, parent, node),
Node::InlineMath(inline_math) => inline_math.handle(self, info, parent, node),
_ => Err(Message {
place: None,
reason: format!("Unexpected node type `{:?}`", node),
Expand Down Expand Up @@ -409,7 +412,7 @@ impl<'a> State<'a> {
index_stack: Vec::new(),
options,
stack: Vec::new(),
r#unsafe: Unsafe::get_default_unsafe(),
r#unsafe: Unsafe::get_default_unsafe(options),
}
}

Expand All @@ -424,6 +427,7 @@ impl<'a> State<'a> {
Node::LinkReference(_) => Some(peek_link_reference()),
Node::Link(link) => Some(peek_link(link, node, self)),
Node::Strong(_) => Some(peek_strong(self)),
Node::InlineMath(_) => Some(peek_inline_math()),
_ => None,
}
}
Expand Down
27 changes: 25 additions & 2 deletions mdast_util_to_markdown/src/unsafe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! JS equivalent: <https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/unsafe.js>.
//! Also: <https://github.com/syntax-tree/mdast-util-to-markdown/blob/fd6a508/lib/types.js#L287-L305>.

use crate::construct_name::ConstructName;
use crate::{construct_name::ConstructName, Options};
use alloc::{vec, vec::Vec};
use regex::Regex;

Expand Down Expand Up @@ -38,7 +38,7 @@ impl<'a> Unsafe<'a> {
}
}

pub fn get_default_unsafe() -> Vec<Self> {
pub fn get_default_unsafe(options: &Options) -> Vec<Self> {
let full_phrasing_spans = vec![
ConstructName::Autolink,
ConstructName::DestinationLiteral,
Expand Down Expand Up @@ -87,6 +87,7 @@ impl<'a> Unsafe<'a> {
ConstructName::CodeFencedMetaTilde,
ConstructName::DestinationLiteral,
ConstructName::HeadingAtx,
ConstructName::MathFlowMeta,
],
vec![],
false,
Expand All @@ -102,6 +103,7 @@ impl<'a> Unsafe<'a> {
ConstructName::CodeFencedMetaTilde,
ConstructName::DestinationLiteral,
ConstructName::HeadingAtx,
ConstructName::MathFlowMeta,
],
vec![],
false,
Expand Down Expand Up @@ -308,6 +310,27 @@ impl<'a> Unsafe<'a> {
false,
),
Self::new('~', None, None, vec![], vec![], true),
Self::new(
'$',
None,
if options.single_dollar_text_math {
None
} else {
"\\$".into()
},
vec![ConstructName::Phrasing],
vec![],
false,
),
Self::new(
'$',
None,
None,
vec![ConstructName::MathFlowMeta],
vec![],
false,
),
Self::new('$', None, "\\$".into(), vec![], vec![], true),
]
}

Expand Down
Loading