fatou 0.5.0

A language server, formatter, and linter for Julia
//! A Wadler/Prettier-style document IR. Rules build a tree of these primitives;
//! the [`printer`](crate::formatter::printer) makes all line-break decisions by
//! choosing flat or broken layout per [`Group`](Ir::Group).

use std::rc::Rc;

#[derive(Debug, Clone)]
pub enum Ir {
    /// Literal text. Must not contain newlines (use [`Ir::HardLine`]).
    Text(Rc<str>),
    /// A sequence of documents laid out one after another.
    Concat(Rc<[Ir]>),
    /// A space when its group is flat, a newline (+ indent) when broken.
    Line,
    /// Nothing when its group is flat, a newline (+ indent) when broken.
    SoftLine,
    /// Always a newline (+ indent); forces every enclosing group to break.
    HardLine,
    /// A bare newline at column zero (no indent); forces every enclosing group
    /// to break. Used to emit a *blank* line between elements of an already-broken
    /// layout, where a [`HardLine`](Ir::HardLine) would leave the indent as trailing
    /// whitespace on the otherwise-empty line.
    BlankLine,
    /// Increase the indent of the contained document by one step.
    Indent(Rc<Ir>),
    /// A group laid out flat if it fits the line width, otherwise broken.
    Group(Rc<Ir>),
    /// Conditional text: the first string when the enclosing group is broken,
    /// the second when it is flat. The canonical use is a trailing separator that
    /// only appears in the broken layout, e.g. `IfBreak(",", "")`. Measured as its
    /// *flat* string when deciding whether a group fits.
    IfBreak(Rc<str>, Rc<str>),
    /// A trailing-argument hug with an explode fallback. Renders as
    /// `prefix body close` — the hug layout: `prefix` (the open bracket plus the
    /// leading arguments, flat) rides the current line and `body` (the hugged
    /// last argument) breaks in place via its own group — when the hug layout's
    /// *first line* fits: `prefix` measured strictly flat, then `body` up to its
    /// first break opportunity. Otherwise renders `explode`, the standard
    /// width-driven one-item-per-line group over all items, which is guaranteed
    /// to break whenever the hug is rejected (the hug measure never exceeds the
    /// flat measure).
    HugGroup {
        prefix: Rc<Ir>,
        body: Rc<Ir>,
        close: Rc<Ir>,
        explode: Rc<Ir>,
    },
}

impl Ir {
    pub fn text(s: impl Into<Rc<str>>) -> Ir {
        Ir::Text(s.into())
    }

    pub fn concat(items: impl IntoIterator<Item = Ir>) -> Ir {
        Ir::Concat(items.into_iter().collect())
    }

    pub fn group(inner: Ir) -> Ir {
        Ir::Group(Rc::new(inner))
    }

    pub fn indent(inner: Ir) -> Ir {
        Ir::Indent(Rc::new(inner))
    }

    pub fn if_break(broken: impl Into<Rc<str>>, flat: impl Into<Rc<str>>) -> Ir {
        Ir::IfBreak(broken.into(), flat.into())
    }

    pub fn hug_group(prefix: Ir, body: Ir, close: Ir, explode: Ir) -> Ir {
        Ir::HugGroup {
            prefix: Rc::new(prefix),
            body: Rc::new(body),
            close: Rc::new(close),
            explode: Rc::new(explode),
        }
    }
}