feat(rome_formatter): predictable order of modifiers
MODIFY
crates/rome_formatter/src/js/lists/method_modifier_list.rs
crates/rome_formatter/src/js/lists/method_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::JsMethodModifierList;
-use rslint_parser::AstNodeList;
impl ToFormatElement for JsMethodModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/js/lists/property_modifier_list.rs
crates/rome_formatter/src/js/lists/property_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::JsPropertyModifierList;
-use rslint_parser::AstNodeList;
impl ToFormatElement for JsPropertyModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/lib.rs
crates/rome_formatter/src/lib.rs
@@ -600,9 +600,12 @@ mod test {
use rslint_parser::{parse, SourceType};
#[test]
+ #[ignore]
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
- let src = r#"let g = [[], [0, 1], [0, 1] ];
+ let src = r#"abstract class A {
+ declare private name?: string;
+}
"#;
let syntax = SourceType::ts();
let tree = parse(src, 0, syntax);

MODIFY
crates/rome_formatter/src/ts/lists/index_signature_modifier_list.rs
crates/rome_formatter/src/ts/lists/index_signature_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::TsIndexSignatureModifierList;
-use rslint_parser::AstNodeList;
impl ToFormatElement for TsIndexSignatureModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/ts/lists/method_signature_modifier_list.rs
crates/rome_formatter/src/ts/lists/method_signature_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::TsMethodSignatureModifierList;
-use rslint_parser::AstNodeList;
impl ToFormatElement for TsMethodSignatureModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/ts/lists/property_parameter_modifier_list.rs
crates/rome_formatter/src/ts/lists/property_parameter_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::TsPropertyParameterModifierList;
-use rslint_parser::AstNodeList;
impl ToFormatElement for TsPropertyParameterModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/ts/lists/property_signature_modifier_list.rs
crates/rome_formatter/src/ts/lists/property_signature_modifier_list.rs
+use crate::utils::sort_modifiers_by_precedence;
use crate::{join_elements, space_token, FormatElement, FormatResult, Formatter, ToFormatElement};
use rslint_parser::ast::TsPropertySignatureModifierList;
-use rslint_parser::AstNodeList;
+
impl ToFormatElement for TsPropertySignatureModifierList {
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> {
Ok(join_elements(
space_token(),
- formatter.format_nodes(self.iter())?,
+ formatter.format_nodes(sort_modifiers_by_precedence(self))?,
))
}
}

MODIFY
crates/rome_formatter/src/utils/mod.rs
crates/rome_formatter/src/utils/mod.rs
@@ -7,8 +7,8 @@ use crate::{
FormatElement, FormatResult, Formatter,
};
pub use call_expression::format_call_expression;
-use rslint_parser::ast::{JsAnyRoot, JsAnyStatement, JsInitializerClause};
-use rslint_parser::{AstNode, SyntaxNode, SyntaxNodeExt, SyntaxToken};
+use rslint_parser::ast::{JsAnyRoot, JsAnyStatement, JsInitializerClause, Modifiers};
+use rslint_parser::{AstNode, AstNodeList, SyntaxNode, SyntaxNodeExt, SyntaxToken};
pub(crate) use simple::*;
@@ -349,3 +349,17 @@ mod tests {
);
}
}
+
+/// This function consumes a list of modifiers and applies a predictable sorting.
+pub(crate) fn sort_modifiers_by_precedence<List, Node>(list: &List) -> Vec<Node>
+where
+ Node: AstNode + Clone,
+ List: AstNodeList<Node>,
+ Modifiers: for<'a> From<&'a Node>,
+{
+ let mut nodes_and_modifiers = list.iter().collect::<Vec<Node>>();
+
+ nodes_and_modifiers.sort_unstable_by_key(|node| Modifiers::from(node));
+
+ nodes_and_modifiers
+}

MODIFY
crates/rome_formatter/tests/specs/ts/declaration/class.ts
crates/rome_formatter/tests/specs/ts/declaration/class.ts
@@ -48,3 +48,13 @@ abstract class AbstractMembers {
abstract get my_name();
abstract set my_name(val);
}
+
+
+abstract class Test1 {
+ private c?: string;
+ private d?: string;
+ private readonly e: string;
+ private readonly f: string;
+ protected abstract readonly g: string;
+ protected readonly abstract h: string;
+}
\ No newline at end of file

MODIFY
crates/rome_formatter/tests/specs/ts/declaration/class.ts.snap
crates/rome_formatter/tests/specs/ts/declaration/class.ts.snap
@@ -55,6 +55,16 @@ abstract class AbstractMembers {
abstract get my_name();
abstract set my_name(val);
}
+
+
+abstract class Test1 {
+ private c?: string;
+ private d?: string;
+ private readonly e: string;
+ private readonly f: string;
+ protected abstract readonly g: string;
+ protected readonly abstract h: string;
+}
=============================
# Outputs
## Output 1
@@ -67,7 +77,7 @@ class Test {
constructor(a: string) {}
- declare private readonly test?: string;
+ private declare readonly test?: string;
readonly test2!: string;
display2(): void {
@@ -115,3 +125,12 @@ abstract class AbstractMembers {
abstract set my_name(val);
}
+abstract class Test1 {
+ private c?: string;
+ private d?: string;
+ private readonly e: string;
+ private readonly f: string;
+ protected abstract readonly g: string;
+ protected abstract readonly h: string;
+}
+

MODIFY
crates/rslint_parser/src/ast.rs
crates/rslint_parser/src/ast.rs
#[macro_use]
mod expr_ext;
mod generated;
+mod modifier_ext;
mod stmt_ext;
mod union_ext;
@@ -16,9 +17,9 @@ use std::iter::FusedIterator;
use std::marker::PhantomData;
use thiserror::Error;
-pub use self::{expr_ext::*, generated::nodes::*, stmt_ext::*};
-
pub(crate) use self::generated::syntax_factory::JsSyntaxFactory;
+pub use self::modifier_ext::Modifiers;
+pub use self::{expr_ext::*, generated::nodes::*, stmt_ext::*};
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly

ADD
crates/rslint_parser/src/ast/modifier_ext.rs
+use crate::ast::{
+ JsAnyMethodModifier, JsAnyPropertyModifier, TsAnyIndexSignatureModifier,
+ TsAnyMethodSignatureModifier, TsAnyPropertyParameterModifier, TsAnyPropertySignatureModifier,
+};
+
+/// Helpful data structure to make the order modifiers predictable inside the formatter
+#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
+#[repr(u8)]
+pub enum Modifiers {
+ // modifiers must be sorted by precedence.
+ Accessibility,
+ Declare,
+ Static,
+ Abstract,
+ Override,
+ Readonly,
+}
+
+impl From<&TsAnyIndexSignatureModifier> for Modifiers {
+ fn from(modifier: &TsAnyIndexSignatureModifier) -> Self {
+ match modifier {
+ TsAnyIndexSignatureModifier::JsStaticModifier(_) => Modifiers::Static,
+ TsAnyIndexSignatureModifier::TsReadonlyModifier(_) => Modifiers::Readonly,
+ }
+ }
+}
+
+impl From<&JsAnyMethodModifier> for Modifiers {
+ fn from(modifier: &JsAnyMethodModifier) -> Self {
+ match modifier {
+ JsAnyMethodModifier::JsStaticModifier(_) => Modifiers::Static,
+ JsAnyMethodModifier::TsAccessibilityModifier(_) => Modifiers::Accessibility,
+ JsAnyMethodModifier::TsOverrideModifier(_) => Modifiers::Override,
+ }
+ }
+}
+
+impl From<&TsAnyMethodSignatureModifier> for Modifiers {
+ fn from(modifier: &TsAnyMethodSignatureModifier) -> Self {
+ match modifier {
+ TsAnyMethodSignatureModifier::JsStaticModifier(_) => Modifiers::Static,
+ TsAnyMethodSignatureModifier::TsAbstractModifier(_) => Modifiers::Abstract,
+ TsAnyMethodSignatureModifier::TsAccessibilityModifier(_) => Modifiers::Accessibility,
+ TsAnyMethodSignatureModifier::TsOverrideModifier(_) => Modifiers::Override,
+ }
+ }
+}
+
+impl From<&JsAnyPropertyModifier> for Modifiers {
+ fn from(modifier: &JsAnyPropertyModifier) -> Self {
+ match modifier {
+ JsAnyPropertyModifier::JsStaticModifier(_) => Modifiers::Static,
+ JsAnyPropertyModifier::TsAccessibilityModifier(_) => Modifiers::Accessibility,
+ JsAnyPropertyModifier::TsOverrideModifier(_) => Modifiers::Override,
+ JsAnyPropertyModifier::TsReadonlyModifier(_) => Modifiers::Readonly,
+ }
+ }
+}
+
+impl From<&TsAnyPropertyParameterModifier> for Modifiers {
+ fn from(modifier: &TsAnyPropertyParameterModifier) -> Self {
+ match modifier {
+ TsAnyPropertyParameterModifier::TsAccessibilityModifier(_) => Modifiers::Accessibility,
+ TsAnyPropertyParameterModifier::TsOverrideModifier(_) => Modifiers::Override,
+ TsAnyPropertyParameterModifier::TsReadonlyModifier(_) => Modifiers::Readonly,
+ }
+ }
+}
+
+impl From<&TsAnyPropertySignatureModifier> for Modifiers {
+ fn from(modifier: &TsAnyPropertySignatureModifier) -> Self {
+ match modifier {
+ TsAnyPropertySignatureModifier::TsAccessibilityModifier(_) => Modifiers::Accessibility,
+ TsAnyPropertySignatureModifier::TsDeclareModifier(_) => Modifiers::Declare,
+ TsAnyPropertySignatureModifier::JsStaticModifier(_) => Modifiers::Static,
+ TsAnyPropertySignatureModifier::TsAbstractModifier(_) => Modifiers::Abstract,
+ TsAnyPropertySignatureModifier::TsOverrideModifier(_) => Modifiers::Override,
+ TsAnyPropertySignatureModifier::TsReadonlyModifier(_) => Modifiers::Readonly,
+ }
+ }
+}

feat(rome_formatter): predictable order of modifiers (#2196)