summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst-syntax/src/parser.rs7
-rw-r--r--crates/typst/src/eval/math.rs6
-rw-r--r--crates/typst/src/math/attach.rs50
-rw-r--r--tests/ref/math-primes-complex.pngbin1274 -> 1652 bytes
-rw-r--r--tests/ref/math-primes-with-superscript.pngbin0 -> 956 bytes
-rw-r--r--tests/suite/math/primes.typ15
6 files changed, 53 insertions, 25 deletions
diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs
index 7e7eeea5..ff01bacf 100644
--- a/crates/typst-syntax/src/parser.rs
+++ b/crates/typst-syntax/src/parser.rs
@@ -393,11 +393,6 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
continue;
}
- // Separate primes and superscripts to different attachments.
- if primed && p.current() == SyntaxKind::Hat {
- p.wrap(m, SyntaxKind::MathAttach);
- }
-
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
// No attachments, so we need to wrap primes as attachment.
if primed {
@@ -429,7 +424,7 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
math_expr_prec(p, prec, stop);
math_unparen(p, m2);
- if p.eat_if(SyntaxKind::Underscore) || (!primed && p.eat_if(SyntaxKind::Hat)) {
+ if p.eat_if(SyntaxKind::Underscore) || p.eat_if(SyntaxKind::Hat) {
let m3 = p.marker();
math_expr_prec(p, prec, SyntaxKind::End);
math_unparen(p, m3);
diff --git a/crates/typst/src/eval/math.rs b/crates/typst/src/eval/math.rs
index 0e5e0eef..548c935d 100644
--- a/crates/typst/src/eval/math.rs
+++ b/crates/typst/src/eval/math.rs
@@ -54,7 +54,11 @@ impl Eval for ast::MathAttach<'_> {
if let Some(expr) = self.top() {
elem.push_t(Some(expr.eval_display(vm)?));
- } else if let Some(primes) = self.primes() {
+ }
+
+ // Always attach primes in scripts style (not limits style),
+ // i.e. at the top-right corner.
+ if let Some(primes) = self.primes() {
elem.push_tr(Some(primes.eval(vm)?));
}
diff --git a/crates/typst/src/math/attach.rs b/crates/typst/src/math/attach.rs
index 7cc03bba..8d85f8c4 100644
--- a/crates/typst/src/math/attach.rs
+++ b/crates/typst/src/math/attach.rs
@@ -52,31 +52,47 @@ pub struct AttachElem {
impl LayoutMath for Packed<AttachElem> {
#[typst_macros::time(name = "math.attach", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
- type GetAttachment = fn(&AttachElem, styles: StyleChain) -> Option<Content>;
-
- let layout_attachment =
- |ctx: &mut MathContext, styles: StyleChain, getter: GetAttachment| {
- getter(self, styles)
- .map(|elem| ctx.layout_into_fragment(&elem, styles))
- .transpose()
- };
-
let base = ctx.layout_into_fragment(self.base(), styles)?;
let sup_style = style_for_superscript(styles);
- let tl = layout_attachment(ctx, styles.chain(&sup_style), AttachElem::tl)?;
- let tr = layout_attachment(ctx, styles.chain(&sup_style), AttachElem::tr)?;
- let t = layout_attachment(ctx, styles.chain(&sup_style), AttachElem::t)?;
+ let sup_style_chain = styles.chain(&sup_style);
+ let tl = self.tl(sup_style_chain);
+ let tr = self.tr(sup_style_chain);
+ let primed = tr.as_ref().is_some_and(|content| content.is::<PrimesElem>());
+ let t = self.t(sup_style_chain);
let sub_style = style_for_subscript(styles);
- let bl = layout_attachment(ctx, styles.chain(&sub_style), AttachElem::bl)?;
- let br = layout_attachment(ctx, styles.chain(&sub_style), AttachElem::br)?;
- let b = layout_attachment(ctx, styles.chain(&sub_style), AttachElem::b)?;
+ let sub_style_chain = styles.chain(&sub_style);
+ let bl = self.bl(sub_style_chain);
+ let br = self.br(sub_style_chain);
+ let b = self.b(sub_style_chain);
let limits = base.limits().active(styles);
- let (t, tr) = if limits || tr.is_some() { (t, tr) } else { (None, t) };
+ let (t, tr) = match (t, tr) {
+ (Some(t), Some(tr)) if primed && !limits => (None, Some(tr + t)),
+ (Some(t), None) if !limits => (None, Some(t)),
+ (t, tr) => (t, tr),
+ };
let (b, br) = if limits || br.is_some() { (b, br) } else { (None, b) };
- layout_attachments(ctx, styles, base, [tl, t, tr, bl, b, br])
+
+ macro_rules! layout {
+ ($content:ident, $style_chain:ident) => {
+ $content
+ .map(|elem| ctx.layout_into_fragment(&elem, $style_chain))
+ .transpose()
+ };
+ }
+
+ let fragments = [
+ layout!(tl, sup_style_chain)?,
+ layout!(t, sup_style_chain)?,
+ layout!(tr, sup_style_chain)?,
+ layout!(bl, sub_style_chain)?,
+ layout!(b, sub_style_chain)?,
+ layout!(br, sub_style_chain)?,
+ ];
+
+ layout_attachments(ctx, styles, base, fragments)
}
}
diff --git a/tests/ref/math-primes-complex.png b/tests/ref/math-primes-complex.png
index 5f5558eb..0e85d08d 100644
--- a/tests/ref/math-primes-complex.png
+++ b/tests/ref/math-primes-complex.png
Binary files differ
diff --git a/tests/ref/math-primes-with-superscript.png b/tests/ref/math-primes-with-superscript.png
new file mode 100644
index 00000000..88a892b9
--- /dev/null
+++ b/tests/ref/math-primes-with-superscript.png
Binary files differ
diff --git a/tests/suite/math/primes.typ b/tests/suite/math/primes.typ
index e10f8876..78bb915a 100644
--- a/tests/suite/math/primes.typ
+++ b/tests/suite/math/primes.typ
@@ -12,7 +12,11 @@ $a'_b^c$, $a_b'^c$, $a_b^c'$, $a_b'^c'^d'$
$(a'_b')^(c'_d')$, $a'/b'$, $a_b'/c_d'$
-$∫'$, $∑'$, $ ∑'_S' $
+$∫'$, $∑'$, $a'^2^2$, $a'_2_2$
+
+$f_n'^a'$, $f^a'_n'$
+
+$ ∑'_S' $
--- math-primes-attach ---
// Test attaching primes only
@@ -48,3 +52,12 @@ $
#g''''''''''''''''' \
gg'
$
+
+--- math-primes-with-superscript ---
+// Test prime symbols don't raise the superscript position
+$
+ sqrt(f)/f
+ sqrt(f^2)/f^2
+ sqrt(f'^2)/f'^2
+ sqrt(f''_n^2)/f''^2_n
+$