Repository: typst/typst
File: crates/typst-syntax/src/ast.rs (Rust)

Code before:

    /// Whether the equation should be displayed as a separate block.
    pub fn block(self) -> bool {
        // The `.unwrap()` calls in this function won't panic because there will
        // always be an extra dollar-sign node on the opposing side.
        let mut front_iter = self.0.children().enumerate().skip(1);
        let (mut front_idx, mut front) = front_iter.next().unwrap();
        // The parser likes to group adjacent trivia, so if the equation's body
        // is an empty Math node, e.g. in `$ /**/ $`, the CST will look like:
        // `Eqn [ Math(<empty>) Space(" ") Trivia("/**/") Space(" ") ]`.
        // Yet we still want to treat this as a block, so we check the following
        // node by skipping an empty math node at the start.
        if front.is_empty() && front.kind() == SyntaxKind::Math {
            (front_idx, front) = front_iter.next().unwrap();
        }
        let (back_idx, back) = self.0.children().enumerate().nth_back(1).unwrap();

        front.kind() == SyntaxKind::Space
            && back.kind() == SyntaxKind::Space
            // If the front and back spaces are the same one, require that it
            // has multiple characters or, if one char, that it is a newline.
            && (front_idx != back_idx || {
                match front.text().char_indices().next_back() {
                    Some((0, c)) => is_newline(c), // A single char must be a newline.
                    _ => true
                }
            })
    }
}

node! {
    /// The contents of a mathematical equation: `x^2 + 1`.
    struct Math
}

impl<'a> Math<'a> {
    /// The expressions the mathematical content consists of.
    pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
        self.0.children().filter_map(Expr::cast_with_space)
    }

    /// Whether this `Math` node was originally parenthesized.
    pub fn was_deparenthesized(self) -> bool {
        let mut iter = self.0.children();
        matches!(iter.next().map(SyntaxNode::kind), Some(SyntaxKind::LeftParen))
            && matches!(
                iter.next_back().map(SyntaxNode::kind),
                Some(SyntaxKind::RightParen)
            )
    }
}

Review comment (suggestion):
could we do this instead?
`front.text().parse::<char>().map_or(true, is_newline)`

Code after:

    /// Whether the equation should be displayed as a separate block.
    pub fn block(self) -> bool {
        // The parser likes to group adjacent trivia, so if the equation's body
        // is an empty Math node, e.g. in `$ /**/ $`, the CST will look like:
        // `Eqn [ Math(<empty>) Space(" ") Trivia("/**/") Space(" ") ]`.
        // Yet we still want to treat this as a block, so we check the following
        // node by skipping an empty math node at the start.
        let mut front_iter = self.0.children().enumerate().skip(1);
        let mut front_pair = front_iter.next();
        if let Some((_, front)) = front_pair
            && front.is_empty()
            && front.kind() == SyntaxKind::Math
        {
            front_pair = front_iter.next();
        }

        if let Some((front_idx, front)) = front_pair
            && let Some((back_idx, back)) = self.0.children().enumerate().nth_back(1)
        {
            front.kind() == SyntaxKind::Space
                && back.kind() == SyntaxKind::Space
                // If the front and back spaces are the same one, require that
                // it has multiple chars or, if one char, that it is a newline.
                // `parse::<char>` is `Ok` if there was only a single char.
                && (front_idx != back_idx
                    || front.text().parse::<char>().map_or(true, is_newline))
        } else {
            false
        }
    }
}

node! {
    /// The contents of a mathematical equation: `x^2 + 1`.
    struct Math
}

impl<'a> Math<'a> {
    /// The expressions the mathematical content consists of.
    pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
        self.0.children().filter_map(Expr::cast_with_space)
    }

    /// Whether this `Math` node was originally parenthesized.
    pub fn was_deparenthesized(self) -> bool {
        let mut iter = self.0.children();
        matches!(iter.next().map(SyntaxNode::kind), Some(SyntaxKind::LeftParen))
            && matches!(
                iter.next_back().map(SyntaxNode::kind),
                Some(SyntaxKind::RightParen)