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

Commit

Permalink
refactor(formatter): Member chain: left to right formatting (#2641)
Browse files Browse the repository at this point in the history
This PR refactors the member chain formatting into two stages:

* Building the member chain groups
* Formatting the groups

Doing so allows us to format the elements in left-to-right order.
  • Loading branch information
MichaReiser authored Jun 6, 2022
1 parent 941faf9 commit f7c6e2d
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 147 deletions.
110 changes: 69 additions & 41 deletions crates/rome_js_formatter/src/utils/member_chain/flatten_item.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
use crate::prelude::*;
use rome_formatter::write;
use rome_js_syntax::{
JsAnyExpression, JsCallExpression, JsComputedMemberExpression, JsIdentifierExpression,
JsImportCallExpression, JsNewExpression, JsStaticMemberExpression, JsSyntaxNode,
JsAnyExpression, JsCallExpression, JsCallExpressionFields, JsComputedMemberExpression,
JsComputedMemberExpressionFields, JsIdentifierExpression, JsImportCallExpression,
JsNewExpression, JsStaticMemberExpression, JsStaticMemberExpressionFields, JsSyntaxNode,
JsThisExpression,
};
use rome_rowan::{AstNode, SyntaxResult};
use std::fmt::Debug;

#[derive(Clone)]
#[derive(Clone, Debug)]
/// Data structure that holds the node with its formatted version
pub(crate) enum FlattenItem {
/// Holds onto a [rome_js_syntax::JsStaticMemberExpression]
StaticMember(JsStaticMemberExpression, Vec<FormatElement>),
StaticMember(JsStaticMemberExpression),
/// Holds onto a [rome_js_syntax::JsCallExpression]
CallExpression(JsCallExpression, Vec<FormatElement>),
CallExpression(JsCallExpression),
/// Holds onto a [rome_js_syntax::JsComputedMemberExpression]
ComputedExpression(JsComputedMemberExpression, Vec<FormatElement>),
ComputedMember(JsComputedMemberExpression),
/// Any other node that are not [rome_js_syntax::JsCallExpression] or [rome_js_syntax::JsStaticMemberExpression]
/// Are tracked using this variant
Node(JsSyntaxNode, FormatElement),
Node(JsSyntaxNode),
}

impl FlattenItem {
/// checks if the current node is a [rome_js_syntax::JsCallExpression], [rome_js_syntax::JsImportExpression] or a [rome_js_syntax::JsNewExpression]
pub fn is_loose_call_expression(&self) -> bool {
match self {
FlattenItem::CallExpression(_, _) => true,
FlattenItem::Node(node, _) => {
FlattenItem::CallExpression(_) => true,
FlattenItem::Node(node) => {
JsImportCallExpression::can_cast(node.kind())
| JsNewExpression::can_cast(node.kind())
}
Expand All @@ -36,36 +38,36 @@ impl FlattenItem {

pub(crate) fn as_syntax(&self) -> &JsSyntaxNode {
match self {
FlattenItem::StaticMember(node, _) => node.syntax(),
FlattenItem::CallExpression(node, _) => node.syntax(),
FlattenItem::ComputedExpression(node, _) => node.syntax(),
FlattenItem::Node(node, _) => node,
FlattenItem::StaticMember(node) => node.syntax(),
FlattenItem::CallExpression(node) => node.syntax(),
FlattenItem::ComputedMember(node) => node.syntax(),
FlattenItem::Node(node) => node,
}
}

pub(crate) fn has_trailing_comments(&self) -> bool {
match self {
FlattenItem::StaticMember(node, _) => node.syntax().has_trailing_comments(),
FlattenItem::CallExpression(node, _) => node.syntax().has_trailing_comments(),
FlattenItem::ComputedExpression(node, _) => node.syntax().has_trailing_comments(),
FlattenItem::Node(node, _) => node.has_trailing_comments(),
FlattenItem::StaticMember(node) => node.syntax().has_trailing_comments(),
FlattenItem::CallExpression(node) => node.syntax().has_trailing_comments(),
FlattenItem::ComputedMember(node) => node.syntax().has_trailing_comments(),
FlattenItem::Node(node) => node.has_trailing_comments(),
}
}

pub fn is_computed_expression(&self) -> bool {
matches!(self, FlattenItem::ComputedExpression(..))
matches!(self, FlattenItem::ComputedMember(..))
}

pub(crate) fn is_this_expression(&self) -> bool {
match self {
FlattenItem::Node(node, _) => JsThisExpression::can_cast(node.kind()),
FlattenItem::Node(node) => JsThisExpression::can_cast(node.kind()),
_ => false,
}
}

pub(crate) fn is_identifier_expression(&self) -> bool {
match self {
FlattenItem::Node(node, _) => JsIdentifierExpression::can_cast(node.kind()),
FlattenItem::Node(node) => JsIdentifierExpression::can_cast(node.kind()),
_ => false,
}
}
Expand Down Expand Up @@ -138,32 +140,58 @@ impl FlattenItem {
}
}

impl Debug for FlattenItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl Format<JsFormatContext> for FlattenItem {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
match self {
FlattenItem::StaticMember(_, formatted) => {
std::write!(f, "StaticMember: {:?}", formatted)
FlattenItem::StaticMember(static_member) => {
let JsStaticMemberExpressionFields {
// Formatted as part of the previous item
object: _,
operator_token,
member,
} = static_member.as_fields();
write![f, [operator_token.format(), member.format(),]]
}
FlattenItem::CallExpression(_, formatted) => {
std::write!(f, "CallExpression: {:?}", formatted)
FlattenItem::CallExpression(call_expression) => {
let JsCallExpressionFields {
// Formatted as part of the previous item
callee: _,
optional_chain_token,
type_arguments,
arguments,
} = call_expression.as_fields();

write!(
f,
[
optional_chain_token.format(),
type_arguments.format(),
arguments.format()
]
)
}
FlattenItem::ComputedExpression(_, formatted) => {
std::write!(f, "ComputedExpression: {:?}", formatted)
FlattenItem::ComputedMember(computed_member) => {
let JsComputedMemberExpressionFields {
// Formatted as part of the previous item
object: _,
optional_chain_token,
l_brack_token,
member,
r_brack_token,
} = computed_member.as_fields();
write!(
f,
[
optional_chain_token.format(),
l_brack_token.format(),
member.format(),
r_brack_token.format(),
]
)
}
FlattenItem::Node(node, formatted) => {
std::write!(f, "{:?} {:?}", node.kind(), formatted)
FlattenItem::Node(node) => {
write!(f, [node.format()])
}
}
}
}

impl From<FlattenItem> for FormatElement {
fn from(flatten_item: FlattenItem) -> Self {
match flatten_item {
FlattenItem::StaticMember(_, formatted) => FormatElement::from_iter(formatted),
FlattenItem::CallExpression(_, formatted) => FormatElement::from_iter(formatted),
FlattenItem::ComputedExpression(_, formatted) => FormatElement::from_iter(formatted),
FlattenItem::Node(_, formatted) => formatted,
}
}
}
53 changes: 18 additions & 35 deletions crates/rome_js_formatter/src/utils/member_chain/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::prelude::*;
use crate::utils::member_chain::flatten_item::FlattenItem;
use crate::utils::member_chain::simple_argument::SimpleArgument;

use rome_formatter::{format, Buffer};
use rome_js_syntax::JsCallExpression;
use rome_rowan::{AstSeparatedList, SyntaxResult};
use std::mem;
Expand Down Expand Up @@ -93,43 +92,25 @@ impl Groups {
Ok(call_expressions_are_not_simple)
}

fn into_formatted_groups(self) -> Vec<FormatElement> {
self.groups
.into_iter()
.map(|group| {
FormatElement::from_iter(group.into_iter().map(|flatten_item| flatten_item.into()))
})
.collect()
}

/// Format groups on multiple lines
pub fn into_joined_hard_line_groups(self) -> FormatElement {
let elements = format!(
JsFormatContext::default(),
[format_once(|f| {
let formatted_groups = self.into_formatted_groups();

f.join_with(&hard_line_break())
.entries(
formatted_groups
.into_iter()
.map(|e| format_once(|f| f.write_element(e))),
)
.finish()
})]
)
.unwrap();

elements.into_format_element()
pub fn write_joined_with_hard_line_breaks(&self, f: &mut JsFormatter) -> FormatResult<()> {
f.join_with(hard_line_break())
.entries(
self.groups
.iter()
.map(|group| format_with(|f| f.join().entries(group.iter()).finish())),
)
.finish()
}

/// Creates two different versions of the formatted groups, one that goes in one line
/// and the other one that goes on multiple lines.
///
/// It's up to the printer to decide which one to use.
pub fn into_format_elements(self) -> FormatElement {
let formatted_groups = self.into_formatted_groups();
FormatElement::from_iter(formatted_groups)
pub fn write(&self, f: &mut JsFormatter) -> FormatResult<()> {
f.join()
.entries(self.groups.iter().flat_map(|group| group.iter()))
.finish()
}

/// Filters the stack of [FlattenItem] and return only the ones that
Expand Down Expand Up @@ -234,11 +215,13 @@ impl HeadGroup {
&self.items
}

pub fn into_format_element(self) -> FormatElement {
FormatElement::from_iter(self.items.into_iter().map(FlattenItem::into))
}

pub fn expand_group(&mut self, group: Vec<FlattenItem>) {
self.items.extend(group)
}
}

impl Format<JsFormatContext> for HeadGroup {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
f.join().entries(self.items.iter()).finish()
}
}
Loading

0 comments on commit f7c6e2d

Please sign in to comment.