use carta_ast::MathType;
pub(crate) fn scan_inline_math(chars: &[char], pos: usize) -> Option<(String, usize)> {
if chars
.get(pos + 1)
.copied()
.is_none_or(is_unicode_whitespace)
{
return None;
}
let content_start = pos + 1;
let mut i = content_start;
while let Some(&ch) = chars.get(i) {
if ch == '\\' && chars.get(i + 1).is_some() {
i += 2;
continue;
}
if ch == '$' {
let prev_space = chars.get(i - 1).copied().is_none_or(is_unicode_whitespace);
let next_digit = chars.get(i + 1).is_some_and(char::is_ascii_digit);
if prev_space || next_digit {
return None;
}
let content: String = chars.get(content_start..i)?.iter().collect();
return Some((content, i + 1));
}
i += 1;
}
None
}
pub(crate) fn scan_display_math(chars: &[char], pos: usize) -> Option<(String, usize)> {
let content_start = pos + 2;
let mut i = content_start;
while chars.get(i).is_some() {
if chars.get(i) == Some(&'$') && chars.get(i + 1) == Some(&'$') {
let content: String = chars.get(content_start..i)?.iter().collect();
return Some((content, i + 2));
}
i += 1;
}
None
}
pub(crate) fn scan_backslash_math(
chars: &[char],
pos: usize,
slashes: usize,
) -> Option<(MathType, String, usize)> {
let open = pos + slashes;
let (close_bracket, math_type) = match chars.get(open).copied() {
Some('(') => (')', MathType::InlineMath),
Some('[') => (']', MathType::DisplayMath),
_ => return None,
};
let content_start = open + 1;
let mut i = content_start;
while chars.get(i).is_some() {
if is_backslash_math_closer(chars, i, slashes, close_bracket) {
if i == content_start {
return None; }
let raw: String = chars.get(content_start..i)?.iter().collect();
let content = match math_type {
MathType::InlineMath => raw.trim().to_owned(),
MathType::DisplayMath => raw,
};
return Some((math_type, content, i + slashes + 1));
}
if slashes == 1 && chars.get(i) == Some(&'\\') && chars.get(i + 1).is_some() {
i += 2;
continue;
}
i += 1;
}
None
}
fn is_backslash_math_closer(chars: &[char], i: usize, slashes: usize, close: char) -> bool {
(0..slashes).all(|k| chars.get(i + k) == Some(&'\\')) && chars.get(i + slashes) == Some(&close)
}
#[cfg(feature = "commonmark")]
pub(crate) fn fold_dash_run(len: usize) -> String {
let (em, en) = if len.is_multiple_of(3) {
(len / 3, 0)
} else if len.is_multiple_of(2) {
(0, len / 2)
} else {
let en = if len % 3 == 1 { 2 } else { 1 };
((len - 2 * en) / 3, en)
};
let mut out = String::with_capacity((em + en) * 3);
out.extend(std::iter::repeat_n('\u{2014}', em));
out.extend(std::iter::repeat_n('\u{2013}', en));
out
}
pub(crate) fn fold_ellipsis_run(len: usize) -> String {
let mut out = String::with_capacity(len / 3 * 3 + len % 3);
out.extend(std::iter::repeat_n('\u{2026}', len / 3));
out.extend(std::iter::repeat_n('.', len % 3));
out
}
pub(crate) fn is_unicode_whitespace(ch: char) -> bool {
ch == ' '
|| ch == '\t'
|| ch == '\n'
|| ch == '\u{0c}'
|| ch == '\u{0b}'
|| ch == '\r'
|| ch.is_whitespace()
}