use crate::ast::{
Annotated, Binder, Expression, Item, Items, Leaf, Selector, SimpleSelector, Term, Token,
TriviaPiece,
};
use crate::doc::{Doc, Elem, Emit, hardline, hardspace, line, linebreak};
use super::Width;
use super::app::{AppCtx, emit_app};
use super::string::emit_simple_string;
impl Emit for SimpleSelector {
fn emit(&self, doc: &mut Doc) {
match self {
Self::ID(id) => id.emit(doc),
Self::String(ann) => {
ann.emit_with(doc, |d, v| emit_simple_string(d, v));
}
Self::Interpol(interp) => interp.emit(doc),
}
}
}
impl Emit for Selector {
fn emit(&self, doc: &mut Doc) {
if let Some(dot) = &self.dot {
dot.emit(doc);
}
self.selector.emit(doc);
}
}
impl Emit for Binder {
fn emit(&self, doc: &mut Doc) {
match self {
Self::Inherit {
kw: inherit,
from: source,
attrs: ids,
semi: semicolon,
} => {
let same_line = inherit.span.start_line() == semicolon.span.start_line();
let few_ids = ids.len() < 4;
let (sep, nosep) = if same_line && few_ids {
(line(), linebreak())
} else {
(hardline(), hardline())
};
doc.group(|d| {
inherit.emit(d);
let sep_doc = [sep.clone()];
let finish_inherit = |nested: &mut Doc| {
if !ids.is_empty() {
nested.sep_by(&sep_doc, ids);
}
nested.push_raw(nosep.clone());
semicolon.emit(nested);
};
match source {
None => {
d.push_raw(sep.clone());
d.nested(finish_inherit);
}
Some(src) => {
d.nested(|nested| {
nested.group(|g| {
g.line();
src.emit(g);
});
nested.push_raw(sep);
finish_inherit(nested);
});
}
}
});
}
Self::Assignment {
path: selectors,
eq: assign,
value: expr,
semi: semicolon,
} => {
let simple_lhs = selectors.len() <= 4 && selectors.iter().all(Selector::is_simple);
doc.group(|d| {
d.sep_by(&[], selectors);
d.nested(|inner| {
inner.hardspace();
assign.emit(inner);
if simple_lhs {
expr.absorb_rhs(inner);
} else {
inner.linebreak();
inner.priority_group(|g| {
expr.absorb_rhs(g);
});
}
});
semicolon.emit(d);
});
}
}
}
}
pub(super) fn empty_brackets(doc: &mut Doc, open: &Leaf, close: &Leaf) {
open.emit(doc);
if open.span.start_line() == close.span.start_line() {
doc.hardspace();
} else {
doc.hardline();
}
close.emit(doc);
}
pub(super) fn emit_list(doc: &mut Doc, open: &Leaf, items: &Items<Term>, close: &Leaf) {
if items.0.is_empty() && open.trail_comment.is_none() && close.pre_trivia.is_empty() {
empty_brackets(doc, open, close);
} else {
render_list(doc, &hardline(), open, items, close);
}
}
impl Term {
pub(in crate::format) fn emit_bare(&self, doc: &mut Doc) {
match self {
Self::List { open, items, close } => emit_list(doc, open, items, close),
_ => self.emit(doc),
}
}
pub(in crate::format) fn emit_wide(&self, doc: &mut Doc) {
match self {
Self::Set {
rec,
open,
items,
close,
} => emit_set(doc, Width::Wide, rec.as_ref(), open, items, close),
Self::List { open, items, close } => emit_list(doc, open, items, close),
_ => self.emit(doc),
}
}
}
pub(super) fn render_list(
doc: &mut Doc,
item_sep: &Elem,
open: &Annotated<Token>,
items: &Items<Term>,
close: &Annotated<Token>,
) {
open.emit_head(doc);
let sur = if open.span.start_line() != close.span.start_line()
|| items.has_only_comments()
|| (open.has_trivia() && items.0.is_empty())
{
hardline()
} else if items.0.is_empty() {
hardspace()
} else {
line()
};
doc.surrounded(&[sur], |d| {
d.nested(|inner| {
open.trail_comment.emit(inner);
items.emit_sep(inner, item_sep, open.trail_comment.is_none());
});
});
close.emit(doc);
}
pub(super) fn emit_set(
doc: &mut Doc,
wide: Width,
rec: Option<&Annotated<Token>>,
open: &Annotated<Token>,
items: &Items<Binder>,
close: &Annotated<Token>,
) {
if items.0.is_empty() && !open.has_trivia() && close.pre_trivia.is_empty() {
if let Some(rec) = rec {
rec.emit(doc);
doc.hardspace();
}
empty_brackets(doc, open, close);
return;
}
if let Some(rec) = rec {
rec.emit(doc);
doc.hardspace();
}
open.emit_head(doc);
let starts_with_emptyline = match items.0.first() {
Some(Item::Comments(trivia)) => trivia.iter().any(|t| matches!(t, TriviaPiece::EmptyLine)),
_ => false,
};
let sep = if (!items.0.is_empty() && (wide == Width::Wide || starts_with_emptyline))
|| open.span.start_line() != close.span.start_line()
{
hardline()
} else {
line()
};
doc.surrounded(&[sep], |d| {
d.nested(|inner| {
open.trail_comment.emit(inner);
items.emit_sep(inner, &hardline(), open.trail_comment.is_none());
});
});
close.emit(doc);
}
impl<T: Emit> Emit for Items<T> {
fn emit(&self, doc: &mut Doc) {
self.emit_sep(doc, &hardline(), true);
}
}
impl<T: Emit> Items<T> {
pub(super) fn emit_sep(&self, doc: &mut Doc, sep: &Elem, fuse_first: bool) {
let mut iter = self.0.iter().peekable();
let mut first = true;
while let Some(item) = iter.next() {
if !first {
doc.push_raw(sep.clone());
}
let may_fuse = fuse_first || !first;
first = false;
if may_fuse
&& let Item::Comments(trivia) = item
&& let [ann @ TriviaPiece::LanguageAnnotation(_)] = &**trivia
&& let Some(Item::Item(next)) = iter.peek()
{
ann.emit(doc);
doc.group(|d| next.emit(d));
iter.next();
continue;
}
item.emit(doc);
}
}
}
impl Expression {
pub(in crate::format) fn emit_paren_body(&self, doc: &mut Doc) {
match self {
_ if self.is_absorbable() => {
doc.group(|inner| self.absorb(inner, Width::Regular));
}
Self::Apply { .. } => {
emit_app(doc, AppCtx::PAREN, self);
}
Self::Term(Term::Selection { base: term, .. }) if term.is_absorbable() => {
doc.linebreak();
doc.group(|inner| self.emit(inner));
doc.linebreak();
}
Self::Term(Term::Selection { .. }) => {
doc.group(|inner| self.emit(inner));
doc.linebreak();
}
_ => {
doc.linebreak();
doc.group(|inner| self.emit(inner));
doc.linebreak();
}
}
}
}
pub(super) fn emit_paren(
doc: &mut Doc,
open: &Annotated<Token>,
expr: &Expression,
close: &Annotated<Token>,
) {
doc.group(|g| {
open.move_trailing_comment_up().emit(g);
g.nested(|nested| {
expr.emit_paren_body(nested);
close.pre_trivia.emit(nested);
});
close.emit_tail(g);
});
}