feat: format macro
MODIFY
.cargo/config.toml
.cargo/config.toml
[alias]
xtask = "run --package xtask --bin xtask --"
+lint = "clippy --workspace --all-targets --verbose -- --deny warnings"

MODIFY
crates/cli/tests/main.rs
crates/cli/tests/main.rs
@@ -29,6 +29,7 @@ fn test_format_cli() {
- file "fixtures/input.json"
- with options Tab
{"string": "foo", "boolean": false, "number": 15, "object": {"something": 15}}
+
"#
);
}

MODIFY
crates/formatter/src/format_token.rs
crates/formatter/src/format_token.rs
@@ -62,6 +62,7 @@ impl IndentToken {
}
}
+/// A token used to gather a list of tokens; optionally they can be printed with a separator, using [ListToken::join]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ListToken {
content: Vec<FormatToken>,
@@ -72,6 +73,7 @@ impl ListToken {
Self { content }
}
+ /// Emits a list of [ListToken] which contains a list of [FormatToken]
pub fn concat<T: IntoIterator<Item = FormatToken>>(tokens: T) -> Self {
let tokens: Vec<FormatToken> = tokens
.into_iter()
@@ -83,7 +85,7 @@ impl ListToken {
Self::new(tokens)
}
- /// Takes a list of tokens and a separator as input and creates a list of tokens where they are separated by the separator.
+ /// Takes a list of tokens and a separator as input and creates a list of tokens where they are separated by that separator.
pub fn join<Separator: Into<FormatToken>, T: IntoIterator<Item = FormatToken>>(
separator: Separator,
tokens: T,

ADD
crates/formatter/src/format_tokens_macro.rs
+/// The macro `format_tokens` is a convenience macro to
+/// use when writing a list of tokens that should be at the same level
+/// without particular rule.
+///
+/// # Examples
+///
+/// Let's suppose you need to create tokens for the string `"foo": "bar"`,
+/// you would write:
+///
+/// ```rust
+/// use rome_formatter::{format_tokens, FormatToken};
+/// let token = format_tokens!("foo:", FormatToken::Space, "bar");
+/// ```
+///
+/// The macro can be also nested, although the macro needs to be decorated with the token you need.
+/// For example, let's try to format following string:
+///
+/// ```no_rust
+/// foo: { bar: lorem }
+/// ```
+/// You would write it like the following:
+///
+/// ```rust
+/// use rome_formatter::{format_tokens, format_token, IndentToken, FormatToken, FormatOptions};
+/// let token = format_tokens!(
+/// "foo:",
+/// FormatToken::Space,
+/// "{",
+/// IndentToken::new(
+/// format_tokens!(FormatToken::Space, "bar:", FormatToken::Space, "lorem")
+/// ),
+/// FormatToken::Space,
+/// "}"
+/// );
+/// assert_eq!(r#"foo: { bar: lorem }"#, format_token(&token, FormatOptions::default()).code());
+/// ```
+/// Or you can also create single tokens:
+/// ```
+/// use rome_formatter::{format_tokens, format_token, FormatOptions};
+/// let unique_token = format_tokens!("single");
+/// assert_eq!(r#"single"#, format_token(&unique_token, FormatOptions::default()).code());
+/// ```
+#[macro_export]
+macro_rules! format_tokens {
+
+ // called for things like format_tokens!("hey")
+ ($token:expr) => {
+ {
+ use $crate::FormatToken;
+ FormatToken::from($token)
+ }
+ };
+
+ ( $( $token:expr ),+ $(,)?) => {{
+ use $crate::{FormatToken, ListToken};
+ FormatToken::concat(vec![
+ $(
+ FormatToken::from($token)
+ ),+
+
+ ])
+ }};
+}

MODIFY
crates/formatter/src/lib.rs
crates/formatter/src/lib.rs
+//! Rome's official formatter.
+//!
+//! The crate exposes some API and utilities to implement the formatting logic.
+//!
+//! The formatter relies on an [IR], which allows to format any kind of data structure.
+//!
+//! In order to implement the formatting logic, you need to implement the trait [FormatValue] for
+//! the data structure you want to format.
+//!
+//! Let's say, for example that you have a small data structure that represents a key/value data:
+//!
+//! ```rust,no_test
+//! struct KeyValue {
+//! key: String,
+//! value: String
+//! }
+//! ```
+//!
+//! Now, we do want to create this IR for the data structure:
+//! ```rust
+//! use rome_formatter::{format_tokens, format_token, FormatToken, FormatValue, FormatOptions};
+//!
+//! struct KeyValue {
+//! key: String,
+//! value: String
+//! }
+//!
+//! impl FormatValue for KeyValue {
+//! fn format(&self) -> FormatToken {
+//! format_tokens!(self.key.as_str(), FormatToken::Space, "=>", FormatToken::Space, self.value.as_str())
+//! }
+//! }
+//!
+//! fn my_function() {
+//! let key_value = KeyValue { key: String::from("lorem"), value: String::from("ipsum") };
+//! let token = key_value.format();
+//! let options = FormatOptions::default();
+//! let result = format_token(&token, options);
+//! assert_eq!(result.code(), "lorem => ipsum");
+//! }
+//!
+//! ```
+//! [IR]: https://en.wikipedia.org/wiki/Intermediate_representation
+
mod format_json;
mod format_token;
+mod format_tokens_macro;
mod intersperse;
mod printer;
use crate::format_json::json_to_tokens;
use std::{fs::File, io::Read, path::PathBuf, str::FromStr};
-pub use format_token::{FormatToken, GroupToken, IfBreakToken, IndentToken, LineMode, ListToken};
+pub use format_token::{
+ FormatToken, GroupToken, IfBreakToken, IndentToken, LineMode, LineToken, ListToken,
+};
pub use printer::PrintResult;
-use printer::Printer;
+pub use printer::Printer;
+pub use printer::PrinterOptions;
-/// This trait should implemented on each node/value that should have a formatted representation
+/// This trait should be implemented on each node/value that should have a formatted representation
pub trait FormatValue {
fn format(&self) -> FormatToken;
}

MODIFY
crates/formatter/src/printer.rs
crates/formatter/src/printer.rs
@@ -11,6 +11,7 @@ pub struct PrinterOptions {
/// What's the max width of a line. Defaults to 80
pub print_width: u16,
+ /// The type of line ending to apply to the printed input
pub line_ending: LineEnding,
/// The never ending question whatever to use spaces or tabs, and if spaces, how many spaces

feat: format macro (#1678)

Co-authored-by: Micha Reiser <micha@reiser.io>