malva 0.9.0

Configurable, smart and fast CSS, SCSS, Sass and Less formatter.
Documentation
use super::{super::DocGen, helpers};
use crate::{ctx::Ctx, state::State};
use raffia::{ast::*, Spanned};
use tiny_pretty::Doc;

impl<'s> DocGen<'s> for MediaAnd<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        use crate::config::OperatorLineBreak;

        let mut docs = match ctx.options.operator_linebreak {
            OperatorLineBreak::Before => vec![Doc::line_or_space(), Doc::text("and"), Doc::space()],
            OperatorLineBreak::After => vec![Doc::space(), Doc::text("and"), Doc::line_or_space()],
        };
        docs.extend(ctx.end_spaced_comments(
            ctx.get_comments_between(self.keyword.span.end, self.media_in_parens.span.start),
        ));
        docs.push(self.media_in_parens.doc(ctx, state));

        Doc::list(docs).group().nest(ctx.indent_width)
    }
}

impl<'s> DocGen<'s> for MediaCondition<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        Doc::list(
            self.conditions
                .iter()
                .fold(
                    (Vec::with_capacity(self.conditions.len()), self.span.start),
                    |(mut docs, pos), condition| {
                        let span = condition.span();
                        docs.extend(
                            ctx.start_spaced_comments(ctx.get_comments_between(pos, span.start)),
                        );
                        docs.push(condition.doc(ctx, state));
                        (docs, span.end)
                    },
                )
                .0,
        )
    }
}

impl<'s> DocGen<'s> for MediaConditionAfterMediaType<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        use crate::config::OperatorLineBreak;

        let mut docs = match ctx.options.operator_linebreak {
            OperatorLineBreak::Before => vec![Doc::text("and"), Doc::space()],
            OperatorLineBreak::After => vec![Doc::text("and"), Doc::line_or_space()],
        };
        docs.extend(ctx.end_spaced_comments(
            ctx.get_comments_between(self.and.span.end, self.condition.span.start),
        ));
        docs.push(self.condition.doc(ctx, state));

        Doc::list(docs).group().nest(ctx.indent_width)
    }
}

impl<'s> DocGen<'s> for MediaConditionKind<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        match self {
            MediaConditionKind::MediaInParens(media_in_parens) => media_in_parens.doc(ctx, state),
            MediaConditionKind::And(and) => and.doc(ctx, state),
            MediaConditionKind::Or(or) => or.doc(ctx, state),
            MediaConditionKind::Not(not) => not.doc(ctx, state),
        }
    }
}

impl<'s> DocGen<'s> for MediaFeature<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        match self {
            MediaFeature::Plain(plain) => plain.doc(ctx, state),
            MediaFeature::Boolean(boolean) => boolean.doc(ctx, state),
            MediaFeature::Range(range) => range.doc(ctx, state),
            MediaFeature::RangeInterval(range_interval) => range_interval.doc(ctx, state),
        }
    }
}

impl<'s> DocGen<'s> for MediaFeatureBoolean<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        self.name.doc(ctx, state)
    }
}

impl<'s> DocGen<'s> for MediaFeatureComparison {
    fn doc(&self, _: &Ctx<'_, 's>, _: &State) -> Doc<'s> {
        Doc::text(match self.kind {
            MediaFeatureComparisonKind::Equal => "=",
            MediaFeatureComparisonKind::GreaterThan => ">",
            MediaFeatureComparisonKind::GreaterThanOrEqual => ">=",
            MediaFeatureComparisonKind::LessThan => "<",
            MediaFeatureComparisonKind::LessThanOrEqual => "<=",
        })
    }
}

impl<'s> DocGen<'s> for MediaFeatureName<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        match self {
            MediaFeatureName::Ident(InterpolableIdent::Literal(ident))
                if !ident.name.starts_with("--") =>
            {
                Doc::text(ident.raw.to_ascii_lowercase())
            }
            MediaFeatureName::Ident(ident) => ident.doc(ctx, state),
            MediaFeatureName::SassVariable(sass_variable) => sass_variable.doc(ctx, state),
        }
    }
}

impl<'s> DocGen<'s> for MediaFeaturePlain<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        self.name
            .doc(ctx, state)
            .concat(ctx.start_spaced_comments(
                ctx.get_comments_between(self.name.span().end, self.colon_span.start),
            ))
            .append(Doc::text(": "))
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.colon_span.start, self.value.span().start),
            ))
            .append(self.value.doc(ctx, state))
    }
}

impl<'s> DocGen<'s> for MediaFeatureRange<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        self.left
            .doc(ctx, state)
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.left.span().end, self.comparison.span.start),
            ))
            .append(self.comparison.doc(ctx, state))
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.comparison.span.end, self.right.span().start),
            ))
            .append(self.right.doc(ctx, state))
    }
}

impl<'s> DocGen<'s> for MediaFeatureRangeInterval<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        let name_span = self.name.span();
        self.left
            .doc(ctx, state)
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.left.span().end, self.left_comparison.span.start),
            ))
            .append(self.left_comparison.doc(ctx, state))
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.left_comparison.span.end, name_span.start),
            ))
            .append(self.name.doc(ctx, state))
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(name_span.end, self.right_comparison.span.start),
            ))
            .append(self.right_comparison.doc(ctx, state))
            .append(Doc::space())
            .concat(ctx.end_spaced_comments(
                ctx.get_comments_between(self.right_comparison.span.end, self.right.span().start),
            ))
            .append(self.right.doc(ctx, state))
    }
}

impl<'s> DocGen<'s> for MediaInParens<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        let kind = match &self.kind {
            MediaInParensKind::MediaCondition(media_condition) => media_condition.doc(ctx, state),
            MediaInParensKind::MediaFeature(media_feature) => media_feature.doc(ctx, state),
        };
        let kind_span = self.kind.span();

        Doc::text("(")
            .concat(
                ctx.end_spaced_comments(ctx.get_comments_between(self.span.start, kind_span.start)),
            )
            .append(kind)
            .concat(
                ctx.start_spaced_comments(ctx.get_comments_between(kind_span.end, self.span.end)),
            )
            .append(Doc::text(")"))
    }
}

impl<'s> DocGen<'s> for MediaNot<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        use crate::config::OperatorLineBreak;

        let mut docs = match ctx.options.operator_linebreak {
            OperatorLineBreak::Before => vec![Doc::line_or_nil(), Doc::text("not"), Doc::space()],
            OperatorLineBreak::After => vec![Doc::text("not"), Doc::line_or_space()],
        };
        docs.extend(ctx.end_spaced_comments(
            ctx.get_comments_between(self.keyword.span.end, self.media_in_parens.span.start),
        ));
        docs.push(self.media_in_parens.doc(ctx, state));

        Doc::list(docs).group().nest(ctx.indent_width)
    }
}

impl<'s> DocGen<'s> for MediaOr<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        use crate::config::OperatorLineBreak;

        let mut docs = match ctx.options.operator_linebreak {
            OperatorLineBreak::Before => vec![Doc::line_or_space(), Doc::text("or"), Doc::space()],
            OperatorLineBreak::After => vec![Doc::space(), Doc::text("or"), Doc::line_or_space()],
        };
        docs.extend(ctx.end_spaced_comments(
            ctx.get_comments_between(self.keyword.span.end, self.media_in_parens.span.start),
        ));
        docs.push(self.media_in_parens.doc(ctx, state));

        Doc::list(docs).group().nest(ctx.indent_width)
    }
}

impl<'s> DocGen<'s> for MediaQuery<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        match self {
            MediaQuery::ConditionOnly(media_condition) => media_condition.doc(ctx, state),
            MediaQuery::WithType(media_query_with_type) => media_query_with_type.doc(ctx, state),
            MediaQuery::Function(function) => function.doc(ctx, state),
            MediaQuery::LessVariable(less_variable) => less_variable.doc(ctx, state),
            MediaQuery::LessNamespaceValue(less_namespace_value) => {
                less_namespace_value.doc(ctx, state)
            }
        }
    }
}

impl<'s> DocGen<'s> for MediaQueryWithType<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        let media_type_span = self.media_type.span();

        let mut docs = Vec::with_capacity(5);
        if let Some(modifier) = &self.modifier {
            docs.push(modifier.doc(ctx, state));
            docs.push(Doc::space());
            docs.extend(ctx.end_spaced_comments(
                ctx.get_comments_between(modifier.span.end, media_type_span.start),
            ));
        }
        docs.push(self.media_type.doc(ctx, state));
        if let Some(condition) = &self.condition {
            docs.push(Doc::space());
            docs.extend(ctx.end_spaced_comments(
                ctx.get_comments_between(media_type_span.end, condition.span.start),
            ));
            docs.push(condition.doc(ctx, state));
        }
        Doc::list(docs)
    }
}

impl<'s> DocGen<'s> for MediaQueryList<'s> {
    fn doc(&self, ctx: &Ctx<'_, 's>, state: &State) -> Doc<'s> {
        helpers::SeparatedListFormatter::new(",", Doc::line_or_space())
            .format(
                &self.queries,
                &self.comma_spans,
                self.span.start,
                ctx,
                state,
            )
            .group()
            .nest(ctx.indent_width)
    }
}