Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

feat(rome_js_formatter): Function parameter & return type grouping #2990

Merged
merged 2 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/rome_formatter/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub use crate::builders::*;
pub use crate::format_element::*;
pub use crate::format_extensions::{FormatOptional as _, MemoizeFormat};
pub use crate::format_extensions::{FormatOptional as _, MemoizeFormat, Memoized};
pub use crate::formatter::Formatter;
pub use crate::printer::PrinterOptions;
pub use crate::token::{
Expand Down
12 changes: 11 additions & 1 deletion crates/rome_js_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ pub fn format_delimited<'a, 'content>(
content: Argument::new(content),
close_token,
mode: DelimitedMode::SoftBlockIndent(None),
grouped: true,
}
}

Expand All @@ -329,6 +330,7 @@ pub struct FormatDelimited<'a, 'content> {
content: Argument<'content, JsFormatContext>,
close_token: &'a JsSyntaxToken,
mode: DelimitedMode,
grouped: bool,
}

impl FormatDelimited<'_, '_> {
Expand Down Expand Up @@ -359,6 +361,12 @@ impl FormatDelimited<'_, '_> {
pub fn soft_block_indent_with_group_id(self, group_id: Option<GroupId>) -> Self {
self.with_mode(DelimitedMode::SoftBlockIndent(group_id))
}

/// Prevents the formatter from grouping the content even in soft block or soft block spaces mode.
pub fn ungrouped(mut self) -> Self {
self.grouped = false;
self
}
}

impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
Expand All @@ -368,6 +376,7 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
close_token,
content,
mode,
grouped,
} = self;

let open_delimiter = format_open_delimiter(open_token);
Expand Down Expand Up @@ -427,8 +436,8 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
});

match mode {
_ if !grouped => write!(f, [delimited])?,
// Group is useless, the block indent would expand it right anyway
DelimitedMode::BlockIndent => write!(f, [delimited])?,
DelimitedMode::SoftBlockIndent(group_id) | DelimitedMode::SoftBlockSpaces(group_id) => {
match group_id {
None => write!(f, [group(&delimited)])?,
Expand All @@ -437,6 +446,7 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
}
}
}
DelimitedMode::BlockIndent => write!(f, [delimited])?,
};

write!(f, [format_trailing_trivia(close_token)])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;

use rome_formatter::write;
use rome_js_syntax::JsConstructorParameters;
use rome_js_syntax::JsConstructorParametersFields;

Expand All @@ -15,12 +14,9 @@ impl FormatNodeRule<JsConstructorParameters> for FormatJsConstructorParameters {
r_paren_token,
} = node.as_fields();

write!(
f,
[
format_delimited(&l_paren_token?, &parameters.format(), &r_paren_token?,)
.soft_block_indent()
]
)
format_delimited(&l_paren_token?, &parameters.format(), &r_paren_token?)
.soft_block_indent()
.ungrouped()
.fmt(f)
}
}
12 changes: 4 additions & 8 deletions crates/rome_js_formatter/src/js/bindings/parameters.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::prelude::*;

use rome_formatter::write;
use rome_js_syntax::JsParameters;
use rome_js_syntax::JsParametersFields;

Expand All @@ -15,12 +14,9 @@ impl FormatNodeRule<JsParameters> for FormatJsParameters {
r_paren_token,
} = node.as_fields();

write!(
f,
[
format_delimited(&l_paren_token?, &items.format(), &r_paren_token?,)
.soft_block_indent()
]
)
format_delimited(&l_paren_token?, &items.format(), &r_paren_token?)
.soft_block_indent()
.ungrouped()
.fmt(f)
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
use crate::prelude::*;

use crate::js::classes::method_class_member::FormatMethodMember;
use rome_formatter::write;
use rome_js_syntax::JsConstructorClassMember;
use rome_js_syntax::JsConstructorClassMemberFields;

#[derive(Debug, Clone, Default)]
pub struct FormatJsConstructorClassMember;

impl FormatNodeRule<JsConstructorClassMember> for FormatJsConstructorClassMember {
fn fmt_fields(&self, node: &JsConstructorClassMember, f: &mut JsFormatter) -> FormatResult<()> {
let JsConstructorClassMemberFields {
modifiers,
name,
parameters,
body,
} = node.as_fields();

write![
f,
[
modifiers.format(),
space(),
name.format(),
parameters.format(),
node.modifiers().format(),
space(),
body.format()
FormatMethodMember::from(node.clone())
]
]
}
Expand Down
223 changes: 198 additions & 25 deletions crates/rome_js_formatter/src/js/classes/method_class_member.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,217 @@
use crate::prelude::*;

use crate::js::declarations::function_declaration::should_group_function_parameters;
use rome_formatter::write;
use rome_js_syntax::JsMethodClassMember;
use rome_js_syntax::JsMethodClassMemberFields;
use rome_js_syntax::{
JsAnyClassMemberName, JsAnyObjectMemberName, JsConstructorClassMember, JsConstructorParameters,
JsFunctionBody, JsParameters, TsMethodSignatureClassMember, TsMethodSignatureTypeMember,
TsReturnTypeAnnotation, TsTypeParameters,
};
use rome_js_syntax::{JsMethodClassMember, JsMethodObjectMember, JsSyntaxToken};
use rome_rowan::{declare_node_union, SyntaxResult};

#[derive(Debug, Clone, Default)]
pub struct FormatJsMethodClassMember;

impl FormatNodeRule<JsMethodClassMember> for FormatJsMethodClassMember {
fn fmt_fields(&self, node: &JsMethodClassMember, f: &mut JsFormatter) -> FormatResult<()> {
let JsMethodClassMemberFields {
modifiers,
async_token,
star_token,
name,
question_mark_token,
type_parameters,
parameters,
return_type_annotation,
body,
} = node.as_fields();

write![f, [modifiers.format(), space(),]]?;

if let Some(async_token) = async_token {
write![
f,
[
node.modifiers().format(),
space(),
FormatMethodMember::from(node.clone())
]
]
}
}

declare_node_union! {
/// Formats the type parameters, parameters, and return type annotation of a method
pub(crate) FormatMethodMember =
MichaReiser marked this conversation as resolved.
Show resolved Hide resolved
JsMethodClassMember |
JsMethodObjectMember |
JsConstructorClassMember |
TsMethodSignatureClassMember |
TsMethodSignatureTypeMember
}

impl Format<JsFormatContext> for FormatMethodMember {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
if let Some(async_token) = self.async_token() {
write!(f, [async_token.format(), space()])?;
}

let type_parameters = self.type_parameters();

write!(
f,
[
star_token.format(),
name.format(),
question_mark_token.format(),
self.star_token().format(),
self.name(),
self.question_mark_token().format(),
type_parameters.format(),
parameters.format(),
return_type_annotation.format(),
space(),
body.format()
]
)
)?;

write!(
f,
[group(&format_with(|f| {
let parameters = self.parameters()?;
let return_type_annotation = self.return_type_annotation();
let mut format_return_type_annotation = return_type_annotation.format().memoized();

if should_group_function_parameters(
type_parameters.as_ref(),
parameters.len(),
return_type_annotation
.as_ref()
.map(|annotation| annotation.ty()),
&mut format_return_type_annotation,
f,
)? {
write!(f, [group(&parameters)])?;
} else {
write!(f, [parameters])?;
}

write!(f, [format_return_type_annotation])
}))]
)?;

if let Some(body) = self.body()? {
write!(f, [space(), body.format()])?;
}

Ok(())
}
}

impl FormatMethodMember {
fn async_token(&self) -> Option<JsSyntaxToken> {
match self {
FormatMethodMember::JsMethodClassMember(member) => member.async_token(),
FormatMethodMember::JsMethodObjectMember(member) => member.async_token(),
FormatMethodMember::JsConstructorClassMember(_) => None,
FormatMethodMember::TsMethodSignatureClassMember(signature) => signature.async_token(),
FormatMethodMember::TsMethodSignatureTypeMember(_) => None,
}
}

fn star_token(&self) -> Option<JsSyntaxToken> {
match self {
FormatMethodMember::JsMethodClassMember(member) => member.star_token(),
FormatMethodMember::JsMethodObjectMember(member) => member.star_token(),
FormatMethodMember::JsConstructorClassMember(_) => None,
FormatMethodMember::TsMethodSignatureClassMember(_) => None,
FormatMethodMember::TsMethodSignatureTypeMember(_) => None,
}
}

fn name(&self) -> SyntaxResult<AnyMemberName> {
Ok(match self {
FormatMethodMember::JsMethodClassMember(member) => member.name()?.into(),
FormatMethodMember::JsMethodObjectMember(member) => member.name()?.into(),
FormatMethodMember::JsConstructorClassMember(member) => {
AnyMemberName::from(JsAnyClassMemberName::from(member.name()?))
}
FormatMethodMember::TsMethodSignatureClassMember(signature) => signature.name()?.into(),
FormatMethodMember::TsMethodSignatureTypeMember(member) => member.name()?.into(),
})
}

fn type_parameters(&self) -> Option<TsTypeParameters> {
match self {
FormatMethodMember::JsMethodClassMember(member) => member.type_parameters(),
FormatMethodMember::JsMethodObjectMember(member) => member.type_parameters(),
FormatMethodMember::JsConstructorClassMember(_) => None,
FormatMethodMember::TsMethodSignatureClassMember(signature) => {
signature.type_parameters()
}
FormatMethodMember::TsMethodSignatureTypeMember(member) => member.type_parameters(),
}
}

fn parameters(&self) -> SyntaxResult<MethodParameters> {
Ok(match self {
FormatMethodMember::JsMethodClassMember(member) => member.parameters()?.into(),
FormatMethodMember::JsMethodObjectMember(member) => member.parameters()?.into(),
FormatMethodMember::JsConstructorClassMember(member) => member.parameters()?.into(),
FormatMethodMember::TsMethodSignatureClassMember(signature) => {
signature.parameters()?.into()
}
FormatMethodMember::TsMethodSignatureTypeMember(member) => member.parameters()?.into(),
})
}

fn return_type_annotation(&self) -> Option<TsReturnTypeAnnotation> {
match self {
FormatMethodMember::JsMethodClassMember(member) => member.return_type_annotation(),
FormatMethodMember::JsMethodObjectMember(member) => member.return_type_annotation(),
FormatMethodMember::JsConstructorClassMember(_) => None,
FormatMethodMember::TsMethodSignatureClassMember(signature) => {
signature.return_type_annotation()
}
FormatMethodMember::TsMethodSignatureTypeMember(member) => {
member.return_type_annotation()
}
}
}

fn question_mark_token(&self) -> Option<JsSyntaxToken> {
match self {
FormatMethodMember::JsMethodClassMember(member) => member.question_mark_token(),
FormatMethodMember::JsMethodObjectMember(_) => None,
FormatMethodMember::JsConstructorClassMember(_) => None,
FormatMethodMember::TsMethodSignatureClassMember(signature) => {
signature.question_mark_token()
}
FormatMethodMember::TsMethodSignatureTypeMember(member) => member.optional_token(),
}
}

fn body(&self) -> SyntaxResult<Option<JsFunctionBody>> {
Ok(match self {
FormatMethodMember::JsMethodClassMember(member) => Some(member.body()?),
FormatMethodMember::JsMethodObjectMember(member) => Some(member.body()?),
FormatMethodMember::JsConstructorClassMember(member) => Some(member.body()?),
FormatMethodMember::TsMethodSignatureClassMember(_) => None,
FormatMethodMember::TsMethodSignatureTypeMember(_) => None,
})
}
}

declare_node_union! {
AnyMemberName = JsAnyClassMemberName | JsAnyObjectMemberName
}

impl Format<JsFormatContext> for AnyMemberName {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
match self {
AnyMemberName::JsAnyClassMemberName(name) => name.format().fmt(f),
AnyMemberName::JsAnyObjectMemberName(name) => name.format().fmt(f),
}
}
}

declare_node_union! {
MethodParameters = JsParameters | JsConstructorParameters
}

impl MethodParameters {
pub fn len(&self) -> usize {
match self {
MethodParameters::JsParameters(parameters) => parameters.items().len(),
MethodParameters::JsConstructorParameters(parameters) => parameters.parameters().len(),
}
}
}

impl Format<JsFormatContext> for MethodParameters {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
match self {
MethodParameters::JsParameters(parameters) => parameters.format().fmt(f),
MethodParameters::JsConstructorParameters(parameters) => parameters.format().fmt(f),
}
}
}
Loading