summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--library/src/math/atom.rs47
-rw-r--r--src/syntax/lexer.rs29
2 files changed, 48 insertions, 28 deletions
diff --git a/library/src/math/atom.rs b/library/src/math/atom.rs
index 14246147..5b35d289 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 e3c29150..433d0def 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.