-
Notifications
You must be signed in to change notification settings - Fork 664
refactor(rome_js_formatter): Move node formatting into its own trait #2476
Conversation
use rome_js_syntax::JsAnyArrayAssignmentPatternElement; | ||
impl ToFormatElement for JsAnyArrayAssignmentPatternElement { | ||
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> { | ||
impl Format for JsAnyArrayAssignmentPatternElement { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unions don't need to implement FormatNode
because the "node" logic will be handled by the inner node.
use rome_js_syntax::JsArrayAssignmentPatternElementList; | ||
|
||
impl ToFormatElement for JsArrayAssignmentPatternElementList { | ||
fn to_format_element(&self, formatter: &Formatter) -> FormatResult<FormatElement> { | ||
impl Format for JsArrayAssignmentPatternElementList { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's fine to not implement FormatNode
for lists because suppressions either apply to the whole wrapper node or a specific element:
// rome-ingore
[ a, b, c] // ignores whole array expression
[
// rome-ignore
a, // Ignores first node
b,
c
]
crates/rome_js_formatter/src/lib.rs
Outdated
impl<T> Format for &T | ||
where | ||
T: Format + ?Sized, | ||
{ | ||
fn format(&self, formatter: &Formatter) -> FormatResult<FormatElement> { | ||
Format::format(&**self, formatter) | ||
} | ||
} | ||
|
||
impl<T> Format for &mut T | ||
where | ||
T: ?Sized + Format, | ||
{ | ||
fn format(&self, formatter: &Formatter) -> FormatResult<FormatElement> { | ||
Format::format(&**self, formatter) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + Format> Format for Ref<'_, T> { | ||
fn format(&self, f: &Formatter) -> FormatResult<FormatElement> { | ||
Format::format(&**self, f) | ||
} | ||
} | ||
|
||
impl<T: ?Sized + Format> Format for RefMut<'_, T> { | ||
fn format(&self, f: &Formatter) -> FormatResult<FormatElement> { | ||
Format::format(&*(self.deref()), f) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan to introduce a format_args[node1, node2]
macro that accepts anything implementing Format
. This implementations will be necessary for such a trait to work
Deploying with Cloudflare Pages
|
451029e
to
ce5b9c0
Compare
ce5b9c0
to
dcf87f3
Compare
Parser conformance results on ubuntu-latestjs/262
jsx/babel
ts/babel
ts/microsoft
|
dcf87f3
to
64d9426
Compare
impl<F: FormatTokenAndNode> FormatTokenAndNode for SyntaxResult<F> { | ||
fn format_with<With, WithResult>( | ||
&self, | ||
formatter: &Formatter, | ||
with: With, | ||
) -> FormatResult<FormatElement> | ||
where | ||
With: FnOnce(FormatElement) -> WithResult, | ||
WithResult: IntoFormatResult, | ||
{ | ||
match self { | ||
Ok(token) => with(token.format(formatter)?).into_format_result(), | ||
Err(err) => Err(err.into()), | ||
} | ||
} | ||
} | ||
|
||
impl FormatTokenAndNode for JsSyntaxToken { | ||
fn format_with<With, WithResult>( | ||
&self, | ||
formatter: &Formatter, | ||
with: With, | ||
) -> FormatResult<FormatElement> | ||
where | ||
With: FnOnce(FormatElement) -> WithResult, | ||
WithResult: IntoFormatResult, | ||
{ | ||
cfg_if::cfg_if! { | ||
if #[cfg(debug_assertions)] { | ||
assert!(formatter.printed_tokens.borrow_mut().insert(self.clone()), "You tried to print the token '{:?}' twice, and this is not valid.", self); | ||
} | ||
} | ||
|
||
with(format_elements![ | ||
formatter.print_leading_trivia(self, TriviaPrintMode::Full), | ||
Token::from(self), | ||
formatter.print_trailing_trivia(self), | ||
]) | ||
.into_format_result() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to Format
and FormatNode
!bench_formatter |
Today, the formatting that must happen for each JS node happens inside `Formatter.format_syntax_node` and the `formatter_traits`. This PR moves the common logic applied by all nodes and tokens into the `ToFormatElement` implementation and renames it to `Format`. * Rename `ToFormatElement` to `Format` and `to_format_element` to `format` * Implement `Format` for `JsSyntaxToken`. CSS, JSON etc. can implement their own generic token formatting logic * Introduce a new `FormatNode` trait (language specific) that implements the formatting of a node and calls into `format_fields` that is implemented for each field. * Make the `FormatterTraits` node and language independent. Rename to `FormatTraits`. `Format`, `FormatterTraits`, `FormatResult`, and `FormatError` are now all language independent. However, some still depend on the `Formatter`. The next step is to move many of the `Formatter`'s helper function out of the Formatter so that it becomes language independent.
64d9426
to
ef1ad81
Compare
Formatter Benchmark Results
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One step forward to make things more generic! I added some feedback and few questions where I have some doubts!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! Thank you for addressing my questions! This is great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still wonder if it would have been possible this trait hierarchy using only generics somehow instead of relying on codegen, but until specialization gets stabilized (and perhaps even after) I think this is probably the only way this can be expressed with the Rust type system
I would love that if you find a way. I even tried to go fancy (at least as far as I'm capable of) and played around with |
Today, the formatting that must happen for each JS node happens inside
Formatter.format_syntax_node
and theformatter_traits
.This PR moves the common logic applied by all nodes and tokens into the
ToFormatElement
implementation and renames it toFormat
.ToFormatElement
toFormat
andto_format_element
toformat
Format
forJsSyntaxToken
. CSS, JSON etc. can implement their own generic token formatting logicFormatNode
trait (language specific) that implements the formatting of a node and calls intoformat_fields
that is implemented for each field.FormatterTraits
node and language independent. Rename toFormatTraits
.Format
,FormatterTraits
,FormatResult
, andFormatError
are now all language independent. However, some still depend on theFormatter
.The next step is to move many of the
Formatter
's helper function out of the Formatter so that it becomes language independent.How to review
rome_js_formatter/lib
andformat_traits
Test Plan
cargo test
Deleted a union, a list, and a node formatter implementation and regenerated the formatter files. Verified that the generated files have no compile errors.