Skip to content

Commit

Permalink
feat(rome_js_formatter): TS Intersection & Union types rome#3162
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Sep 12, 2022
1 parent 28e5351 commit 088041e
Show file tree
Hide file tree
Showing 25 changed files with 1,091 additions and 1,531 deletions.
6 changes: 1 addition & 5 deletions crates/rome_js_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,11 +750,7 @@ function() {
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
let src = r#"
type Example = {
[A in B]: T;
} & {
[A in B]: T;
};
type C = B & (C | A) & B;
"#;
let syntax = SourceType::tsx();
Expand Down
7 changes: 2 additions & 5 deletions crates/rome_js_formatter/src/ts/expressions/type_arguments.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::utils::should_hug_type;
use crate::{prelude::*, utils::is_object_like_type};
use rome_formatter::write;
use rome_js_syntax::{
JsAnyExpression, JsSyntaxKind, JsVariableDeclarator, TsType, TsTypeArguments,
Expand Down Expand Up @@ -28,10 +28,7 @@ impl FormatNodeRule<TsTypeArguments> for FormatTsTypeArguments {
let first_argument = first_argument?;

// first argument is not mapped type or object type
if !matches!(
first_argument,
TsType::TsObjectType(_) | TsType::TsMappedType(_)
) {
if !is_object_like_type(&first_argument) {
// we then go up until we can find a potential type annotation,
// meaning four levels up
let maybe_type_annotation = first_argument.syntax().ancestors().nth(4);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{prelude::*, utils::is_object_like_type};
use rome_formatter::{format_args, write};
use rome_js_syntax::{TsIntersectionTypeElementList, TsType};
use rome_js_syntax::TsIntersectionTypeElementList;
use rome_rowan::AstSeparatedList;

#[derive(Debug, Clone, Default)]
Expand All @@ -19,8 +19,7 @@ impl FormatRule<TsIntersectionTypeElementList> for FormatTsIntersectionTypeEleme
for (index, element) in node.elements().enumerate() {
let node = element.node()?;

let is_object_type_like =
matches!(node, TsType::TsMappedType(_) | TsType::TsObjectType(_));
let is_object_type_like = is_object_like_type(node);

// always inline first element
if index == 0 {
Expand Down
51 changes: 30 additions & 21 deletions crates/rome_js_formatter/src/ts/lists/union_type_variant_list.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::prelude::*;
use crate::utils::should_hug_type;
use rome_formatter::write;
use rome_js_syntax::{JsLanguage, JsSyntaxKind, TsType, TsUnionTypeVariantList};
use rome_js_syntax::{JsLanguage, TsType, TsUnionTypeVariantList};
use rome_rowan::{AstSeparatedElement, AstSeparatedList};

#[derive(Debug, Clone, Default)]
Expand All @@ -10,15 +11,27 @@ impl FormatRule<TsUnionTypeVariantList> for FormatTsUnionTypeVariantList {
type Context = JsFormatContext;

fn fmt(&self, node: &TsUnionTypeVariantList, f: &mut JsFormatter) -> FormatResult<()> {
// ```ts
// {
// a: string
// } | null | void
// ```
// should be inlined and not be printed in the multi-line variant
let should_hug = node
.parent::<TsType>()
.as_ref()
.map_or(false, should_hug_type);

let last_index = node.len().saturating_sub(1);

f.join()
f.join_with(space())
.entries(
node.elements()
.enumerate()
.map(|(index, item)| FormatTypeVariant {
last: index == last_index,
element: item,
should_hug,
}),
)
.finish()
Expand All @@ -27,34 +40,30 @@ impl FormatRule<TsUnionTypeVariantList> for FormatTsUnionTypeVariantList {

pub struct FormatTypeVariant {
pub last: bool,
pub should_hug: bool,
pub element: AstSeparatedElement<JsLanguage, TsType>,
}

impl Format<JsFormatContext> for FormatTypeVariant {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
write!(f, [group(&self.element.node().format())])?;

let separator = self.element.trailing_separator()?;

match separator {
Some(token) => {
if self.last {
write!(f, [format_removed(token)])?;
if self.should_hug {
write!(f, [self.element.node().format()])?;
} else {
write!(f, [align(2, &self.element.node().format())])?;
}

if let Some(token) = separator {
if self.last {
write!(f, [format_removed(token)])?;
} else {
if self.should_hug {
write!(f, [space()])?;
} else {
write![f, [soft_line_break_or_space(), token.format(), space()]]?;
}
}
None => {
if !self.last {
write![
f,
[
soft_line_break_or_space(),
format_inserted(JsSyntaxKind::PIPE),
space()
]
]?;
write!(f, [soft_line_break_or_space()])?;
}
write![f, [token.format()]]?;
}
}

Expand Down
67 changes: 5 additions & 62 deletions crates/rome_js_formatter/src/ts/types/intersection_type.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use crate::prelude::*;

use crate::parentheses::{
is_in_many_type_union_or_intersection_list, operator_type_or_higher_needs_parens,
NeedsParentheses,
use crate::parentheses::NeedsParentheses;
use crate::utils::{
union_or_intersection_type_needs_parentheses, FormatTypeMemberSeparator,
TsIntersectionOrUnionTypeList,
};
use crate::utils::FormatTypeMemberSeparator;
use rome_formatter::{format_args, write};
use rome_js_syntax::{
JsSyntaxKind, JsSyntaxNode, TsIntersectionTypeElementList, TsIntersectionTypeFields,
TsUnionTypeVariantList,
};
use rome_js_syntax::{JsSyntaxToken, TsIntersectionType};
use rome_js_syntax::{JsSyntaxNode, TsIntersectionType, TsIntersectionTypeFields};

#[derive(Debug, Clone, Default)]
pub struct FormatTsIntersectionType;
Expand All @@ -35,28 +31,6 @@ impl FormatNodeRule<TsIntersectionType> for FormatTsIntersectionType {
}
}

pub struct FormatTypeSetLeadingSeparator<'a> {
pub(crate) separator: JsSyntaxKind,
pub(crate) leading_separator: Option<&'a JsSyntaxToken>,
}

impl Format<JsFormatContext> for FormatTypeSetLeadingSeparator<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
match &self.leading_separator {
Some(token) => {
format_only_if_breaks(token, &format_args!(token.format(), space())).fmt(f)
}
None => write!(
f,
[if_group_breaks(&format_args![
format_inserted(self.separator),
space()
])]
),
}
}
}

impl NeedsParentheses for TsIntersectionType {
fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
union_or_intersection_type_needs_parentheses(
Expand All @@ -67,37 +41,6 @@ impl NeedsParentheses for TsIntersectionType {
}
}

pub(super) fn union_or_intersection_type_needs_parentheses(
node: &JsSyntaxNode,
parent: &JsSyntaxNode,
types: &TsIntersectionOrUnionTypeList,
) -> bool {
debug_assert!(matches!(
node.kind(),
JsSyntaxKind::TS_INTERSECTION_TYPE | JsSyntaxKind::TS_UNION_TYPE
));

if is_in_many_type_union_or_intersection_list(node, parent) {
types.len() > 1
} else {
operator_type_or_higher_needs_parens(node, parent)
}
}

pub(super) enum TsIntersectionOrUnionTypeList {
TsIntersectionTypeElementList(TsIntersectionTypeElementList),
TsUnionTypeVariantList(TsUnionTypeVariantList),
}

impl TsIntersectionOrUnionTypeList {
fn len(&self) -> usize {
match self {
TsIntersectionOrUnionTypeList::TsIntersectionTypeElementList(list) => list.len(),
TsIntersectionOrUnionTypeList::TsUnionTypeVariantList(list) => list.len(),
}
}
}

#[cfg(test)]
mod tests {

Expand Down
Loading

0 comments on commit 088041e

Please sign in to comment.