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

feat(rome_js_formatter): Member assignment formatting #3061

Merged
merged 2 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
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