use crate::prelude::*;
use crate::utils::sort_modifiers_by_precedence;
use crate::{AsFormat, IntoFormat};
use biome_formatter::{format_args, write};
use biome_js_syntax::JsSyntaxKind::JS_DECORATOR;
use biome_js_syntax::{JsLanguage, Modifiers};
use biome_rowan::{AstNode, AstNodeList, NodeOrToken};
pub(crate) struct FormatModifiers<List> {
pub(crate) list: List,
}
impl<List> FormatModifiers<List> {
pub(crate) fn from(list: List) -> Self {
Self { list }
}
}
impl<List, Node> Format<JsFormatContext> for FormatModifiers<List>
where
Node: AstNode<Language = JsLanguage> + AsFormat<JsFormatContext> + IntoFormat<JsFormatContext>,
List: AstNodeList<Language = JsLanguage, Node = Node>,
Modifiers: for<'a> From<&'a Node>,
{
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
let modifiers = sort_modifiers_by_precedence(&self.list);
let should_expand = should_expand_decorators(&self.list);
let mut iter = modifiers.into_iter().peekable();
let decorators = format_once(|f| {
let mut join = f.join_nodes_with_soft_line();
while let Some(node) = iter.peek() {
match node.syntax().kind() {
JS_DECORATOR => {
join.entry(node.syntax(), &node.format());
iter.next();
}
_ => {
break;
}
}
}
join.finish()
});
write!(
f,
[group(&format_args![decorators, soft_line_break_or_space()])
.should_expand(should_expand)]
)?;
f.join_with(&space()).entries(iter.formatted()).finish()
}
}
pub(crate) fn should_expand_decorators<List, Node>(list: &List) -> bool
where
Node: AstNode<Language = JsLanguage>,
List: AstNodeList<Language = JsLanguage, Node = Node>,
{
for node in list.iter().skip(1) {
match node.syntax().kind() {
JS_DECORATOR => {
if node.syntax().has_leading_newline() {
return true;
}
}
_ => {
return node.syntax().has_leading_newline();
}
}
}
list.syntax_list()
.node()
.next_sibling_or_token()
.map_or(false, |node| match node {
NodeOrToken::Node(node) => node.has_leading_newline(),
NodeOrToken::Token(token) => token.has_leading_newline(),
})
}