This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 659
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rome_js_formatter): Member assignment formatting (#3061)
- Loading branch information
1 parent
4942e79
commit 85ddb4b
Showing
13 changed files
with
294 additions
and
396 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 2 additions & 9 deletions
11
crates/rome_js_formatter/src/js/assignments/static_member_assignment.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,12 @@ | ||
use crate::js::expressions::static_member_expression::JsAnyStaticMemberLike; | ||
use crate::prelude::*; | ||
use rome_formatter::write; | ||
use rome_js_syntax::JsStaticMemberAssignment; | ||
use rome_js_syntax::JsStaticMemberAssignmentFields; | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct FormatJsStaticMemberAssignment; | ||
|
||
impl FormatNodeRule<JsStaticMemberAssignment> for FormatJsStaticMemberAssignment { | ||
fn fmt_fields(&self, node: &JsStaticMemberAssignment, f: &mut JsFormatter) -> FormatResult<()> { | ||
let JsStaticMemberAssignmentFields { | ||
object, | ||
dot_token, | ||
member, | ||
} = node.as_fields(); | ||
|
||
write![f, [object.format(), dot_token.format(), member.format(),]] | ||
JsAnyStaticMemberLike::from(node.clone()).fmt(f) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 102 additions & 60 deletions
162
crates/rome_js_formatter/src/js/expressions/static_member_expression.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,144 @@ | ||
use crate::prelude::*; | ||
|
||
use crate::js::expressions::computed_member_expression::JsAnyComputedMemberLike; | ||
use rome_formatter::{format_args, write}; | ||
use rome_js_syntax::{ | ||
JsAnyExpression, JsAssignmentExpression, JsStaticMemberExpression, | ||
JsStaticMemberExpressionFields, JsVariableDeclarator, | ||
JsAnyAssignment, JsAnyAssignmentPattern, JsAnyExpression, JsAnyName, JsAssignmentExpression, | ||
JsInitializerClause, JsStaticMemberAssignment, JsStaticMemberExpression, JsSyntaxToken, | ||
}; | ||
use rome_rowan::AstNode; | ||
use rome_rowan::{declare_node_union, AstNode, SyntaxResult}; | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct FormatJsStaticMemberExpression; | ||
|
||
impl FormatNodeRule<JsStaticMemberExpression> for FormatJsStaticMemberExpression { | ||
fn fmt_fields(&self, node: &JsStaticMemberExpression, f: &mut JsFormatter) -> FormatResult<()> { | ||
let JsStaticMemberExpressionFields { | ||
object, | ||
operator_token, | ||
member, | ||
} = node.as_fields(); | ||
JsAnyStaticMemberLike::from(node.clone()).fmt(f) | ||
} | ||
} | ||
|
||
write!(f, [object.format()])?; | ||
#[derive(Debug, Copy, Clone)] | ||
enum StaticMemberLikeLayout { | ||
/// Forces that there's no line break between the object, operator, and member | ||
NoBreak, | ||
|
||
let layout = compute_member_layout(node)?; | ||
/// Breaks the static member expression after the object if the whole expression doesn't fit on a single line | ||
BreakAfterObject, | ||
} | ||
|
||
declare_node_union! { | ||
pub(crate) JsAnyStaticMemberLike = JsStaticMemberExpression | JsStaticMemberAssignment | ||
} | ||
|
||
impl Format<JsFormatContext> for JsAnyStaticMemberLike { | ||
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> { | ||
write!(f, [self.object().format()])?; | ||
|
||
let layout = self.layout()?; | ||
|
||
match layout { | ||
StaticMemberExpressionLayout::NoBreak => { | ||
write!(f, [operator_token.format(), member.format()]) | ||
StaticMemberLikeLayout::NoBreak => { | ||
write!(f, [self.operator_token().format(), self.member().format()]) | ||
} | ||
StaticMemberExpressionLayout::BreakAfterObject => { | ||
StaticMemberLikeLayout::BreakAfterObject => { | ||
write!( | ||
f, | ||
[group(&indent(&format_args![ | ||
soft_line_break(), | ||
operator_token.format(), | ||
member.format(), | ||
self.operator_token().format(), | ||
self.member().format(), | ||
]))] | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
enum StaticMemberExpressionLayout { | ||
/// Forces that there's no line break between the object, operator, and member | ||
NoBreak, | ||
impl JsAnyStaticMemberLike { | ||
fn object(&self) -> SyntaxResult<JsAnyExpression> { | ||
match self { | ||
JsAnyStaticMemberLike::JsStaticMemberExpression(expression) => expression.object(), | ||
JsAnyStaticMemberLike::JsStaticMemberAssignment(assignment) => assignment.object(), | ||
} | ||
} | ||
|
||
/// Breaks the static member expression after the object if the whole expression doesn't fit on a single line | ||
BreakAfterObject, | ||
} | ||
fn operator_token(&self) -> SyntaxResult<JsSyntaxToken> { | ||
match self { | ||
JsAnyStaticMemberLike::JsStaticMemberExpression(expression) => { | ||
expression.operator_token() | ||
} | ||
JsAnyStaticMemberLike::JsStaticMemberAssignment(assignment) => assignment.dot_token(), | ||
} | ||
} | ||
|
||
fn compute_member_layout( | ||
member: &JsStaticMemberExpression, | ||
) -> FormatResult<StaticMemberExpressionLayout> { | ||
let parent = member.syntax().parent(); | ||
|
||
let nested = parent | ||
.as_ref() | ||
.map_or(false, |p| JsStaticMemberExpression::can_cast(p.kind())); | ||
|
||
if let Some(parent) = &parent { | ||
if JsAssignmentExpression::can_cast(parent.kind()) | ||
|| JsVariableDeclarator::can_cast(parent.kind()) | ||
{ | ||
let no_break = match member.object()? { | ||
JsAnyExpression::JsCallExpression(call_expression) => { | ||
!call_expression.arguments()?.args().is_empty() | ||
} | ||
JsAnyExpression::TsNonNullAssertionExpression(non_null_assertion) => { | ||
match non_null_assertion.expression()? { | ||
fn member(&self) -> SyntaxResult<JsAnyName> { | ||
match self { | ||
JsAnyStaticMemberLike::JsStaticMemberExpression(expression) => expression.member(), | ||
JsAnyStaticMemberLike::JsStaticMemberAssignment(assignment) => assignment.member(), | ||
} | ||
} | ||
|
||
fn layout(&self) -> SyntaxResult<StaticMemberLikeLayout> { | ||
let parent = self.syntax().parent(); | ||
let object = self.object()?; | ||
|
||
let is_nested = match &parent { | ||
Some(parent) => { | ||
if JsAssignmentExpression::can_cast(parent.kind()) | ||
|| JsInitializerClause::can_cast(parent.kind()) | ||
{ | ||
let no_break = match &object { | ||
JsAnyExpression::JsCallExpression(call_expression) => { | ||
!call_expression.arguments()?.args().is_empty() | ||
} | ||
JsAnyExpression::TsNonNullAssertionExpression(non_null_assertion) => { | ||
match non_null_assertion.expression()? { | ||
JsAnyExpression::JsCallExpression(call_expression) => { | ||
!call_expression.arguments()?.args().is_empty() | ||
} | ||
_ => false, | ||
} | ||
} | ||
_ => false, | ||
}; | ||
|
||
if no_break { | ||
return Ok(StaticMemberLikeLayout::NoBreak); | ||
} | ||
} | ||
_ => false, | ||
}; | ||
|
||
if no_break { | ||
return Ok(StaticMemberExpressionLayout::NoBreak); | ||
JsAnyStaticMemberLike::can_cast(parent.kind()) | ||
|| JsAnyComputedMemberLike::can_cast(parent.kind()) | ||
} | ||
} | ||
}; | ||
None => false, | ||
}; | ||
|
||
if !nested && matches!(member.object()?, JsAnyExpression::JsIdentifierExpression(_)) { | ||
return Ok(StaticMemberExpressionLayout::NoBreak); | ||
} | ||
if !is_nested && matches!(&object, JsAnyExpression::JsIdentifierExpression(_)) { | ||
return Ok(StaticMemberLikeLayout::NoBreak); | ||
} | ||
|
||
let first_non_static_member_ancestor = member | ||
.syntax() | ||
.ancestors() | ||
.find(|parent| !JsStaticMemberExpression::can_cast(parent.kind())); | ||
let first_non_static_member_ancestor = self.syntax().ancestors().find(|parent| { | ||
!JsAnyStaticMemberLike::can_cast(parent.kind()) | ||
|| JsAnyComputedMemberLike::can_cast(parent.kind()) | ||
}); | ||
|
||
let layout = match first_non_static_member_ancestor.and_then(JsAnyExpression::cast) { | ||
Some(JsAnyExpression::JsNewExpression(_)) => StaticMemberLikeLayout::NoBreak, | ||
Some(JsAnyExpression::JsAssignmentExpression(assignment)) => { | ||
if matches!( | ||
assignment.left()?, | ||
JsAnyAssignmentPattern::JsAnyAssignment( | ||
JsAnyAssignment::JsIdentifierAssignment(_) | ||
) | ||
) { | ||
StaticMemberLikeLayout::BreakAfterObject | ||
} else { | ||
StaticMemberLikeLayout::NoBreak | ||
} | ||
} | ||
_ => StaticMemberLikeLayout::BreakAfterObject, | ||
}; | ||
|
||
if matches!( | ||
first_non_static_member_ancestor.and_then(JsAnyExpression::cast), | ||
Some(JsAnyExpression::JsNewExpression(_)) | ||
) { | ||
return Ok(StaticMemberExpressionLayout::NoBreak); | ||
Ok(layout) | ||
} | ||
|
||
Ok(StaticMemberExpressionLayout::BreakAfterObject) | ||
} |
Oops, something went wrong.