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

Commit

Permalink
feat(rome_js_formatter): Group return type with parameters
Browse files Browse the repository at this point in the history
Implement the same heuristic as Prettier for grouping parameters with the return type annotation.

This ensures that a return type will break FIRST before the parameters and the parameters only break if they don't fit on the same line together with the expanded return type annotation.

This PR further unifies the formatting of methods and functions to achieve better code reuse.
  • Loading branch information
MichaReiser committed Aug 2, 2022
1 parent a47eadd commit d7bff1c
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 321 deletions.
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 =
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

0 comments on commit d7bff1c

Please sign in to comment.