From ea378e89b4f2267bb85ec56c905111a6c73d4721 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 22 Jan 2023 13:27:49 +0100 Subject: [PATCH] Better math atoms --- library/src/math/atom.rs | 47 ++++++++++++++++++++++++---------------- src/syntax/lexer.rs | 29 +++++++++++++++++-------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/library/src/math/atom.rs b/library/src/math/atom.rs index 142461474..5b35d289a 100644 --- a/library/src/math/atom.rs +++ b/library/src/math/atom.rs @@ -10,7 +10,7 @@ use super::*; /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct AtomNode(pub EcoString); @@ -21,26 +21,35 @@ impl AtomNode { } } -impl Texify for AtomNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - let multi = self.0.graphemes(true).count() > 1; - if multi { - t.push_str("\\mathrm{"); - } - - for c in self.0.chars() { - let supportive = c == '|'; - if supportive { - t.support(); +impl LayoutMath for AtomNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + let mut chars = self.0.chars(); + if let Some(glyph) = chars + .next() + .filter(|_| chars.next().is_none()) + .and_then(|c| GlyphFragment::try_new(ctx, c)) + { + // A single letter that is available in the math font. + if ctx.style.size == MathSize::Display + && 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); - if supportive { - t.support(); + } else if self.0.chars().all(|c| c.is_ascii_digit()) { + // A number that should respect math styling and can therefore + // 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()); } - } - - if multi { - t.push_str("}"); + let frame = MathRow(vec).to_frame(ctx); + ctx.push(frame); + } else { + // Anything else is handled by Typst's standard text layout. + TextNode(self.0.clone()).pack().layout_math(ctx)?; } Ok(()) diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index e3c291509..433d0defc 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -1,3 +1,4 @@ +use unicode_segmentation::UnicodeSegmentation; use unicode_xid::UnicodeXID; use unscanny::Scanner; @@ -103,7 +104,7 @@ impl Lexer<'_> { Some(c) => match self.mode { LexMode::Markup => self.markup(start, c), - LexMode::Math => self.math(c), + LexMode::Math => self.math(start, c), LexMode::Code => self.code(start, c), }, @@ -404,7 +405,7 @@ impl Lexer<'_> { /// Math. impl Lexer<'_> { - fn math(&mut self, c: char) -> SyntaxKind { + fn math(&mut self, start: usize, c: char) -> SyntaxKind { match c { '\\' => self.backslash(), ':' if self.s.at(is_id_start) => self.maybe_symbol(), @@ -441,13 +442,7 @@ impl Lexer<'_> { } // Other math atoms. - _ => { - // Keep numbers together. - if c.is_numeric() { - self.s.eat_while(char::is_numeric); - } - SyntaxKind::Atom - } + _ => self.atom(start, c), } } @@ -469,6 +464,22 @@ impl Lexer<'_> { 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.