Repository: typst/typst
File: crates/typst/src/math/attach.rs (Rust)

Code before:
    /// smart positioning.
    pub b: Option<Content>,

    /// The top-left attachment (before the base).
    pub tl: Option<Content>,

    /// The bottom-left attachment (before base).
    pub bl: Option<Content>,

    /// The top-right attachment (after the base).
    pub tr: Option<Content>,

    /// The bottom-right attachment (after the base).
    pub br: Option<Content>,
}

impl LayoutMath for Packed<AttachElem> {
    #[typst_macros::time(name = "math.attach", span = self.span())]
    fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
        let new_elem = merge_base(self);
        let elem = new_elem.as_ref().unwrap_or(self);

        // Get the size to stretch the base to, if the attach argument is true.
        let stretch = elem
            .base()
            .to_packed::<StretchElem>()
            .filter(|stretch| stretch.attach(styles))
            .map(|stretch| stretch.size(styles));

        let mut base = ctx.layout_into_fragment(elem.base(), styles)?;
        let sup_style = style_for_superscript(styles);
        let sup_style_chain = styles.chain(&sup_style);
        let tl = elem.tl(sup_style_chain);
        let tr = elem.tr(sup_style_chain);
        let primed = tr.as_ref().is_some_and(|content| content.is::<PrimesElem>());
        let t = elem.t(sup_style_chain);

        let sub_style = style_for_subscript(styles);
        let sub_style_chain = styles.chain(&sub_style);
        let bl = elem.bl(sub_style_chain);
        let br = elem.br(sub_style_chain);
        let b = elem.b(sub_style_chain);

        let limits = base.limits().active(styles);
        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) };


Review comment (suggestion):
This will not handle the case where `#let x = $stretch(..)$; $ x^"text" $`. maybe that's ok, but worth mentioning.

Code after:
    /// smart positioning.
    pub b: Option<Content>,

    /// The top-left attachment (before the base).
    pub tl: Option<Content>,

    /// The bottom-left attachment (before base).
    pub bl: Option<Content>,

    /// The top-right attachment (after the base).
    pub tr: Option<Content>,

    /// The bottom-right attachment (after the base).
    pub br: Option<Content>,
}

impl LayoutMath for Packed<AttachElem> {
    #[typst_macros::time(name = "math.attach", span = self.span())]
    fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
        let new_elem = merge_base(self);
        let elem = new_elem.as_ref().unwrap_or(self);
        let stretch = stretch_size(styles, elem);

        let mut base = ctx.layout_into_fragment(elem.base(), styles)?;
        let sup_style = style_for_superscript(styles);
        let sup_style_chain = styles.chain(&sup_style);
        let tl = elem.tl(sup_style_chain);
        let tr = elem.tr(sup_style_chain);
        let primed = tr.as_ref().is_some_and(|content| content.is::<PrimesElem>());
        let t = elem.t(sup_style_chain);

        let sub_style = style_for_subscript(styles);
        let sub_style_chain = styles.chain(&sub_style);
        let bl = elem.bl(sub_style_chain);
        let br = elem.br(sub_style_chain);
        let b = elem.b(sub_style_chain);

        let limits = base.limits().active(styles);
        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) };

        macro_rules! layout {
            ($content:ident, $style_chain:ident) => {
                $content
                    .map(|elem| ctx.layout_into_fragment(&elem, $style_chain))
                    .transpose()
            };