Skip to content

Commit

Permalink
feat(rome_js_formatter): Member assignment formatting (rome#3061)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser authored and IWANABETHATGUY committed Aug 22, 2022
1 parent b57cd08 commit 3d99ce3
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 396 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::prelude::*;
use rome_formatter::write;

use crate::js::expressions::computed_member_expression::JsAnyComputedMemberLike;
use rome_js_syntax::JsComputedMemberAssignment;
use rome_js_syntax::JsComputedMemberAssignmentFields;

#[derive(Debug, Clone, Default)]
pub struct FormatJsComputedMemberAssignment;
Expand All @@ -13,21 +12,6 @@ impl FormatNodeRule<JsComputedMemberAssignment> for FormatJsComputedMemberAssign
node: &JsComputedMemberAssignment,
f: &mut JsFormatter,
) -> FormatResult<()> {
let JsComputedMemberAssignmentFields {
object,
l_brack_token,
member,
r_brack_token,
} = node.as_fields();

write!(
f,
[
object.format(),
l_brack_token.format(),
member.format(),
r_brack_token.format(),
]
)
JsAnyComputedMemberLike::from(node.clone()).fmt(f)
}
}
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)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::prelude::*;

use rome_formatter::{format_args, write};
use rome_js_syntax::JsComputedMemberExpression;
use rome_js_syntax::JsComputedMemberExpressionFields;
use rome_js_syntax::JsSyntaxToken;
use rome_js_syntax::{
JsAnyExpression, JsAnyLiteralExpression, JsComputedMemberAssignment, JsComputedMemberExpression,
};
use rome_rowan::{declare_node_union, SyntaxResult};

#[derive(Debug, Clone, Default)]
pub struct FormatJsComputedMemberExpression;
Expand All @@ -13,26 +16,91 @@ impl FormatNodeRule<JsComputedMemberExpression> for FormatJsComputedMemberExpres
node: &JsComputedMemberExpression,
f: &mut JsFormatter,
) -> FormatResult<()> {
let JsComputedMemberExpressionFields {
object,
optional_chain_token,
l_brack_token,
member,
r_brack_token,
} = node.as_fields();

write![
f,
[
object.format(),
group(&format_args![
optional_chain_token.format(),
l_brack_token.format(),
soft_line_break(),
soft_block_indent(&member.format()),
r_brack_token.format()
]),
]
]
JsAnyComputedMemberLike::from(node.clone()).fmt(f)
}
}

declare_node_union! {
pub(crate) JsAnyComputedMemberLike = JsComputedMemberExpression | JsComputedMemberAssignment
}

impl Format<JsFormatContext> for JsAnyComputedMemberLike {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
write!(f, [self.object().format()])?;

match self.member()? {
JsAnyExpression::JsAnyLiteralExpression(
JsAnyLiteralExpression::JsNumberLiteralExpression(literal),
) => {
write!(
f,
[
self.optional_chain_token().format(),
self.l_brack_token().format(),
literal.format(),
self.r_brack_token().format()
]
)
}
member => {
write![
f,
[group(&format_args![
self.optional_chain_token().format(),
self.l_brack_token().format(),
soft_line_break(),
soft_block_indent(&member.format()),
self.r_brack_token().format()
]),]
]
}
}
}
}

impl JsAnyComputedMemberLike {
fn object(&self) -> SyntaxResult<JsAnyExpression> {
match self {
JsAnyComputedMemberLike::JsComputedMemberExpression(expression) => expression.object(),
JsAnyComputedMemberLike::JsComputedMemberAssignment(assignment) => assignment.object(),
}
}

fn l_brack_token(&self) -> SyntaxResult<JsSyntaxToken> {
match self {
JsAnyComputedMemberLike::JsComputedMemberExpression(expression) => {
expression.l_brack_token()
}
JsAnyComputedMemberLike::JsComputedMemberAssignment(assignment) => {
assignment.l_brack_token()
}
}
}

fn optional_chain_token(&self) -> Option<JsSyntaxToken> {
match self {
JsAnyComputedMemberLike::JsComputedMemberExpression(expression) => {
expression.optional_chain_token()
}
JsAnyComputedMemberLike::JsComputedMemberAssignment(_) => None,
}
}

fn member(&self) -> SyntaxResult<JsAnyExpression> {
match self {
JsAnyComputedMemberLike::JsComputedMemberExpression(expression) => expression.member(),
JsAnyComputedMemberLike::JsComputedMemberAssignment(assignment) => assignment.member(),
}
}

fn r_brack_token(&self) -> SyntaxResult<JsSyntaxToken> {
match self {
JsAnyComputedMemberLike::JsComputedMemberExpression(expression) => {
expression.r_brack_token()
}
JsAnyComputedMemberLike::JsComputedMemberAssignment(assignment) => {
assignment.r_brack_token()
}
}
}
}
162 changes: 102 additions & 60 deletions crates/rome_js_formatter/src/js/expressions/static_member_expression.rs
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)
}
Loading

0 comments on commit 3d99ce3

Please sign in to comment.