This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(formatter): Introduce
write
, format
, and format_args
m…
…acros (#2634) > tldr: Rust's allocation free `format`, `write` and `format_args` for Rome formatter! First of all, I'm sorry for this massive PR. The motivation behind this PR is to change our formatting to work from left to right. Something that may become useful when formatting comments. The current formatter formats the most-grouped elements first rather than left to right, mainly because all IR elements like `group_elements`, `fill` etc. accept a `FormatElement` as a parameter and not a `Format` implementation. The other motivation behind this PR is to make all formatting macros allocation free compared to the current `formatted!` and `format_elements` macro that requires at least one allocation (except the compiler optimises it away). This PR enforces left to right formatting by changing Format's signature from: ```rust fn format(&self, f: &Formatter<JsFormatOptions>) -> FormatResult<FormatElement> ``` to ```rust fn format(&self, f: &mut Formatter<JsFormatOptions>) -> FormatResult() ``` The main change is that `format` no longer returns the formatted result but instead writes it into the `Formatter`, similar to Rust's `Debug` and `Display` trait with the `write` and `format_args` macros. The fact that `format` now writes to a shared `FormatElement` buffer enforces format rules to write the element in order or they will appear out of order in the output. Second, the PR changes all builders (`group_elements`, `fill`, `space`) to return objects that implement `Format` rather than `FormatElement`s directly. This decouples the formatting DSL from the IR used by the printer. The other added benefit is that builders that accept some inner content no longer accept `FormatElement` but instead accept any object implementing `Format`. This should remove the need for many `formatted!` calls that were necessary just because some helper needs a `FormatElement` because it's the least common denominator. OK, but how do I write my formatting logic now: ```rust impl FormatNodeFields<JsFunctionBody> for FormatNodeRule<JsFunctionBody> { fn format_fields( node: &JsFunctionBody, f: &mut Formatter<JsFormatOptions>, ) -> FormatResult<()> { let JsFunctionBodyFields { l_curly_token, directives, statements, r_curly_token, } = node.as_fields(); let format_statements = format_with(|f| { let mut join = f.join_nodes_with_hardline(); for stmt in &statements { join.entry(stmt.syntax(), &stmt.format_or_verbatim()); } join.finish() }); write!( f, [f.delimited( &l_curly_token?, &format_args![directives.format(), format_statements], &r_curly_token?, ) .block_indent()] ) } } ``` The main differences are * You call `write!(f, [])` instead of `formatted` if you want to write something to the document. * You use `format_args` if you want to pass multiple `Format` objects to a helper like `group_elements`. * The formatter now exposes helpers like `f.join()`, `f.join_with()`, `f.fill()` and `f.join_nodes_with_softline_break` etc to format a sequence of objects Part of #2571
- Loading branch information