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

Commit

Permalink
feat(rome_js_formatter): Implement the decorator formatting #4608
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Jun 28, 2023
1 parent 90111a0 commit 93f80fc
Show file tree
Hide file tree
Showing 15 changed files with 715 additions and 754 deletions.
9 changes: 5 additions & 4 deletions crates/rome_js_formatter/src/js/bindings/formal_parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ impl FormatNodeRule<JsFormalParameter> for FormatJsFormalParameter {
write![
f,
[
decorators.format(),
binding.format(),
question_mark_token.format(),
type_annotation.format()
Expand All @@ -40,10 +39,12 @@ impl FormatNodeRule<JsFormalParameter> for FormatJsFormalParameter {
should_hug_function_parameters(&parameters, f.comments()).unwrap_or(false)
});

if is_hug_parameter {
write![f, [content]]?;
if is_hug_parameter && decorators.is_empty() {
write![f, [decorators.format(), content]]?;
} else if decorators.is_empty() {
write![f, [decorators.format(), group(&content)]]?;
} else {
write![f, [group(&content)]]?;
write![f, [group(&decorators.format()), group(&content)]]?;
}

write![f, [FormatInitializerClause::new(initializer.as_ref())]]
Expand Down
10 changes: 1 addition & 9 deletions crates/rome_js_formatter/src/js/bindings/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,7 @@ impl Format<JsFormatContext> for FormatAnyJsParameters {
fn fmt(&self, f: &mut Formatter<JsFormatContext>) -> FormatResult<()> {
let list = self.list();

let has_any_decorated_parameter = list.iter().any(|node| match node {
Ok(node) => node.syntax().first_token().map_or(false, |token| {
token
.leading_trivia()
.pieces()
.any(|piece| piece.is_skipped())
}),
Err(_) => false,
});
let has_any_decorated_parameter = list.has_any_decorated_parameter();

let can_hug = should_hug_function_parameters(self, f.context().comments())?
&& !has_any_decorated_parameter;
Expand Down
58 changes: 36 additions & 22 deletions crates/rome_js_formatter/src/js/lists/decorator_list.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use crate::prelude::*;
use crate::utils::format_modifiers::should_expand_decorators;
use rome_formatter::write;
use rome_js_syntax::JsSyntaxKind::JS_CLASS_EXPRESSION;
use rome_js_syntax::JsSyntaxKind::{
JS_CLASS_EXPRESSION, JS_FORMAL_PARAMETER, JS_REST_PARAMETER, TS_PROPERTY_PARAMETER,
};
use rome_js_syntax::{
AnyJsDeclarationClause, AnyJsExportClause, AnyJsExportDefaultDeclaration, JsDecoratorList,
JsExport,
};
use rome_rowan::SyntaxNodeOptionExt;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatJsDecoratorList;
Expand Down Expand Up @@ -54,37 +58,47 @@ impl FormatRule<JsDecoratorList> for FormatJsDecoratorList {
join.finish()?;

write!(f, [hard_line_break()])
} else if matches!(
node.syntax().parent().map(|parent| parent.kind()),
Some(JS_CLASS_EXPRESSION)
) {
} else if matches!(node.syntax().parent().kind(), Some(JS_CLASS_EXPRESSION)) {
write!(f, [expand_parent()])?;
f.join_with(&soft_line_break_or_space())
.entries(node.iter().formatted())
.finish()?;

write!(f, [soft_line_break_or_space()])
} else {
// If the parent node is an export declaration and the decorator
// was written before the export, the export will be responsible
// for printing the decorators.
let export = node.syntax().grand_parent().and_then(|grand_parent| {
JsExport::cast_ref(&grand_parent)
.or_else(|| grand_parent.parent().and_then(JsExport::cast))
});
let is_export = export.is_some();

let has_decorators_before_export =
export.map_or(false, |export| !export.decorators().is_empty());
let is_parameter_decorators = matches!(
node.syntax().parent().kind(),
Some(JS_FORMAL_PARAMETER | JS_REST_PARAMETER | TS_PROPERTY_PARAMETER)
);

if has_decorators_before_export {
return Ok(());
}
if is_parameter_decorators {
let should_expand = should_expand_decorators(node);

if is_export {
write!(f, [hard_line_break()])?;
if should_expand {
write!(f, [expand_parent()])?;
}
} else {
write!(f, [expand_parent()])?;
// If the parent node is an export declaration and the decorator
// was written before the export, the export will be responsible
// for printing the decorators.
let export = node.syntax().grand_parent().and_then(|grand_parent| {
JsExport::cast_ref(&grand_parent)
.or_else(|| grand_parent.parent().and_then(JsExport::cast))
});
let is_export = export.is_some();

let has_decorators_before_export =
export.map_or(false, |export| !export.decorators().is_empty());

if has_decorators_before_export {
return Ok(());
}

if is_export {
write!(f, [hard_line_break()])?;
} else {
write!(f, [expand_parent()])?;
}
}

f.join_with(&soft_line_break_or_space())
Expand Down
12 changes: 12 additions & 0 deletions crates/rome_js_formatter/src/js/lists/parameter_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,18 @@ impl AnyJsParameterList {
}
})
}

/// This method checks if any parameters in the given list are decorated.
pub fn has_any_decorated_parameter(&self) -> bool {
self.iter().any(|parameter| {
parameter.map_or(false, |parameter| match parameter {
AnyParameter::AnyJsConstructorParameter(parameter) => {
parameter.has_any_decorated_parameter()
}
AnyParameter::AnyJsParameter(parameter) => parameter.has_any_decorated_parameter(),
})
})
}
}

pub(crate) enum AnyJsParameterListNodeIter {
Expand Down
24 changes: 16 additions & 8 deletions crates/rome_js_formatter/src/ts/bindings/property_parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ impl FormatNodeRule<TsPropertyParameter> for FormatTsPropertyParameter {
formal_parameter,
} = node.as_fields();

write![
f,
[
decorators.format(),
modifiers.format(),
space(),
formal_parameter.format()
let content = format_with(|f| {
write![
f,
[
decorators.format(),
modifiers.format(),
space(),
formal_parameter.format()
]
]
]
});

if decorators.is_empty() {
write![f, [content]]
} else {
write![f, [group(&content)]]
}
}
}
2 changes: 1 addition & 1 deletion crates/rome_js_formatter/src/utils/format_modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ where
}

/// This function expands decorators enclosing a group if there is a newline between decorators or after the last decorator.
fn should_expand_decorators<List, Node>(list: &List) -> bool
pub(crate) fn should_expand_decorators<List, Node>(list: &List) -> bool
where
Node: AstNode<Language = JsLanguage>,
List: AstNodeList<Language = JsLanguage, Node = Node>,
Expand Down
6 changes: 5 additions & 1 deletion crates/rome_js_formatter/tests/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ impl TestFormatLanguage for JsTestFormatLanguage {
type FormatLanguage = JsFormatLanguage;

fn parse(&self, text: &str) -> AnyParse {
let parse = parse(text, self.source_type, JsParserOptions::default());
let parse = parse(
text,
self.source_type,
JsParserOptions::default().with_parse_class_parameter_decorators(),
);

AnyParse::new(
parse.syntax().as_send().unwrap(),
Expand Down
18 changes: 14 additions & 4 deletions crates/rome_js_formatter/tests/quick_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ mod language {
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
let src = r#"
class A { constructor(private x, protected y, public z) {} }
class B { constructor(readonly w, private readonly x, protected readonly y, public readonly z) {} }
class C { constructor(private x: string, readonly y?, z = "default", ...rest) {} }
class Class3 {
method(
@dec @dec
{ prop1_1, prop1_2 }: Type,
) {
doSomething();
}
}
"#;
let syntax = JsFileSource::tsx();
let tree = parse(src, syntax, JsParserOptions::default());
let tree = parse(
src,
syntax,
JsParserOptions::default().with_parse_class_parameter_decorators(),
);
dbg!(tree.syntax());
let options = JsFormatOptions::new(syntax)
.with_semicolons(Semicolons::AsNeeded)
.with_quote_style(QuoteStyle::Double)
Expand Down

This file was deleted.

Loading

0 comments on commit 93f80fc

Please sign in to comment.