use crate::predoc::{Doc, DocE, GroupAnn, Pretty, hardspace, line};
use crate::types::{Ann, Binder, Expression, Item, Parameter, Term, Token};
use super::app::push_pretty_app;
use super::op::push_pretty_operation;
use super::term::{push_pretty_term, push_pretty_term_wide};
use super::util::{
Width, has_trivia, is_lone_ann, items_has_only_comments, split_paren_trivia,
term_first_token_has_pre_trivia,
};
pub(super) fn is_absorbable_term(term: &Term) -> bool {
match term {
Term::IndentedString(s) if s.value.len() >= 2 => true,
Term::Set { items, .. } if !items.0.is_empty() => true,
Term::List { items, .. } if !items.0.is_empty() => true,
Term::Set {
open, items, close, ..
} if items.0.is_empty() && open.span.start_line() != close.span.start_line() => true,
Term::List { open, items, close }
if items.0.is_empty() && open.span.start_line() != close.span.start_line() =>
{
true
}
Term::List { open, items, .. } if has_trivia(open) || items_has_only_comments(items) => {
true
}
Term::Set { open, items, .. } if has_trivia(open) || items_has_only_comments(items) => true,
Term::Parenthesized { open, expr, .. } if is_lone_ann(open) => {
matches!(&**expr, Expression::Term(t) if is_absorbable_term(t))
}
_ => false,
}
}
pub(super) fn is_absorbable_expr(expr: &Expression) -> bool {
match expr {
Expression::Term(t) => is_absorbable_term(t),
Expression::With { body, .. } => {
matches!(&**body, Expression::Term(t) if is_absorbable_term(t))
}
Expression::Abstraction {
param: Parameter::Id(_),
body,
..
} => match &**body {
Expression::Term(t) => is_absorbable_term(t),
Expression::Abstraction { .. } => is_absorbable_expr(body),
_ => false,
},
_ => false,
}
}
pub(super) fn push_absorb_expr(doc: &mut Doc, width: Width, expr: &Expression) {
match expr {
Expression::Term(t) if is_absorbable_term(t) => match width {
Width::Wide => push_pretty_term_wide(doc, t),
Width::Regular => push_pretty_term(doc, t),
},
Expression::With {
kw_with: with_kw,
scope: env,
semi: semicolon,
body,
} if matches!(&**body, Expression::Term(t) if is_absorbable_term(t)) => {
let Expression::Term(t) = &**body else {
unreachable!()
};
doc.group_ann(GroupAnn::RegularG, |g| {
g.line_prime();
with_kw.pretty(g);
g.hardspace();
g.nested(|n| {
n.group(|gg| env.pretty(gg));
});
semicolon.pretty(g);
g.hardspace();
g.group_ann(GroupAnn::Priority, |pg| push_pretty_term_wide(pg, t));
});
}
_ => expr.pretty(doc),
}
}
fn push_nested_rhs(doc: &mut Doc, lead: DocE, f: impl FnOnce(&mut Doc)) {
doc.nested(|d| {
d.push_raw(lead);
d.group(f);
});
}
pub(super) fn push_absorb_rhs(doc: &mut Doc, expr: &Expression) {
match expr {
Expression::Term(Term::Set { items: binders, .. })
if matches!(binders.0.as_slice(), [Item::Item(Binder::Inherit { .. })]) =>
{
push_nested_rhs(doc, hardspace(), |inner| {
push_absorb_expr(inner, Width::Regular, expr);
});
}
_ if is_absorbable_expr(expr) => {
push_nested_rhs(doc, hardspace(), |inner| {
push_absorb_expr(inner, Width::Wide, expr);
});
}
Expression::Term(Term::Parenthesized {
open,
expr: inner,
close,
}) => {
doc.nested(|d| {
d.hardspace();
push_absorb_paren(d, open, inner, close);
});
}
Expression::Term(Term::SimpleString(_) | Term::IndentedString(_) | Term::Path(_)) => {
push_nested_rhs(doc, hardspace(), |inner| expr.pretty(inner));
}
Expression::Term(_) => {
doc.nested(|d| {
d.group(|inner| {
inner.line();
expr.pretty(inner);
});
});
}
Expression::Application { .. } => {
doc.nested(|d| push_pretty_app(d, false, &[line()], false, expr));
}
Expression::With { .. } => {
doc.nested(|d| {
d.group(|inner| {
inner.line();
expr.pretty(inner);
});
});
}
Expression::Operation { lhs: left, op, .. }
if op.value.is_update_concat_plus()
&& matches!(
&**left,
Expression::Term(t)
if is_absorbable_term(t) && !term_first_token_has_pre_trivia(t)
) =>
{
doc.hardspace();
push_pretty_operation(doc, true, expr, op);
}
Expression::Operation {
lhs: left,
op,
rhs: right,
} if is_lone_ann(op)
&& op.value.is_update_concat_plus()
&& matches!(&**right, Expression::Term(t) if is_absorbable_term(t)) =>
{
let Expression::Term(t) = &**right else {
unreachable!()
};
doc.nested(|d| {
d.group(|g| {
g.line();
left.pretty(g);
g.line();
g.group_ann(GroupAnn::Transparent, |tg| {
op.pretty(tg);
tg.hardspace();
tg.group_ann(GroupAnn::Priority, |pg| {
push_pretty_term_wide(pg, t);
});
});
});
});
}
_ => {
push_nested_rhs(doc, line(), |inner| expr.pretty(inner));
}
}
}
pub(super) fn push_absorb_paren(
doc: &mut Doc,
open: &Ann<Token>,
expr: &Expression,
close: &Ann<Token>,
) {
let (open, trail, close_pre, close) = split_paren_trivia(open, close);
doc.group_ann(GroupAnn::Priority, |g| {
g.nested(|outer| {
open.pretty(outer);
outer.line_prime();
outer.group(|inner| {
inner.nested(|body| {
trail.pretty(body);
expr.pretty(body);
close_pre.pretty(body);
});
});
outer.line_prime();
close.pretty(outer);
});
});
}