Better math atoms

This commit is contained in:
Laurenz 2023-01-22 13:27:49 +01:00
parent a50cb58823
commit ea378e89b4
2 changed files with 48 additions and 28 deletions

View File

@ -10,7 +10,7 @@ use super::*;
/// ## Category /// ## Category
/// math /// math
#[func] #[func]
#[capable(Texify)] #[capable(LayoutMath)]
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct AtomNode(pub EcoString); pub struct AtomNode(pub EcoString);
@ -21,26 +21,35 @@ impl AtomNode {
} }
} }
impl Texify for AtomNode { impl LayoutMath for AtomNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let multi = self.0.graphemes(true).count() > 1; let mut chars = self.0.chars();
if multi { if let Some(glyph) = chars
t.push_str("\\mathrm{"); .next()
} .filter(|_| chars.next().is_none())
.and_then(|c| GlyphFragment::try_new(ctx, c))
for c in self.0.chars() { {
let supportive = c == '|'; // A single letter that is available in the math font.
if supportive { if ctx.style.size == MathSize::Display
t.support(); && glyph.class() == Some(MathClass::Large)
{
let height = scaled!(ctx, display_operator_min_height);
ctx.push(glyph.stretch_vertical(ctx, height, Abs::zero()));
} else {
ctx.push(glyph);
} }
t.push_escaped(c); } else if self.0.chars().all(|c| c.is_ascii_digit()) {
if supportive { // A number that should respect math styling and can therefore
t.support(); // not fall back to the normal text layout.
let mut vec = vec![];
for c in self.0.chars() {
vec.push(GlyphFragment::new(ctx, c).into());
} }
} let frame = MathRow(vec).to_frame(ctx);
ctx.push(frame);
if multi { } else {
t.push_str("}"); // Anything else is handled by Typst's standard text layout.
TextNode(self.0.clone()).pack().layout_math(ctx)?;
} }
Ok(()) Ok(())

View File

@ -1,3 +1,4 @@
use unicode_segmentation::UnicodeSegmentation;
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
use unscanny::Scanner; use unscanny::Scanner;
@ -103,7 +104,7 @@ impl Lexer<'_> {
Some(c) => match self.mode { Some(c) => match self.mode {
LexMode::Markup => self.markup(start, c), LexMode::Markup => self.markup(start, c),
LexMode::Math => self.math(c), LexMode::Math => self.math(start, c),
LexMode::Code => self.code(start, c), LexMode::Code => self.code(start, c),
}, },
@ -404,7 +405,7 @@ impl Lexer<'_> {
/// Math. /// Math.
impl Lexer<'_> { impl Lexer<'_> {
fn math(&mut self, c: char) -> SyntaxKind { fn math(&mut self, start: usize, c: char) -> SyntaxKind {
match c { match c {
'\\' => self.backslash(), '\\' => self.backslash(),
':' if self.s.at(is_id_start) => self.maybe_symbol(), ':' if self.s.at(is_id_start) => self.maybe_symbol(),
@ -441,13 +442,7 @@ impl Lexer<'_> {
} }
// Other math atoms. // Other math atoms.
_ => { _ => self.atom(start, c),
// Keep numbers together.
if c.is_numeric() {
self.s.eat_while(char::is_numeric);
}
SyntaxKind::Atom
}
} }
} }
@ -469,6 +464,22 @@ impl Lexer<'_> {
SyntaxKind::Ident SyntaxKind::Ident
} }
fn atom(&mut self, start: usize, c: char) -> SyntaxKind {
// Keep numbers and grapheme clusters together.
if c.is_numeric() {
self.s.eat_while(char::is_numeric);
} else {
let len = self
.s
.get(start..self.s.string().len())
.graphemes(true)
.next()
.map_or(0, str::len);
self.s.jump(start + len);
}
SyntaxKind::Atom
}
} }
/// Code. /// Code.