use crate::chars::latex_table::LATEX_TABLE;
use crate::strings::latex::ToLatex;
use core::fmt::{Formatter, Result, Write};
fn lookup(c: char) -> Option<(bool, &'static str)> {
LATEX_TABLE
.binary_search_by_key(&c, |&(k, _, _)| k)
.ok()
.map(|i| (LATEX_TABLE[i].1, LATEX_TABLE[i].2))
}
fn script_kind(c: char) -> Option<char> {
match lookup(c) {
Some((true, latex)) => match latex.as_bytes().first() {
Some(&k @ (b'^' | b'_')) => Some(char::from(k)),
_ => None,
},
_ => None,
}
}
fn needs_braces(latex: &str) -> bool {
latex.starts_with('\\') && latex.ends_with(|c: char| c.is_ascii_alphabetic())
}
const fn is_space_like(c: char) -> bool {
matches!(c, ' ' | '\t' | '\n' | '\r' | '\u{b}' | '\u{c}')
}
const fn forms_ligature(prev: char, c: char) -> bool {
matches!(
(prev, c),
('-', '-') | ('\'', '\'') | (',', ',') | ('`' | '!' | '?', '`')
)
}
pub(crate) fn fmt_latex_chars<I: Iterator<Item = char>>(cs: I, f: &mut Formatter) -> Result {
let mut cs = cs.peekable();
let mut in_text = false;
let mut last = None;
let mut any = false;
let mut needs_base = true;
while let Some(c) = cs.next() {
any = true;
match lookup(c) {
Some((true, latex)) if script_kind(c).is_some() => {
let kind = script_kind(c).unwrap();
if in_text {
f.write_char('}')?;
in_text = false;
last = None;
}
if needs_base {
f.write_str("{}")?;
}
f.write_char(kind)?;
let braced = cs.peek().copied().and_then(script_kind) == Some(kind);
if braced {
f.write_str("{")?;
}
f.write_str(&latex[1..])?;
while cs.peek().copied().and_then(script_kind) == Some(kind) {
let c = cs.next().unwrap();
f.write_str(&lookup(c).unwrap().1[1..])?;
}
if braced {
f.write_str("}")?;
}
needs_base = true;
}
Some((true, latex)) => {
if in_text {
f.write_char('}')?;
in_text = false;
last = None;
}
f.write_str(latex)?;
needs_base = false;
}
Some((false, latex)) => {
needs_base = false;
if !in_text {
f.write_str("\\text{")?;
in_text = true;
}
if let (Some(p), Some(n)) = (last, latex.chars().next())
&& forms_ligature(p, n)
{
f.write_str("{}")?;
}
last = if needs_braces(latex) {
f.write_char('{')?;
f.write_str(latex)?;
f.write_char('}')?;
Some('}')
} else {
f.write_str(latex)?;
latex.chars().next_back()
};
}
None => {
needs_base = false;
if !in_text {
f.write_str("\\text{")?;
in_text = true;
}
if is_space_like(c) {
if c == ' ' && last != Some(' ') {
f.write_char(' ')?;
} else {
f.write_str("\\ ")?;
}
last = Some(' ');
continue;
}
if let Some(p) = last
&& forms_ligature(p, c)
{
f.write_str("{}")?;
}
f.write_char(c)?;
last = Some(c);
}
}
}
if in_text {
f.write_char('}')
} else if any {
Ok(())
} else {
f.write_str("\\text{}")
}
}
impl ToLatex for char {
#[inline]
fn fmt_latex(&self, f: &mut Formatter) -> Result {
fmt_latex_chars(core::iter::once(*self), f)
}
}
pub(crate) fn latex_spelling(c: char) -> Option<&'static str> {
lookup(c).map(|(_, latex)| latex)
}