rbfmt 0.0.2

Ruby code formatter
Documentation
use crate::fmt;

impl<'src> super::Parser<'src> {
    pub(super) fn parse_case_match(&mut self, node: prism::CaseMatchNode) -> fmt::Node {
        let conditions = node.conditions();
        let consequent = node.consequent();
        let end_loc = node.end_keyword_loc();
        let first_branch_start = conditions
            .iter()
            .next()
            .map(|n| n.location().start_offset());

        let pred_next = first_branch_start
            .or(consequent.as_ref().map(|c| c.location().start_offset()))
            .unwrap_or(end_loc.start_offset());
        let predicate = node.predicate().map(|n| self.parse(n, Some(pred_next)));
        let case_trailing = if predicate.is_some() {
            fmt::TrailingTrivia::none()
        } else {
            self.take_trailing_comment(pred_next)
        };

        let first_branch_leading = match first_branch_start {
            Some(first_branch_start) => self.take_leading_trivia(first_branch_start),
            None => fmt::LeadingTrivia::new(),
        };

        let mut branches = vec![];
        let conditions_next = consequent
            .as_ref()
            .map(|c| c.location().start_offset())
            .unwrap_or(end_loc.start_offset());
        Self::each_node_with_trailing_end(
            conditions.iter(),
            Some(conditions_next),
            |node, trailing_end| {
                let condition = match node {
                    prism::Node::InNode { .. } => {
                        let node = node.as_in_node().unwrap();
                        self.parse_case_in(node, trailing_end)
                    }
                    _ => panic!("unexpected case expression branch: {:?}", node),
                };
                branches.push(condition);
            },
        );

        let otherwise = consequent.map(|node| self.parse_else(node, end_loc.start_offset()));

        let case_match = fmt::CaseMatch {
            case_trailing,
            predicate: predicate.map(Box::new),
            first_branch_leading,
            branches,
            otherwise,
        };
        fmt::Node::new(fmt::Kind::CaseMatch(case_match))
    }

    fn parse_case_in(&mut self, node: prism::InNode, body_end: Option<usize>) -> fmt::CaseIn {
        let loc = node.location();
        let was_flat = !self.does_line_break_exist_in(loc.start_offset(), loc.end_offset());

        let pattern_next = node
            .statements()
            .as_ref()
            .map(|n| n.location().start_offset());
        let pattern = self.parse(node.pattern(), pattern_next);

        let mut case_in = fmt::CaseIn::new(was_flat, pattern);
        let body = self.parse_statements_body(node.statements(), body_end);
        case_in.set_body(body);
        case_in
    }

    pub(super) fn parse_match_assign(
        &mut self,
        expression: prism::Node,
        operator_loc: prism::Location,
        pattern: prism::Node,
    ) -> fmt::Node {
        let expression = self.parse(expression, Some(operator_loc.start_offset()));
        let pattern = self.parse(pattern, None);
        let operator = Self::source_lossy_at(&operator_loc);
        let match_assign = fmt::MatchAssign::new(expression, operator, pattern);
        fmt::Node::new(fmt::Kind::MatchAssign(match_assign))
    }

    pub(super) fn parse_array_pattern(&mut self, node: prism::ArrayPatternNode) -> fmt::Node {
        let constant = node.constant().map(|c| self.parse(c, None));
        let mut opening = node.opening_loc().as_ref().map(Self::source_lossy_at);
        let mut closing = node.closing_loc().as_ref().map(Self::source_lossy_at);
        let rest = node.rest();
        let posts = node.posts();

        if closing.is_none() && posts.iter().next().is_none() {
            if let Some(rest) = rest.as_ref() {
                let should_close = match rest {
                    prism::Node::ImplicitRestNode { .. } => true,
                    prism::Node::SplatNode { .. } => {
                        rest.as_splat_node().unwrap().expression().is_none()
                    }
                    _ => false,
                };
                if should_close {
                    opening = Some("[".to_string());
                    closing = Some("]".to_string());
                }
            }
        }
        let mut array = fmt::ArrayPattern::new(constant, opening, closing);

        let posts_head = posts.iter().next();

        let closing_start = node.closing_loc().as_ref().map(|c| c.start_offset());
        let requireds_next = rest
            .as_ref()
            .map(|r| r.location().start_offset())
            .or_else(|| posts_head.as_ref().map(|p| p.location().start_offset()))
            .or(closing_start);
        Self::each_node_with_trailing_end(
            node.requireds().iter(),
            requireds_next,
            |node, trailing_end| {
                let element = self.parse(node, trailing_end);
                array.append_element(element);
            },
        );

        if let Some(rest) = node.rest() {
            let rest_next = posts_head
                .as_ref()
                .map(|p| p.location().start_offset())
                .or(closing_start);
            let element = self.parse(rest, rest_next);
            array.append_element(element);
            array.last_comma_allowed = false;
        }

        if posts_head.is_some() {
            Self::each_node_with_trailing_end(
                node.posts().iter(),
                closing_start,
                |node, trailing_end| {
                    let element = self.parse(node, trailing_end);
                    array.append_element(element);
                },
            );
            array.last_comma_allowed = false;
        }

        let end = self.take_end_trivia_as_virtual_end(closing_start);
        array.set_virtual_end(end);

        fmt::Node::new(fmt::Kind::ArrayPattern(array))
    }

    pub(super) fn parse_find_pattern(&mut self, node: prism::FindPatternNode) -> fmt::Node {
        let constant = node.constant().map(|c| self.parse(c, None));
        let opening = node.opening_loc().as_ref().map(Self::source_lossy_at);
        let closing = node.closing_loc().as_ref().map(Self::source_lossy_at);
        let mut array = fmt::ArrayPattern::new(constant, opening, closing);
        array.last_comma_allowed = false;

        let requireds = node.requireds();
        let right = node.right();

        let left_next = requireds
            .iter()
            .next()
            .map(|n| n.location().start_offset())
            .unwrap_or(right.location().start_offset());
        let left = self.parse(node.left(), Some(left_next));
        array.append_element(left);

        Self::each_node_with_trailing_end(
            node.requireds().iter(),
            Some(right.location().start_offset()),
            |node, trailing_end| {
                let element = self.parse(node, trailing_end);
                array.append_element(element);
            },
        );

        let closing_start = node.closing_loc().as_ref().map(|l| l.start_offset());

        let right = self.parse(right, closing_start);
        array.append_element(right);

        let end = self.take_end_trivia_as_virtual_end(closing_start);
        array.set_virtual_end(end);

        fmt::Node::new(fmt::Kind::ArrayPattern(array))
    }

    pub(super) fn parse_hash_pattern(&mut self, node: prism::HashPatternNode) -> fmt::Node {
        let constant = node.constant().map(|c| self.parse(c, None));
        let opening_loc = node.opening_loc();
        let closing_loc = node.closing_loc();
        let opening = opening_loc.as_ref().map(Self::source_lossy_at);
        let closing = closing_loc.as_ref().map(Self::source_lossy_at);
        let should_be_inline = match (opening_loc.as_ref(), node.elements().iter().next()) {
            (Some(opening_loc), Some(first_element)) => !self.does_line_break_exist_in(
                opening_loc.start_offset(),
                first_element.location().start_offset(),
            ),
            _ => true,
        };
        let mut hash = fmt::HashPattern::new(constant, opening, closing, should_be_inline);

        let rest = node.rest();
        let closing_start = closing_loc.as_ref().map(|c| c.start_offset());

        let elements_next = rest
            .as_ref()
            .map(|r| r.location().start_offset())
            .or(closing_start);
        Self::each_node_with_trailing_end(
            node.elements().iter(),
            elements_next,
            |node, trailing_end| {
                let element = self.parse(node, trailing_end);
                hash.append_element(element);
            },
        );

        if let Some(rest) = node.rest() {
            let rest = self.parse(rest, closing_start);
            hash.append_element(rest);
            hash.last_comma_allowed = false;
        }

        let end = self.take_end_trivia_as_virtual_end(closing_start);
        hash.set_virtual_end(end);

        fmt::Node::new(fmt::Kind::HashPattern(hash))
    }

    pub(super) fn parse_pinned_expression(
        &mut self,
        node: prism::PinnedExpressionNode,
    ) -> fmt::Node {
        let operator = Self::source_lossy_at(&node.operator_loc());
        let rparen_start = node.rparen_loc().start_offset();
        let expression = self.parse(node.expression(), Some(rparen_start));

        let mut stmts = fmt::Statements::new();
        stmts.append_node(expression);
        stmts.set_virtual_end(self.take_end_trivia_as_virtual_end(Some(rparen_start)));
        let mut parens = fmt::Parens::new(stmts);
        parens.closing_break_allowed = false;

        let node = fmt::Node::new(fmt::Kind::Parens(parens));
        let prefix = fmt::Prefix::new(operator, Some(node));
        fmt::Node::new(fmt::Kind::Prefix(prefix))
    }

    pub(super) fn parse_pinned_variable(&mut self, node: prism::PinnedVariableNode) -> fmt::Node {
        let operator = Self::source_lossy_at(&node.operator_loc());
        let variable = self.parse(node.variable(), None);
        let prefix = fmt::Prefix::new(operator, Some(variable));
        fmt::Node::new(fmt::Kind::Prefix(prefix))
    }

    pub(super) fn parse_capture_pattern(&mut self, node: prism::CapturePatternNode) -> fmt::Node {
        let value = self.parse(node.value(), Some(node.operator_loc().start_offset()));
        let operator = Self::source_lossy_at(&node.operator_loc());
        let target = self.parse(node.target(), None);
        let assoc = fmt::Assoc::new(value, Some(operator), target);
        fmt::Node::new(fmt::Kind::Assoc(assoc))
    }

    pub(super) fn parse_alternation_pattern(
        &mut self,
        node: prism::AlternationPatternNode,
    ) -> fmt::Node {
        let operator_loc = node.operator_loc();
        let left = self.parse(node.left(), Some(operator_loc.start_offset()));
        let mut chain = match left.kind {
            fmt::Kind::AltPatternChain(chain) => chain,
            _ => fmt::AltPatternChain::new(left),
        };
        let right = node.right();
        let right = self.parse(right, None);
        chain.append_right(right);
        fmt::Node::new(fmt::Kind::AltPatternChain(chain))
    }
}