pub(crate) mod array;
mod assignment_like;
mod binary_like_expression;
mod conditional;
pub mod string_utils;
pub(crate) mod format_class;
pub(crate) mod format_modifiers;
pub(crate) mod function_body;
pub mod jsx;
pub(crate) mod member_chain;
mod object;
mod object_like;
mod object_pattern_like;
#[cfg(test)]
mod quickcheck_utils;
pub(crate) mod test_call;
pub(crate) mod test_each_template;
mod typescript;
use crate::context::trailing_comma::FormatTrailingComma;
use crate::context::Semicolons;
use crate::parentheses::is_callee;
pub(crate) use crate::parentheses::resolve_left_most_expression;
use crate::prelude::*;
pub(crate) use assignment_like::{
with_assignment_layout, AnyJsAssignmentLike, AssignmentLikeLayout,
};
pub(crate) use binary_like_expression::{
needs_binary_like_parentheses, AnyJsBinaryLikeExpression, AnyJsBinaryLikeLeftExpression,
};
use biome_formatter::{format_args, write, Buffer};
use biome_js_syntax::JsSyntaxToken;
use biome_js_syntax::{
AnyJsExpression, AnyJsStatement, JsCallExpression, JsInitializerClause, JsLanguage, Modifiers,
};
use biome_rowan::{AstNode, AstNodeList};
pub(crate) use conditional::{AnyJsConditional, ConditionalJsxChain};
pub(crate) use object_like::JsObjectLike;
pub(crate) use object_pattern_like::JsObjectPatternLike;
pub(crate) use string_utils::*;
pub(crate) use typescript::{
is_object_like_type, should_hug_type, union_or_intersection_type_needs_parentheses,
TsIntersectionOrUnionTypeList,
};
pub(crate) fn is_long_curried_call(expression: Option<&JsCallExpression>) -> bool {
if let Some(expression) = expression {
if let Some(parent_call) = expression.parent::<JsCallExpression>() {
if let (Ok(arguments), Ok(parent_arguments)) =
(expression.arguments(), parent_call.arguments())
{
return is_callee(expression.syntax(), parent_call.syntax())
&& arguments.args().len() > parent_arguments.args().len()
&& !parent_arguments.args().is_empty();
}
}
}
false
}
pub(crate) struct FormatTypeMemberSeparator<'a> {
token: Option<&'a JsSyntaxToken>,
}
impl<'a> FormatTypeMemberSeparator<'a> {
pub fn new(token: Option<&'a JsSyntaxToken>) -> Self {
Self { token }
}
}
impl Format<JsFormatContext> for FormatTypeMemberSeparator<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
if let Some(separator) = self.token {
format_removed(separator).fmt(f)
} else {
Ok(())
}
}
}
pub(crate) struct FormatInitializerClause<'a> {
initializer: Option<&'a JsInitializerClause>,
}
impl<'a> FormatInitializerClause<'a> {
pub fn new(initializer: Option<&'a JsInitializerClause>) -> Self {
Self { initializer }
}
}
impl Format<JsFormatContext> for FormatInitializerClause<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
if let Some(initializer) = self.initializer {
write!(f, [space(), initializer.format()])
} else {
Ok(())
}
}
}
pub(crate) struct FormatInterpreterToken<'a> {
token: Option<&'a JsSyntaxToken>,
}
impl<'a> FormatInterpreterToken<'a> {
pub fn new(interpreter_token: Option<&'a JsSyntaxToken>) -> Self {
Self {
token: interpreter_token,
}
}
}
impl Format<JsFormatContext> for FormatInterpreterToken<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
if let Some(interpreter) = self.token {
write!(f, [interpreter.format()])?;
match interpreter
.next_token()
.map_or(0, |next_token| get_lines_before_token(&next_token))
{
0 | 1 => write!(f, [hard_line_break()]),
_ => write!(f, [empty_line()]),
}
} else {
Ok(())
}
}
}
pub(crate) struct FormatStatementBody<'a> {
body: &'a AnyJsStatement,
force_space: bool,
}
impl<'a> FormatStatementBody<'a> {
pub fn new(body: &'a AnyJsStatement) -> Self {
Self {
body,
force_space: false,
}
}
pub fn with_forced_space(mut self, forced: bool) -> Self {
self.force_space = forced;
self
}
}
impl Format<JsFormatContext> for FormatStatementBody<'_> {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
use AnyJsStatement::*;
if let JsEmptyStatement(empty) = &self.body {
write!(f, [empty.format()])
} else if matches!(&self.body, JsBlockStatement(_)) || self.force_space {
write!(f, [space(), self.body.format()])
} else {
write!(
f,
[indent(&format_args![
soft_line_break_or_space(),
self.body.format()
])]
)
}
}
}
pub(crate) fn sort_modifiers_by_precedence<List, Node>(list: &List) -> Vec<Node>
where
Node: AstNode<Language = JsLanguage>,
List: AstNodeList<Language = JsLanguage, Node = Node>,
Modifiers: for<'a> From<&'a Node>,
{
let mut nodes_and_modifiers = list.iter().collect::<Vec<Node>>();
nodes_and_modifiers.sort_unstable_by_key(|node| Modifiers::from(node));
nodes_and_modifiers
}
pub(crate) type FormatStatementSemicolon<'a> = FormatOptionalSemicolon<'a>;
pub(crate) struct FormatOptionalSemicolon<'a> {
semicolon: Option<&'a JsSyntaxToken>,
}
impl<'a> FormatOptionalSemicolon<'a> {
pub(crate) fn new(semicolon: Option<&'a JsSyntaxToken>) -> Self {
Self { semicolon }
}
}
impl Format<JsFormatContext> for FormatOptionalSemicolon<'_> {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
match f.options().semicolons() {
Semicolons::Always => FormatSemicolon::new(self.semicolon).fmt(f),
Semicolons::AsNeeded => match self.semicolon {
None => Ok(()),
Some(semicolon) => format_removed(semicolon).fmt(f),
},
}
}
}
pub(crate) struct FormatSemicolon<'a> {
semicolon: Option<&'a JsSyntaxToken>,
}
impl<'a> FormatSemicolon<'a> {
pub fn new(semicolon: Option<&'a JsSyntaxToken>) -> Self {
Self { semicolon }
}
}
impl Format<JsFormatContext> for FormatSemicolon<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
match self.semicolon {
Some(semicolon) => semicolon.format().fmt(f),
None => {
let is_after_bogus = f.elements().start_tag(TagKind::Verbatim).map_or(
false,
|signal| match signal {
Tag::StartVerbatim(kind) => kind.is_bogus(),
_ => unreachable!(),
},
);
if !is_after_bogus {
write!(f, [text(";")])?;
}
Ok(())
}
}
}
}
pub(crate) fn is_call_like_expression(expression: &AnyJsExpression) -> bool {
matches!(
expression,
AnyJsExpression::JsNewExpression(_)
| AnyJsExpression::JsImportCallExpression(_)
| AnyJsExpression::JsCallExpression(_)
)
}
pub(crate) fn write_arguments_multi_line<S: Format<JsFormatContext>, I>(
separated: I,
f: &mut JsFormatter,
) -> FormatResult<()>
where
I: Iterator<Item = S>,
{
let mut iterator = separated.peekable();
let mut join_with = f.join_with(soft_line_break_or_space());
while let Some(element) = iterator.next() {
let last = iterator.peek().is_none();
if last {
join_with.entry(&format_args![&element, FormatTrailingComma::All]);
} else {
join_with.entry(&element);
}
}
join_with.finish()
}