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

refactor(formatter): Introduce write, format, and format_args macros #2634

Merged
merged 38 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5a3e8df
refactor(formatter): `format` and `write` macros
MichaReiser May 30, 2022
1efbafb
refactor(formatter): Make Format `Options` a type parameter
MichaReiser May 30, 2022
9fe5507
refactor(formatter): Rewrite formatters
MichaReiser May 31, 2022
b98d4c2
refactor(formatter): Clippy
MichaReiser May 31, 2022
278f082
refactor(formatter): Fix memory layout issue
MichaReiser May 31, 2022
4c555f4
refactor(formatter): Fix formatting issues
MichaReiser May 31, 2022
994f5ae
Document `rome_formatter`
MichaReiser May 31, 2022
85c7c8c
Document macros, store owned content in builders, implement Debug
MichaReiser Jun 1, 2022
f1fa424
Update codegen to use `JsFormatContext`
MichaReiser Jun 1, 2022
f7c7e50
Formatting
MichaReiser Jun 1, 2022
1f4401d
Move JS builders to `builders.rs` (except separated)
MichaReiser Jun 1, 2022
acd1266
Move `format_separated` to its own file
MichaReiser Jun 1, 2022
8d4fddb
Add support for best fitting
MichaReiser Jun 1, 2022
6e0e0b4
Refine builders
MichaReiser Jun 1, 2022
68e31fe
Update formatter codegen
MichaReiser Jun 1, 2022
85e950d
Fix unknown assigment formatting
MichaReiser Jun 1, 2022
a3364c9
Apply format leading trivia optimisation
MichaReiser Jun 1, 2022
aa9c728
Merge branch 'main' into refactor/format-and-write-macros
MichaReiser Jun 2, 2022
aef0ef5
Clippy
MichaReiser Jun 2, 2022
2ac2775
Remove unnecessary clone
MichaReiser Jun 2, 2022
8ac52ad
Fix out of order printing for union/intersection type
MichaReiser Jun 2, 2022
1edde7b
Address first batch of review comments
MichaReiser Jun 2, 2022
5ee400b
Add debuging macro
MichaReiser Jun 2, 2022
9db75c2
Use alternative printing in debug
MichaReiser Jun 2, 2022
076b2ae
Small cleanups
MichaReiser Jun 3, 2022
988a8d3
Code review comments
MichaReiser Jun 3, 2022
4ea0982
Added a test that verifies that `format_once` panics if formatted twice
MichaReiser Jun 3, 2022
92d27bd
Some more options to context renaming
MichaReiser Jun 3, 2022
486a08c
Update documentation
MichaReiser Jun 3, 2022
f4185ab
Merge branch 'main' into refactor/format-and-write-macros
MichaReiser Jun 3, 2022
ae2c61c
Change builders to accept `&dyn Format` to improve compile time
Jun 4, 2022
6007d32
Rename `format_fields` to `fmt_fields`
Jun 4, 2022
2570732
Revert submodule changes
Jun 4, 2022
c6183b3
More documentation & use list formatting logic rather than re-impleme…
MichaReiser Jun 4, 2022
1196545
Simplify setter formatting
MichaReiser Jun 4, 2022
e45f7b4
refactor(formatter): Use argument instead of `dyn Format`
MichaReiser Jun 6, 2022
de2c274
Merge branch 'main' into refactor/format-and-write-macros
MichaReiser Jun 6, 2022
00e8740
Clippy
MichaReiser Jun 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
154 changes: 154 additions & 0 deletions crates/rome_formatter/src/arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use super::{Buffer, Format, Formatter};
use crate::FormatResult;
use std::ffi::c_void;
use std::marker::PhantomData;

/// Mono-morphed type to format an object. Used by the [rome_formatter::format], [rome_formatter::format_args], and
/// [rome_formatter::write] macros.
///
/// This struct is similar to a dynamic dispatch (using `dyn Format`) because it stores a pointer to the value.
/// However, it doesn't store the pointer to `dyn Format`'s vtable, instead it statically resolves the function
/// pointer of `Format::format` and stores it in `formatter`.
pub struct Argument<'fmt, Context> {
/// The value to format stored as a raw pointer where `lifetime` stores the value's lifetime.
value: *const c_void,
ematipico marked this conversation as resolved.
Show resolved Hide resolved

/// Stores the lifetime of the value. To get the most out of our dear borrow checker.
lifetime: PhantomData<&'fmt ()>,

/// The function pointer to `value`'s `Format::format` method
formatter: fn(*const c_void, &mut Formatter<'_, Context>) -> FormatResult<()>,
ematipico marked this conversation as resolved.
Show resolved Hide resolved
}

impl<Context> Clone for Argument<'_, Context> {
fn clone(&self) -> Self {
*self
}
}
impl<Context> Copy for Argument<'_, Context> {}

impl<'fmt, Context> Argument<'fmt, Context> {
/// Called by the [rome_formatter::format_args] macro. Creates a mono-morphed value for formatting
/// an object.
#[doc(hidden)]
#[inline]
pub fn new<F: Format<Context>>(value: &'fmt F) -> Self {
fn formatter<F: Format<Context>, Context>(
ptr: *const c_void,
fmt: &mut Formatter<Context>,
) -> FormatResult<()> {
// SAFETY: Safe because the 'fmt lifetime is captured by the 'lifetime' field.
F::fmt(unsafe { &*(ptr as *const F) }, fmt)
}

Self {
value: value as *const F as *const c_void,
ematipico marked this conversation as resolved.
Show resolved Hide resolved
lifetime: PhantomData,
formatter: formatter::<F, Context>,
}
}

/// Formats the value stored by this argument using the given formatter.
#[inline]
pub(super) fn format(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
(self.formatter)(self.value, f)
MichaReiser marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Sequence of objects that should be formatted in the specified order.
///
/// The [`format_args!`] macro will safely create an instance of this structure.
///
/// You can use the `Arguments<a>` that [`format_args!]` return in `Format` context as seen below.
/// It will call the `format` function for every of it's objects.
///
/// ```rust
/// use rome_formatter::prelude::*;
/// use rome_formatter::{format, format_args};
///
/// let formatted = format!(SimpleFormatContext::default(), [
/// format_args!(token("a"), space_token(), token("b"))
/// ]).unwrap();
///
/// assert_eq!("a b", formatted.print().as_code());
/// ```
pub struct Arguments<'fmt, Context>(pub &'fmt [Argument<'fmt, Context>]);

impl<'fmt, Context> Arguments<'fmt, Context> {
#[doc(hidden)]
#[inline]
pub fn new(arguments: &'fmt [Argument<'fmt, Context>]) -> Self {
Self(arguments)
}

/// Returns the arguments
#[inline]
pub(super) fn items(&self) -> &'fmt [Argument<'fmt, Context>] {
self.0
}
}

impl<Context> Copy for Arguments<'_, Context> {}

impl<Context> Clone for Arguments<'_, Context> {
fn clone(&self) -> Self {
Self(self.0)
}
}

impl<Context> Format<Context> for Arguments<'_, Context> {
#[inline]
fn fmt(&self, formatter: &mut Formatter<Context>) -> FormatResult<()> {
formatter.write_fmt(*self)
}
}

impl<Context> std::fmt::Debug for Arguments<'_, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Arguments[...]")
}
}

impl<'fmt, Context> From<&'fmt Argument<'fmt, Context>> for Arguments<'fmt, Context> {
fn from(argument: &'fmt Argument<'fmt, Context>) -> Self {
Arguments::new(std::slice::from_ref(argument))
}
}

#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::{format_args, write, FormatState, VecBuffer};

#[test]
fn test_nesting() {
ematipico marked this conversation as resolved.
Show resolved Hide resolved
let mut context = FormatState::new(());
let mut buffer = VecBuffer::new(&mut context);

write!(
&mut buffer,
[
token("function"),
space_token(),
token("a"),
space_token(),
group_elements(&format_args!(token("("), token(")")))
]
)
.unwrap();

assert_eq!(
buffer.into_element(),
FormatElement::List(List::new(vec![
FormatElement::Token(Token::Static { text: "function" }),
FormatElement::Space,
FormatElement::Token(Token::Static { text: "a" }),
FormatElement::Space,
FormatElement::Group(Group::new(FormatElement::List(List::new(vec![
FormatElement::Token(Token::Static { text: "(" }),
FormatElement::Token(Token::Static { text: ")" }),
]))))
]))
);
}
}
Loading