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
/// 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{");
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);
}
} 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() {
let supportive = c == '|';
if supportive {
t.support();
vec.push(GlyphFragment::new(ctx, c).into());
}
t.push_escaped(c);
if supportive {
t.support();
}
}
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(())

View File

@ -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.