Introduce Token element (#7048)

This commit is contained in:
Micha Reiser 2023-09-02 10:05:47 +02:00 committed by GitHub
parent 2f3a950f6f
commit c05e4628b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 733 additions and 723 deletions

View file

@ -70,7 +70,7 @@ impl<'fmt, Context> Argument<'fmt, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [ /// let formatted = format!(SimpleFormatContext::default(), [
/// format_args!(text("a"), space(), text("b")) /// format_args!(token("a"), space(), token("b"))
/// ])?; /// ])?;
/// ///
/// assert_eq!("a b", formatted.print()?.as_code()); /// assert_eq!("a b", formatted.print()?.as_code());
@ -135,11 +135,11 @@ mod tests {
write!( write!(
&mut buffer, &mut buffer,
[ [
text("function"), token("function"),
space(), space(),
text("a"), token("a"),
space(), space(),
group(&format_args!(text("("), text(")"))) group(&format_args!(token("("), token(")")))
] ]
) )
.unwrap(); .unwrap();
@ -147,14 +147,14 @@ mod tests {
assert_eq!( assert_eq!(
buffer.into_vec(), buffer.into_vec(),
vec![ vec![
FormatElement::StaticText { text: "function" }, FormatElement::Token { text: "function" },
FormatElement::Space, FormatElement::Space,
FormatElement::StaticText { text: "a" }, FormatElement::Token { text: "a" },
FormatElement::Space, FormatElement::Space,
// Group // Group
FormatElement::Tag(Tag::StartGroup(tag::Group::new())), FormatElement::Tag(Tag::StartGroup(tag::Group::new())),
FormatElement::StaticText { text: "(" }, FormatElement::Token { text: "(" },
FormatElement::StaticText { text: ")" }, FormatElement::Token { text: ")" },
FormatElement::Tag(Tag::EndGroup) FormatElement::Tag(Tag::EndGroup)
] ]
); );

View file

@ -25,9 +25,9 @@ pub trait Buffer {
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// ///
/// buffer.write_element(FormatElement::StaticText { text: "test"}); /// buffer.write_element(FormatElement::Token { text: "test"});
/// ///
/// assert_eq!(buffer.into_vec(), vec![FormatElement::StaticText { text: "test" }]); /// assert_eq!(buffer.into_vec(), vec![FormatElement::Token { text: "test" }]);
/// ``` /// ```
fn write_element(&mut self, element: FormatElement); fn write_element(&mut self, element: FormatElement);
@ -50,9 +50,9 @@ pub trait Buffer {
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// ///
/// buffer.write_fmt(format_args!(text("Hello World"))).unwrap(); /// buffer.write_fmt(format_args!(token("Hello World"))).unwrap();
/// ///
/// assert_eq!(buffer.into_vec(), vec![FormatElement::StaticText{ text: "Hello World" }]); /// assert_eq!(buffer.into_vec(), vec![FormatElement::Token{ text: "Hello World" }]);
/// ``` /// ```
fn write_fmt(mut self: &mut Self, arguments: Arguments<Self::Context>) -> FormatResult<()> { fn write_fmt(mut self: &mut Self, arguments: Arguments<Self::Context>) -> FormatResult<()> {
write(&mut self, arguments) write(&mut self, arguments)
@ -316,11 +316,11 @@ where
/// write!( /// write!(
/// buffer, /// buffer,
/// [ /// [
/// text("The next soft line or space gets replaced by a space"), /// token("The next soft line or space gets replaced by a space"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("and the line here"), /// token("and the line here"),
/// soft_line_break(), /// soft_line_break(),
/// text("is removed entirely.") /// token("is removed entirely.")
/// ] /// ]
/// ) /// )
/// })] /// })]
@ -329,10 +329,10 @@ where
/// assert_eq!( /// assert_eq!(
/// formatted.document().as_ref(), /// formatted.document().as_ref(),
/// &[ /// &[
/// FormatElement::StaticText { text: "The next soft line or space gets replaced by a space" }, /// FormatElement::Token { text: "The next soft line or space gets replaced by a space" },
/// FormatElement::Space, /// FormatElement::Space,
/// FormatElement::StaticText { text: "and the line here" }, /// FormatElement::Token { text: "and the line here" },
/// FormatElement::StaticText { text: "is removed entirely." } /// FormatElement::Token { text: "is removed entirely." }
/// ] /// ]
/// ); /// );
/// ///
@ -488,19 +488,19 @@ pub trait BufferExtensions: Buffer + Sized {
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| { /// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
/// let mut recording = f.start_recording(); /// let mut recording = f.start_recording();
/// ///
/// write!(recording, [text("A")])?; /// write!(recording, [token("A")])?;
/// write!(recording, [text("B")])?; /// write!(recording, [token("B")])?;
/// ///
/// write!(recording, [format_with(|f| write!(f, [text("C"), text("D")]))])?; /// write!(recording, [format_with(|f| write!(f, [token("C"), token("D")]))])?;
/// ///
/// let recorded = recording.stop(); /// let recorded = recording.stop();
/// assert_eq!( /// assert_eq!(
/// recorded.deref(), /// recorded.deref(),
/// &[ /// &[
/// FormatElement::StaticText{ text: "A" }, /// FormatElement::Token{ text: "A" },
/// FormatElement::StaticText{ text: "B" }, /// FormatElement::Token{ text: "B" },
/// FormatElement::StaticText{ text: "C" }, /// FormatElement::Token{ text: "C" },
/// FormatElement::StaticText{ text: "D" } /// FormatElement::Token{ text: "D" }
/// ] /// ]
/// ); /// );
/// ///

View file

@ -26,7 +26,7 @@ use crate::{Buffer, VecBuffer};
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![text("a,"), soft_line_break(), text("b")]) /// group(&format_args![token("a,"), soft_line_break(), token("b")])
/// ])?; /// ])?;
/// ///
/// assert_eq!( /// assert_eq!(
@ -52,9 +52,9 @@ use crate::{Buffer, VecBuffer};
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("a long word,"), /// token("a long word,"),
/// soft_line_break(), /// soft_line_break(),
/// text("so that the group doesn't fit on a single line"), /// token("so that the group doesn't fit on a single line"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -83,9 +83,9 @@ pub const fn soft_line_break() -> Line {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("a,"), /// token("a,"),
/// hard_line_break(), /// hard_line_break(),
/// text("b"), /// token("b"),
/// hard_line_break() /// hard_line_break()
/// ]) /// ])
/// ])?; /// ])?;
@ -115,9 +115,9 @@ pub const fn hard_line_break() -> Line {
/// let elements = format!( /// let elements = format!(
/// SimpleFormatContext::default(), [ /// SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("a,"), /// token("a,"),
/// empty_line(), /// empty_line(),
/// text("b"), /// token("b"),
/// empty_line() /// empty_line()
/// ]) /// ])
/// ])?; /// ])?;
@ -146,9 +146,9 @@ pub const fn empty_line() -> Line {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("a,"), /// token("a,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("b"), /// token("b"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -173,9 +173,9 @@ pub const fn empty_line() -> Line {
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("a long word,"), /// token("a long word,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("so that the group doesn't fit on a single line"), /// token("so that the group doesn't fit on a single line"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -215,12 +215,8 @@ impl std::fmt::Debug for Line {
} }
} }
/// Creates a token that gets written as is to the output. Make sure to properly escape the text if /// Creates a token that gets written as is to the output. A token must be ASCII only and is not allowed
/// it's user generated (e.g. a string and not a language keyword). /// to contain any line breaks or tab characters.
///
/// # Line feeds
/// Tokens may contain line breaks but they must use the line feeds (`\n`).
/// The [`crate::Printer`] converts the line feed characters to the character specified in the [`crate::PrinterOptions`].
/// ///
/// # Examples /// # Examples
/// ///
@ -229,7 +225,7 @@ impl std::fmt::Debug for Line {
/// use ruff_formatter::prelude::*; /// use ruff_formatter::prelude::*;
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [text("Hello World")])?; /// let elements = format!(SimpleFormatContext::default(), [token("Hello World")])?;
/// ///
/// assert_eq!( /// assert_eq!(
/// "Hello World", /// "Hello World",
@ -248,34 +244,38 @@ impl std::fmt::Debug for Line {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// // the tab must be encoded as \\t to not literally print a tab character ("Hello{tab}World" vs "Hello\tWorld") /// // the tab must be encoded as \\t to not literally print a tab character ("Hello{tab}World" vs "Hello\tWorld")
/// let elements = format!(SimpleFormatContext::default(), [text("\"Hello\\tWorld\"")])?; /// let elements = format!(SimpleFormatContext::default(), [token("\"Hello\\tWorld\"")])?;
/// ///
/// assert_eq!(r#""Hello\tWorld""#, elements.print()?.as_code()); /// assert_eq!(r#""Hello\tWorld""#, elements.print()?.as_code());
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
pub fn text(text: &'static str) -> StaticText { pub fn token(text: &'static str) -> Token {
debug_assert_no_newlines(text); debug_assert!(text.is_ascii(), "Token must be ASCII text only");
debug_assert!(
!text.contains(['\n', '\r', '\t']),
"A token should not contain any newlines or tab characters"
);
StaticText { text } Token { text }
} }
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct StaticText { pub struct Token {
text: &'static str, text: &'static str,
} }
impl<Context> Format<Context> for StaticText { impl<Context> Format<Context> for Token {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::StaticText { text: self.text }); f.write_element(FormatElement::Token { text: self.text });
Ok(()) Ok(())
} }
} }
impl std::fmt::Debug for StaticText { impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "StaticToken({})", self.text) std::write!(f, "Token({})", self.text)
} }
} }
@ -295,11 +295,11 @@ impl std::fmt::Debug for StaticText {
/// ///
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// source_position(TextSize::new(0)), /// source_position(TextSize::new(0)),
/// text("\"Hello "), /// token("\"Hello "),
/// source_position(TextSize::new(8)), /// source_position(TextSize::new(8)),
/// text("'Ruff'"), /// token("'Ruff'"),
/// source_position(TextSize::new(14)), /// source_position(TextSize::new(14)),
/// text("\""), /// token("\""),
/// source_position(TextSize::new(20)) /// source_position(TextSize::new(20))
/// ])?; /// ])?;
/// ///
@ -336,25 +336,25 @@ impl<Context> Format<Context> for SourcePosition {
/// Creates a text from a dynamic string with its optional start-position in the source document. /// Creates a text from a dynamic string with its optional start-position in the source document.
/// This is done by allocating a new string internally. /// This is done by allocating a new string internally.
pub fn dynamic_text(text: &str, position: Option<TextSize>) -> DynamicText { pub fn text(text: &str, position: Option<TextSize>) -> Text {
debug_assert_no_newlines(text); debug_assert_no_newlines(text);
DynamicText { text, position } Text { text, position }
} }
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
pub struct DynamicText<'a> { pub struct Text<'a> {
text: &'a str, text: &'a str,
position: Option<TextSize>, position: Option<TextSize>,
} }
impl<Context> Format<Context> for DynamicText<'_> { impl<Context> Format<Context> for Text<'_> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
if let Some(source_position) = self.position { if let Some(source_position) = self.position {
f.write_element(FormatElement::SourcePosition(source_position)); f.write_element(FormatElement::SourcePosition(source_position));
} }
f.write_element(FormatElement::DynamicText { f.write_element(FormatElement::Text {
text: self.text.to_string().into_boxed_str(), text: self.text.to_string().into_boxed_str(),
}); });
@ -362,9 +362,9 @@ impl<Context> Format<Context> for DynamicText<'_> {
} }
} }
impl std::fmt::Debug for DynamicText<'_> { impl std::fmt::Debug for Text<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "DynamicToken({})", self.text) std::write!(f, "Text({})", self.text)
} }
} }
@ -446,9 +446,9 @@ fn debug_assert_no_newlines(text: &str) {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// text("a"), /// token("a"),
/// line_suffix(&text("c"), 0), /// line_suffix(&token("c"), 0),
/// text("b") /// token("b")
/// ])?; /// ])?;
/// ///
/// assert_eq!("abc", elements.print()?.as_code()); /// assert_eq!("abc", elements.print()?.as_code());
@ -470,16 +470,16 @@ fn debug_assert_no_newlines(text: &str) {
/// let elements = format!(context, [ /// let elements = format!(context, [
/// // Breaks /// // Breaks
/// group(&format_args![ /// group(&format_args![
/// if_group_breaks(&text("(")), /// if_group_breaks(&token("(")),
/// soft_block_indent(&format_args![text("a"), line_suffix(&text(" // a comment"), 13)]), /// soft_block_indent(&format_args![token("a"), line_suffix(&token(" // a comment"), 13)]),
/// if_group_breaks(&text(")")) /// if_group_breaks(&token(")"))
/// ]), /// ]),
/// ///
/// // Fits /// // Fits
/// group(&format_args![ /// group(&format_args![
/// if_group_breaks(&text("(")), /// if_group_breaks(&token("(")),
/// soft_block_indent(&format_args![text("a"), line_suffix(&text(" // a comment"), 0)]), /// soft_block_indent(&format_args![token("a"), line_suffix(&token(" // a comment"), 0)]),
/// if_group_breaks(&text(")")) /// if_group_breaks(&token(")"))
/// ]), /// ]),
/// ])?; /// ])?;
/// # assert_eq!("(\n\ta // a comment\n)a // a comment", elements.print()?.as_code()); /// # assert_eq!("(\n\ta // a comment\n)a // a comment", elements.print()?.as_code());
@ -533,11 +533,11 @@ impl<Context> std::fmt::Debug for LineSuffix<'_, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// text("a"), /// token("a"),
/// line_suffix(&text("c"), 0), /// line_suffix(&token("c"), 0),
/// text("b"), /// token("b"),
/// line_suffix_boundary(), /// line_suffix_boundary(),
/// text("d") /// token("d")
/// ])?; /// ])?;
/// ///
/// assert_eq!( /// assert_eq!(
@ -599,7 +599,7 @@ impl<Context> Format<Context> for LineSuffixBoundary {
/// write!(recording, [ /// write!(recording, [
/// labelled( /// labelled(
/// LabelId::of(MyLabels::Main), /// LabelId::of(MyLabels::Main),
/// &text("'I have a label'") /// &token("'I have a label'")
/// ) /// )
/// ])?; /// ])?;
/// ///
@ -608,9 +608,9 @@ impl<Context> Format<Context> for LineSuffixBoundary {
/// let is_labelled = recorded.first().is_some_and( |element| element.has_label(LabelId::of(MyLabels::Main))); /// let is_labelled = recorded.first().is_some_and( |element| element.has_label(LabelId::of(MyLabels::Main)));
/// ///
/// if is_labelled { /// if is_labelled {
/// write!(f, [text(" has label `Main`")]) /// write!(f, [token(" has label `Main`")])
/// } else { /// } else {
/// write!(f, [text(" doesn't have label `Main`")]) /// write!(f, [token(" doesn't have label `Main`")])
/// } /// }
/// })] /// })]
/// )?; /// )?;
@ -670,7 +670,7 @@ impl<Context> std::fmt::Debug for FormatLabelled<'_, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// // the tab must be encoded as \\t to not literally print a tab character ("Hello{tab}World" vs "Hello\tWorld") /// // the tab must be encoded as \\t to not literally print a tab character ("Hello{tab}World" vs "Hello\tWorld")
/// let elements = format!(SimpleFormatContext::default(), [text("a"), space(), text("b")])?; /// let elements = format!(SimpleFormatContext::default(), [token("a"), space(), token("b")])?;
/// ///
/// assert_eq!("a b", elements.print()?.as_code()); /// assert_eq!("a b", elements.print()?.as_code());
/// # Ok(()) /// # Ok(())
@ -708,16 +708,16 @@ impl<Context> Format<Context> for Space {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let block = format!(SimpleFormatContext::default(), [ /// let block = format!(SimpleFormatContext::default(), [
/// text("switch {"), /// token("switch {"),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("default:"), /// token("default:"),
/// indent(&format_args![ /// indent(&format_args![
/// // this is where we want to use a /// // this is where we want to use a
/// hard_line_break(), /// hard_line_break(),
/// text("break;"), /// token("break;"),
/// ]) /// ])
/// ]), /// ]),
/// text("}"), /// token("}"),
/// ])?; /// ])?;
/// ///
/// assert_eq!( /// assert_eq!(
@ -772,22 +772,22 @@ impl<Context> std::fmt::Debug for Indent<'_, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let block = format!(SimpleFormatContext::default(), [ /// let block = format!(SimpleFormatContext::default(), [
/// text("root"), /// token("root"),
/// align(2, &format_args![ /// align(2, &format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("aligned"), /// token("aligned"),
/// dedent(&format_args![ /// dedent(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("not aligned"), /// token("not aligned"),
/// ]), /// ]),
/// dedent(&indent(&format_args![ /// dedent(&indent(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("Indented, not aligned") /// token("Indented, not aligned")
/// ])) /// ]))
/// ]), /// ]),
/// dedent(&format_args![ /// dedent(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("Dedent on root level is a no-op.") /// token("Dedent on root level is a no-op.")
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -841,23 +841,23 @@ impl<Context> std::fmt::Debug for Dedent<'_, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let block = format!(SimpleFormatContext::default(), [ /// let block = format!(SimpleFormatContext::default(), [
/// text("root"), /// token("root"),
/// indent(&format_args![ /// indent(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("indent level 1"), /// token("indent level 1"),
/// indent(&format_args![ /// indent(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("indent level 2"), /// token("indent level 2"),
/// align(2, &format_args![ /// align(2, &format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("two space align"), /// token("two space align"),
/// dedent_to_root(&format_args![ /// dedent_to_root(&format_args![
/// hard_line_break(), /// hard_line_break(),
/// text("starts at the beginning of the line") /// token("starts at the beginning of the line")
/// ]), /// ]),
/// ]), /// ]),
/// hard_line_break(), /// hard_line_break(),
/// text("end indent level 2"), /// token("end indent level 2"),
/// ]) /// ])
/// ]), /// ]),
/// ])?; /// ])?;
@ -903,24 +903,24 @@ where
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let block = format!(SimpleFormatContext::default(), [ /// let block = format!(SimpleFormatContext::default(), [
/// text("a"), /// token("a"),
/// hard_line_break(), /// hard_line_break(),
/// text("?"), /// token("?"),
/// space(), /// space(),
/// align(2, &format_args![ /// align(2, &format_args![
/// text("function () {"), /// token("function () {"),
/// hard_line_break(), /// hard_line_break(),
/// text("}"), /// token("}"),
/// ]), /// ]),
/// hard_line_break(), /// hard_line_break(),
/// text(":"), /// token(":"),
/// space(), /// space(),
/// align(2, &format_args![ /// align(2, &format_args![
/// text("function () {"), /// token("function () {"),
/// block_indent(&text("console.log('test');")), /// block_indent(&token("console.log('test');")),
/// text("}"), /// token("}"),
/// ]), /// ]),
/// text(";") /// token(";")
/// ])?; /// ])?;
/// ///
/// assert_eq!( /// assert_eq!(
@ -953,24 +953,24 @@ where
/// }); /// });
/// ///
/// let block = format!(context, [ /// let block = format!(context, [
/// text("a"), /// token("a"),
/// hard_line_break(), /// hard_line_break(),
/// text("?"), /// token("?"),
/// space(), /// space(),
/// align(2, &format_args![ /// align(2, &format_args![
/// text("function () {"), /// token("function () {"),
/// hard_line_break(), /// hard_line_break(),
/// text("}"), /// token("}"),
/// ]), /// ]),
/// hard_line_break(), /// hard_line_break(),
/// text(":"), /// token(":"),
/// space(), /// space(),
/// align(2, &format_args![ /// align(2, &format_args![
/// text("function () {"), /// token("function () {"),
/// block_indent(&text("console.log('test');")), /// block_indent(&token("console.log('test');")),
/// text("}"), /// token("}"),
/// ]), /// ]),
/// text(";") /// token(";")
/// ])?; /// ])?;
/// ///
/// assert_eq!( /// assert_eq!(
@ -1038,13 +1038,13 @@ impl<Context> std::fmt::Debug for Align<'_, Context> {
/// let block = format![ /// let block = format![
/// SimpleFormatContext::default(), /// SimpleFormatContext::default(),
/// [ /// [
/// text("{"), /// token("{"),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("let a = 10;"), /// token("let a = 10;"),
/// hard_line_break(), /// hard_line_break(),
/// text("let c = a + 5;"), /// token("let c = a + 5;"),
/// ]), /// ]),
/// text("}"), /// token("}"),
/// ] /// ]
/// ]?; /// ]?;
/// ///
@ -1083,13 +1083,13 @@ pub fn block_indent<Context>(content: &impl Format<Context>) -> BlockIndent<Cont
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'First string',"), /// token("'First string',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("'second string',"), /// token("'second string',"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1109,13 +1109,13 @@ pub fn block_indent<Context>(content: &impl Format<Context>) -> BlockIndent<Cont
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("5,"), /// token("5,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("10"), /// token("10"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1157,15 +1157,15 @@ pub fn soft_block_indent<Context>(content: &impl Format<Context>) -> BlockIndent
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("name"), /// token("name"),
/// space(), /// space(),
/// text("="), /// token("="),
/// soft_line_indent_or_space(&format_args![ /// soft_line_indent_or_space(&format_args![
/// text("firstName"), /// token("firstName"),
/// space(), /// space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// text("lastName"), /// token("lastName"),
/// ]), /// ]),
/// ]) /// ])
/// ])?; /// ])?;
@ -1186,10 +1186,10 @@ pub fn soft_block_indent<Context>(content: &impl Format<Context>) -> BlockIndent
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("a"), /// token("a"),
/// space(), /// space(),
/// text("="), /// token("="),
/// soft_line_indent_or_space(&text("10")), /// soft_line_indent_or_space(&token("10")),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1289,14 +1289,14 @@ impl<Context> std::fmt::Debug for BlockIndent<'_, Context> {
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("{"), /// token("{"),
/// soft_space_or_block_indent(&format_args![ /// soft_space_or_block_indent(&format_args![
/// text("aPropertyThatExceeds"), /// token("aPropertyThatExceeds"),
/// text(":"), /// token(":"),
/// space(), /// space(),
/// text("'line width'"), /// token("'line width'"),
/// ]), /// ]),
/// text("}") /// token("}")
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1316,14 +1316,14 @@ impl<Context> std::fmt::Debug for BlockIndent<'_, Context> {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("{"), /// token("{"),
/// soft_space_or_block_indent(&format_args![ /// soft_space_or_block_indent(&format_args![
/// text("a"), /// token("a"),
/// text(":"), /// token(":"),
/// space(), /// space(),
/// text("5"), /// token("5"),
/// ]), /// ]),
/// text("}") /// token("}")
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1361,15 +1361,15 @@ pub fn soft_space_or_block_indent<Context>(content: &impl Format<Context>) -> Bl
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1394,15 +1394,15 @@ pub fn soft_space_or_block_indent<Context>(content: &impl Format<Context>) -> Bl
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'Good morning! How are you today?',"), /// token("'Good morning! How are you today?',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1495,37 +1495,37 @@ impl<Context> std::fmt::Debug for Group<'_, Context> {
/// let content = format_with(|f| { /// let content = format_with(|f| {
/// let parentheses_id = f.group_id("parentheses"); /// let parentheses_id = f.group_id("parentheses");
/// group(&format_args![ /// group(&format_args![
/// if_group_breaks(&text("(")), /// if_group_breaks(&token("(")),
/// indent_if_group_breaks(&format_args![ /// indent_if_group_breaks(&format_args![
/// soft_line_break(), /// soft_line_break(),
/// conditional_group(&format_args![ /// conditional_group(&format_args![
/// text("'aaaaaaa'"), /// token("'aaaaaaa'"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// fits_expanded(&conditional_group(&format_args![ /// fits_expanded(&conditional_group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'Good morning!',"), /// token("'Good morning!',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("'How are you?'"), /// token("'How are you?'"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ], tag::Condition::if_group_fits_on_line(parentheses_id))), /// ], tag::Condition::if_group_fits_on_line(parentheses_id))),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// conditional_group(&format_args![ /// conditional_group(&format_args![
/// text("'bbbb'"), /// token("'bbbb'"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("and"), /// token("and"),
/// space(), /// space(),
/// text("'c'") /// token("'c'")
/// ], tag::Condition::if_group_fits_on_line(parentheses_id)) /// ], tag::Condition::if_group_fits_on_line(parentheses_id))
/// ], tag::Condition::if_breaks()), /// ], tag::Condition::if_breaks()),
/// ], parentheses_id), /// ], parentheses_id),
/// soft_line_break(), /// soft_line_break(),
/// if_group_breaks(&text(")")) /// if_group_breaks(&token(")"))
/// ]) /// ])
/// .with_group_id(Some(parentheses_id)) /// .with_group_id(Some(parentheses_id))
/// .fmt(f) /// .fmt(f)
@ -1623,16 +1623,16 @@ impl<Context> std::fmt::Debug for ConditionalGroup<'_, Context> {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'Good morning! How are you today?',"), /// token("'Good morning! How are you today?',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// expand_parent(), // Forces the parent to expand /// expand_parent(), // Forces the parent to expand
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1679,16 +1679,16 @@ impl<Context> Format<Context> for ExpandParent {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [ /// let elements = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// if_group_breaks(&text(",")) /// if_group_breaks(&token(","))
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1713,16 +1713,16 @@ impl<Context> Format<Context> for ExpandParent {
/// ///
/// let elements = format!(context, [ /// let elements = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'A somewhat longer string to force a line break',"), /// token("'A somewhat longer string to force a line break',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// if_group_breaks(&text(",")) /// if_group_breaks(&token(","))
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1760,16 +1760,16 @@ where
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [ /// let formatted = format!(SimpleFormatContext::default(), [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// if_group_fits_on_line(&text(",")) /// if_group_fits_on_line(&token(","))
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1794,16 +1794,16 @@ where
/// ///
/// let formatted = format!(context, [ /// let formatted = format!(context, [
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("'A somewhat longer string to force a line break',"), /// token("'A somewhat longer string to force a line break',"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// if_group_fits_on_line(&text(",")) /// if_group_fits_on_line(&token(","))
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]) /// ])
/// ])?; /// ])?;
/// ///
@ -1860,21 +1860,21 @@ impl<Context> IfGroupBreaks<'_, Context> {
/// write!(f, [ /// write!(f, [
/// group( /// group(
/// &format_args![ /// &format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_with(|f| { /// soft_block_indent(&format_with(|f| {
/// f.fill() /// f.fill()
/// .entry(&soft_line_break_or_space(), &text("1,")) /// .entry(&soft_line_break_or_space(), &token("1,"))
/// .entry(&soft_line_break_or_space(), &text("234568789,")) /// .entry(&soft_line_break_or_space(), &token("234568789,"))
/// .entry(&soft_line_break_or_space(), &text("3456789,")) /// .entry(&soft_line_break_or_space(), &token("3456789,"))
/// .entry(&soft_line_break_or_space(), &format_args!( /// .entry(&soft_line_break_or_space(), &format_args!(
/// text("["), /// token("["),
/// soft_block_indent(&text("4")), /// soft_block_indent(&token("4")),
/// text("]"), /// token("]"),
/// if_group_breaks(&text(",")).with_group_id(Some(group_id)) /// if_group_breaks(&token(",")).with_group_id(Some(group_id))
/// )) /// ))
/// .finish() /// .finish()
/// })), /// })),
/// text("]") /// token("]")
/// ], /// ],
/// ).with_group_id(Some(group_id)) /// ).with_group_id(Some(group_id))
/// ]) /// ])
@ -1931,9 +1931,9 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
/// let id = f.group_id("head"); /// let id = f.group_id("head");
/// ///
/// write!(f, [ /// write!(f, [
/// group(&text("Head")).with_group_id(Some(id)), /// group(&token("Head")).with_group_id(Some(id)),
/// if_group_breaks(&indent(&text("indented"))).with_group_id(Some(id)), /// if_group_breaks(&indent(&token("indented"))).with_group_id(Some(id)),
/// if_group_fits_on_line(&text("indented")).with_group_id(Some(id)) /// if_group_fits_on_line(&token("indented")).with_group_id(Some(id))
/// ]) /// ])
/// ///
/// # }); /// # });
@ -1956,8 +1956,8 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
/// let group_id = f.group_id("header"); /// let group_id = f.group_id("header");
/// ///
/// write!(f, [ /// write!(f, [
/// group(&text("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)), /// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
/// indent_if_group_breaks(&format_args![hard_line_break(), text("a => b")], group_id) /// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
/// ]) /// ])
/// }); /// });
/// ///
@ -1986,8 +1986,8 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
/// let group_id = f.group_id("header"); /// let group_id = f.group_id("header");
/// ///
/// write!(f, [ /// write!(f, [
/// group(&text("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)), /// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
/// indent_if_group_breaks(&format_args![hard_line_break(), text("a => b")], group_id) /// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
/// ]) /// ])
/// }); /// });
/// ///
@ -2059,17 +2059,17 @@ impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
/// ///
/// write!(f, [ /// write!(f, [
/// group(&format_args![ /// group(&format_args![
/// text("a"), /// token("a"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// fits_expanded(&group(&format_args![ /// fits_expanded(&group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("a,"), space(), text("# comment"), expand_parent(), soft_line_break_or_space(), /// token("a,"), space(), token("# comment"), expand_parent(), soft_line_break_or_space(),
/// text("b") /// token("b")
/// ]), /// ]),
/// text("]") /// token("]")
/// ])) /// ]))
/// ]), /// ]),
/// ]) /// ])
@ -2161,17 +2161,17 @@ impl<Context, T> std::fmt::Debug for FormatWith<Context, T> {
/// impl Format<SimpleFormatContext> for MyFormat { /// impl Format<SimpleFormatContext> for MyFormat {
/// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> { /// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> {
/// write!(f, [ /// write!(f, [
/// text("("), /// token("("),
/// block_indent(&format_with(|f| { /// block_indent(&format_with(|f| {
/// let separator = space(); /// let separator = space();
/// let mut join = f.join_with(&separator); /// let mut join = f.join_with(&separator);
/// ///
/// for item in &self.items { /// for item in &self.items {
/// join.entry(&format_with(|f| write!(f, [dynamic_text(item, None)]))); /// join.entry(&format_with(|f| write!(f, [text(item, None)])));
/// } /// }
/// join.finish() /// join.finish()
/// })), /// })),
/// text(")") /// token(")")
/// ]) /// ])
/// } /// }
/// } /// }
@ -2212,8 +2212,8 @@ where
/// ///
/// struct MyFormat; /// struct MyFormat;
/// ///
/// fn generate_values() -> impl Iterator<Item=StaticText> { /// fn generate_values() -> impl Iterator<Item=Token> {
/// vec![text("1"), text("2"), text("3"), text("4")].into_iter() /// vec![token("1"), token("2"), token("3"), token("4")].into_iter()
/// } /// }
/// ///
/// impl Format<SimpleFormatContext> for MyFormat { /// impl Format<SimpleFormatContext> for MyFormat {
@ -2244,7 +2244,7 @@ where
/// ///
/// Formatting the same value twice results in a panic. /// Formatting the same value twice results in a panic.
/// ///
/// ```panics /// ```should_panic
/// use ruff_formatter::prelude::*; /// use ruff_formatter::prelude::*;
/// use ruff_formatter::{SimpleFormatContext, format, write, Buffer}; /// use ruff_formatter::{SimpleFormatContext, format, write, Buffer};
/// use ruff_text_size::TextSize; /// use ruff_text_size::TextSize;
@ -2252,7 +2252,7 @@ where
/// let mut count = 0; /// let mut count = 0;
/// ///
/// let value = format_once(|f| { /// let value = format_once(|f| {
/// write!(f, [dynamic_token(&std::format!("Formatted {count}."), TextSize::default())]) /// write!(f, [text(&std::format!("Formatted {count}."), None)])
/// }); /// });
/// ///
/// format!(SimpleFormatContext::default(), [value]).expect("Formatting once works fine"); /// format!(SimpleFormatContext::default(), [value]).expect("Formatting once works fine");
@ -2476,54 +2476,54 @@ impl<'a, Context> BestFitting<'a, Context> {
/// // Everything fits on a single line /// // Everything fits on a single line
/// format_args!( /// format_args!(
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]") /// token("]")
/// ]), /// ]),
/// space(), /// space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// text("aVeryLongIdentifier") /// token("aVeryLongIdentifier")
/// ), /// ),
/// ///
/// // Breaks after `[` and prints each elements on a single line /// // Breaks after `[` and prints each elements on a single line
/// // The group is necessary because the variant, by default is printed in flat mode and a /// // The group is necessary because the variant, by default is printed in flat mode and a
/// // hard line break indicates that the content doesn't fit. /// // hard line break indicates that the content doesn't fit.
/// format_args!( /// format_args!(
/// text("["), /// token("["),
/// group(&block_indent(&format_args![text("1,"), hard_line_break(), text("2,"), hard_line_break(), text("3")])).should_expand(true), /// group(&block_indent(&format_args![token("1,"), hard_line_break(), token("2,"), hard_line_break(), token("3")])).should_expand(true),
/// text("]"), /// token("]"),
/// space(), /// space(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// text("aVeryLongIdentifier") /// token("aVeryLongIdentifier")
/// ), /// ),
/// ///
/// // Adds parentheses and indents the body, breaks after the operator /// // Adds parentheses and indents the body, breaks after the operator
/// format_args!( /// format_args!(
/// text("("), /// token("("),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("["), /// token("["),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// hard_line_break(), /// hard_line_break(),
/// text("2,"), /// token("2,"),
/// hard_line_break(), /// hard_line_break(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// hard_line_break(), /// hard_line_break(),
/// text("+"), /// token("+"),
/// space(), /// space(),
/// text("aVeryLongIdentifier") /// token("aVeryLongIdentifier")
/// ]), /// ]),
/// text(")") /// token(")")
/// ) /// )
/// ).with_mode(BestFittingMode::AllLines) /// ).with_mode(BestFittingMode::AllLines)
/// ] /// ]

View file

@ -30,12 +30,11 @@ pub enum FormatElement {
/// formatted position. /// formatted position.
SourcePosition(TextSize), SourcePosition(TextSize),
/// Token constructed by the formatter from a static string /// A ASCII only Token that contains no line breaks or tab characters.
StaticText { text: &'static str }, Token { text: &'static str },
/// Token constructed from the input source as a dynamic /// An arbitrary text that can contain tabs, newlines, and unicode characters.
/// string. Text {
DynamicText {
/// There's no need for the text to be mutable, using `Box<str>` safes 8 bytes over `String`. /// There's no need for the text to be mutable, using `Box<str>` safes 8 bytes over `String`.
text: Box<str>, text: Box<str>,
}, },
@ -72,12 +71,8 @@ impl std::fmt::Debug for FormatElement {
FormatElement::Space => write!(fmt, "Space"), FormatElement::Space => write!(fmt, "Space"),
FormatElement::Line(mode) => fmt.debug_tuple("Line").field(mode).finish(), FormatElement::Line(mode) => fmt.debug_tuple("Line").field(mode).finish(),
FormatElement::ExpandParent => write!(fmt, "ExpandParent"), FormatElement::ExpandParent => write!(fmt, "ExpandParent"),
FormatElement::StaticText { text } => { FormatElement::Token { text } => fmt.debug_tuple("Token").field(text).finish(),
fmt.debug_tuple("StaticText").field(text).finish() FormatElement::Text { text, .. } => fmt.debug_tuple("DynamicText").field(text).finish(),
}
FormatElement::DynamicText { text, .. } => {
fmt.debug_tuple("DynamicText").field(text).finish()
}
FormatElement::SourceCodeSlice { FormatElement::SourceCodeSlice {
slice, slice,
contains_newlines, contains_newlines,
@ -244,8 +239,8 @@ impl FormatElement {
matches!( matches!(
self, self,
FormatElement::SourceCodeSlice { .. } FormatElement::SourceCodeSlice { .. }
| FormatElement::DynamicText { .. } | FormatElement::Text { .. }
| FormatElement::StaticText { .. } | FormatElement::Token { .. }
) )
} }
@ -260,8 +255,8 @@ impl FormatElements for FormatElement {
FormatElement::ExpandParent => true, FormatElement::ExpandParent => true,
FormatElement::Tag(Tag::StartGroup(group)) => !group.mode().is_flat(), FormatElement::Tag(Tag::StartGroup(group)) => !group.mode().is_flat(),
FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty), FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty),
FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'), FormatElement::Text { text, .. } => text.contains('\n'),
FormatElement::SourceCodeSlice { FormatElement::SourceCodeSlice {
contains_newlines, .. contains_newlines, ..
} => *contains_newlines, } => *contains_newlines,
@ -275,6 +270,7 @@ impl FormatElements for FormatElement {
FormatElement::LineSuffixBoundary FormatElement::LineSuffixBoundary
| FormatElement::Space | FormatElement::Space
| FormatElement::Tag(_) | FormatElement::Tag(_)
| FormatElement::Token { .. }
| FormatElement::SourcePosition(_) => false, | FormatElement::SourcePosition(_) => false,
} }
} }

View file

@ -104,8 +104,7 @@ impl Document {
expands = false; expands = false;
continue; continue;
} }
FormatElement::StaticText { text } => text.contains('\n'), FormatElement::Text { text, .. } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::SourceCodeSlice { FormatElement::SourceCodeSlice {
contains_newlines, .. contains_newlines, ..
} => *contains_newlines, } => *contains_newlines,
@ -249,20 +248,20 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
while let Some(element) = iter.next() { while let Some(element) = iter.next() {
if !first_element && !in_text && !element.is_end_tag() { if !first_element && !in_text && !element.is_end_tag() {
// Write a separator between every two elements // Write a separator between every two elements
write!(f, [text(","), soft_line_break_or_space()])?; write!(f, [token(","), soft_line_break_or_space()])?;
} }
first_element = false; first_element = false;
match element { match element {
element @ (FormatElement::Space element @ (FormatElement::Space
| FormatElement::StaticText { .. } | FormatElement::Token { .. }
| FormatElement::DynamicText { .. } | FormatElement::Text { .. }
| FormatElement::SourceCodeSlice { .. }) => { | FormatElement::SourceCodeSlice { .. }) => {
fn write_escaped(element: &FormatElement, f: &mut Formatter<IrFormatContext>) { fn write_escaped(element: &FormatElement, f: &mut Formatter<IrFormatContext>) {
let text = match element { let text = match element {
FormatElement::StaticText { text } => text, FormatElement::Token { text } => text,
FormatElement::DynamicText { text } => text.as_ref(), FormatElement::Text { text } => text.as_ref(),
FormatElement::SourceCodeSlice { slice, .. } => { FormatElement::SourceCodeSlice { slice, .. } => {
slice.text(f.context().source_code()) slice.text(f.context().source_code())
} }
@ -270,7 +269,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
}; };
if text.contains('"') { if text.contains('"') {
f.write_element(FormatElement::DynamicText { f.write_element(FormatElement::Text {
text: text.replace('"', r#"\""#).into(), text: text.replace('"', r#"\""#).into(),
}); });
} else { } else {
@ -279,14 +278,14 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
} }
if !in_text { if !in_text {
write!(f, [text("\"")])?; write!(f, [token("\"")])?;
} }
in_text = true; in_text = true;
match element { match element {
FormatElement::Space => { FormatElement::Space => {
write!(f, [text(" ")])?; write!(f, [token(" ")])?;
} }
element if element.is_text() => { element if element.is_text() => {
write_escaped(element, f); write_escaped(element, f);
@ -297,45 +296,42 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
let is_next_text = iter.peek().is_some_and(|e| e.is_text() || e.is_space()); let is_next_text = iter.peek().is_some_and(|e| e.is_text() || e.is_space());
if !is_next_text { if !is_next_text {
write!(f, [text("\"")])?; write!(f, [token("\"")])?;
in_text = false; in_text = false;
} }
} }
FormatElement::Line(mode) => match mode { FormatElement::Line(mode) => match mode {
LineMode::SoftOrSpace => { LineMode::SoftOrSpace => {
write!(f, [text("soft_line_break_or_space")])?; write!(f, [token("soft_line_break_or_space")])?;
} }
LineMode::Soft => { LineMode::Soft => {
write!(f, [text("soft_line_break")])?; write!(f, [token("soft_line_break")])?;
} }
LineMode::Hard => { LineMode::Hard => {
write!(f, [text("hard_line_break")])?; write!(f, [token("hard_line_break")])?;
} }
LineMode::Empty => { LineMode::Empty => {
write!(f, [text("empty_line")])?; write!(f, [token("empty_line")])?;
} }
}, },
FormatElement::ExpandParent => { FormatElement::ExpandParent => {
write!(f, [text("expand_parent")])?; write!(f, [token("expand_parent")])?;
} }
FormatElement::SourcePosition(position) => { FormatElement::SourcePosition(position) => {
write!( write!(
f, f,
[dynamic_text( [text(&std::format!("source_position({position:?})"), None)]
&std::format!("source_position({position:?})"),
None
)]
)?; )?;
} }
FormatElement::LineSuffixBoundary => { FormatElement::LineSuffixBoundary => {
write!(f, [text("line_suffix_boundary")])?; write!(f, [token("line_suffix_boundary")])?;
} }
FormatElement::BestFitting { variants, mode } => { FormatElement::BestFitting { variants, mode } => {
write!(f, [text("best_fitting([")])?; write!(f, [token("best_fitting([")])?;
f.write_elements([ f.write_elements([
FormatElement::Tag(StartIndent), FormatElement::Tag(StartIndent),
FormatElement::Line(LineMode::Hard), FormatElement::Line(LineMode::Hard),
@ -350,13 +346,13 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
FormatElement::Line(LineMode::Hard), FormatElement::Line(LineMode::Hard),
]); ]);
write!(f, [text("]")])?; write!(f, [token("]")])?;
if *mode != BestFittingMode::FirstLine { if *mode != BestFittingMode::FirstLine {
write!(f, [dynamic_text(&std::format!(", mode: {mode:?}"), None),])?; write!(f, [text(&std::format!(", mode: {mode:?}"), None),])?;
} }
write!(f, [text(")")])?; write!(f, [token(")")])?;
} }
FormatElement::Interned(interned) => { FormatElement::Interned(interned) => {
@ -370,7 +366,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
dynamic_text(&std::format!("<interned {index}>"), None), text(&std::format!("<interned {index}>"), None),
space(), space(),
&&**interned, &&**interned,
] ]
@ -379,10 +375,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
Some(reference) => { Some(reference) => {
write!( write!(
f, f,
[dynamic_text( [text(&std::format!("<ref interned *{reference}>"), None)]
&std::format!("<ref interned *{reference}>"),
None
)]
)?; )?;
} }
} }
@ -401,9 +394,9 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
text("<END_TAG_WITHOUT_START<"), token("<END_TAG_WITHOUT_START<"),
dynamic_text(&std::format!("{:?}", tag.kind()), None), text(&std::format!("{:?}", tag.kind()), None),
text(">>"), token(">>"),
] ]
)?; )?;
first_element = false; first_element = false;
@ -414,13 +407,13 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
f, f,
[ [
ContentArrayEnd, ContentArrayEnd,
text(")"), token(")"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("ERROR<START_END_TAG_MISMATCH<start: "), token("ERROR<START_END_TAG_MISMATCH<start: "),
dynamic_text(&std::format!("{start_kind:?}"), None), text(&std::format!("{start_kind:?}"), None),
text(", end: "), token(", end: "),
dynamic_text(&std::format!("{:?}", tag.kind()), None), text(&std::format!("{:?}", tag.kind()), None),
text(">>") token(">>")
] ]
)?; )?;
first_element = false; first_element = false;
@ -434,7 +427,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
match tag { match tag {
StartIndent => { StartIndent => {
write!(f, [text("indent(")])?; write!(f, [token("indent(")])?;
} }
StartDedent(mode) => { StartDedent(mode) => {
@ -443,16 +436,16 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
DedentMode::Root => "dedentRoot", DedentMode::Root => "dedentRoot",
}; };
write!(f, [text(label), text("(")])?; write!(f, [token(label), token("(")])?;
} }
StartAlign(tag::Align(count)) => { StartAlign(tag::Align(count)) => {
write!( write!(
f, f,
[ [
text("align("), token("align("),
dynamic_text(&count.to_string(), None), text(&count.to_string(), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
@ -462,27 +455,27 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
text("line_suffix("), token("line_suffix("),
dynamic_text(&std::format!("{reserved_width:?}"), None), text(&std::format!("{reserved_width:?}"), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
} }
StartVerbatim(_) => { StartVerbatim(_) => {
write!(f, [text("verbatim(")])?; write!(f, [token("verbatim(")])?;
} }
StartGroup(group) => { StartGroup(group) => {
write!(f, [text("group(")])?; write!(f, [token("group(")])?;
if let Some(group_id) = group.id() { if let Some(group_id) = group.id() {
write!( write!(
f, f,
[ [
dynamic_text(&std::format!("\"{group_id:?}\""), None), text(&std::format!("\"{group_id:?}\""), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
@ -491,10 +484,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
match group.mode() { match group.mode() {
GroupMode::Flat => {} GroupMode::Flat => {}
GroupMode::Expand => { GroupMode::Expand => {
write!(f, [text("expand: true,"), space()])?; write!(f, [token("expand: true,"), space()])?;
} }
GroupMode::Propagated => { GroupMode::Propagated => {
write!(f, [text("expand: propagated,"), space()])?; write!(f, [token("expand: propagated,"), space()])?;
} }
} }
} }
@ -503,10 +496,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
text("conditional_group(condition:"), token("conditional_group(condition:"),
space(), space(),
group.condition(), group.condition(),
text(","), token(","),
space() space()
] ]
)?; )?;
@ -514,10 +507,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
match group.mode() { match group.mode() {
GroupMode::Flat => {} GroupMode::Flat => {}
GroupMode::Expand => { GroupMode::Expand => {
write!(f, [text("expand: true,"), space()])?; write!(f, [token("expand: true,"), space()])?;
} }
GroupMode::Propagated => { GroupMode::Propagated => {
write!(f, [text("expand: propagated,"), space()])?; write!(f, [token("expand: propagated,"), space()])?;
} }
} }
} }
@ -526,9 +519,9 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
text("indent_if_group_breaks("), token("indent_if_group_breaks("),
dynamic_text(&std::format!("\"{id:?}\""), None), text(&std::format!("\"{id:?}\""), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
@ -537,10 +530,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
StartConditionalContent(condition) => { StartConditionalContent(condition) => {
match condition.mode { match condition.mode {
PrintMode::Flat => { PrintMode::Flat => {
write!(f, [text("if_group_fits_on_line(")])?; write!(f, [token("if_group_fits_on_line(")])?;
} }
PrintMode::Expanded => { PrintMode::Expanded => {
write!(f, [text("if_group_breaks(")])?; write!(f, [token("if_group_breaks(")])?;
} }
} }
@ -548,8 +541,8 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
dynamic_text(&std::format!("\"{group_id:?}\""), None), text(&std::format!("\"{group_id:?}\""), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
@ -560,36 +553,36 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!( write!(
f, f,
[ [
text("label("), token("label("),
dynamic_text(&std::format!("\"{label_id:?}\""), None), text(&std::format!("\"{label_id:?}\""), None),
text(","), token(","),
space(), space(),
] ]
)?; )?;
} }
StartFill => { StartFill => {
write!(f, [text("fill(")])?; write!(f, [token("fill(")])?;
} }
StartFitsExpanded(tag::FitsExpanded { StartFitsExpanded(tag::FitsExpanded {
condition, condition,
propagate_expand, propagate_expand,
}) => { }) => {
write!(f, [text("fits_expanded(propagate_expand:"), space()])?; write!(f, [token("fits_expanded(propagate_expand:"), space()])?;
if propagate_expand.get() { if propagate_expand.get() {
write!(f, [text("true")])?; write!(f, [token("true")])?;
} else { } else {
write!(f, [text("false")])?; write!(f, [token("false")])?;
} }
write!(f, [text(","), space()])?; write!(f, [token(","), space()])?;
if let Some(condition) = condition { if let Some(condition) = condition {
write!( write!(
f, f,
[text("condition:"), space(), condition, text(","), space()] [token("condition:"), space(), condition, token(","), space()]
)?; )?;
} }
} }
@ -611,7 +604,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
| EndDedent | EndDedent
| EndFitsExpanded | EndFitsExpanded
| EndVerbatim => { | EndVerbatim => {
write!(f, [ContentArrayEnd, text(")")])?; write!(f, [ContentArrayEnd, token(")")])?;
} }
}; };
@ -627,9 +620,9 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
f, f,
[ [
ContentArrayEnd, ContentArrayEnd,
text(")"), token(")"),
soft_line_break_or_space(), soft_line_break_or_space(),
dynamic_text(&std::format!("<START_WITHOUT_END<{top:?}>>"), None), text(&std::format!("<START_WITHOUT_END<{top:?}>>"), None),
] ]
)?; )?;
} }
@ -644,7 +637,7 @@ impl Format<IrFormatContext<'_>> for ContentArrayStart {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::{StartGroup, StartIndent}; use Tag::{StartGroup, StartIndent};
write!(f, [text("[")])?; write!(f, [token("[")])?;
f.write_elements([ f.write_elements([
FormatElement::Tag(StartGroup(tag::Group::new())), FormatElement::Tag(StartGroup(tag::Group::new())),
@ -667,7 +660,7 @@ impl Format<IrFormatContext<'_>> for ContentArrayEnd {
FormatElement::Tag(EndGroup), FormatElement::Tag(EndGroup),
]); ]);
write!(f, [text("]")]) write!(f, [token("]")])
} }
} }
@ -767,22 +760,22 @@ impl FormatElements for [FormatElement] {
impl Format<IrFormatContext<'_>> for Condition { impl Format<IrFormatContext<'_>> for Condition {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
match (self.mode, self.group_id) { match (self.mode, self.group_id) {
(PrintMode::Flat, None) => write!(f, [text("if_fits_on_line")]), (PrintMode::Flat, None) => write!(f, [token("if_fits_on_line")]),
(PrintMode::Flat, Some(id)) => write!( (PrintMode::Flat, Some(id)) => write!(
f, f,
[ [
text("if_group_fits_on_line("), token("if_group_fits_on_line("),
dynamic_text(&std::format!("\"{id:?}\""), None), text(&std::format!("\"{id:?}\""), None),
text(")") token(")")
] ]
), ),
(PrintMode::Expanded, None) => write!(f, [text("if_breaks")]), (PrintMode::Expanded, None) => write!(f, [token("if_breaks")]),
(PrintMode::Expanded, Some(id)) => write!( (PrintMode::Expanded, Some(id)) => write!(
f, f,
[ [
text("if_group_breaks("), token("if_group_breaks("),
dynamic_text(&std::format!("\"{id:?}\""), None), text(&std::format!("\"{id:?}\""), None),
text(")") token(")")
] ]
), ),
} }
@ -805,11 +798,11 @@ mod tests {
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
text("("), token("("),
soft_block_indent(&format_args![ soft_block_indent(&format_args![
text("Some longer content"), token("Some longer content"),
space(), space(),
text("That should ultimately break"), token("That should ultimately break"),
]) ])
])] ])]
) )
@ -838,7 +831,7 @@ mod tests {
fn escapes_quotes() { fn escapes_quotes() {
let formatted = format!( let formatted = format!(
SimpleFormatContext::default(), SimpleFormatContext::default(),
[text(r#""""Python docstring""""#)] [token(r#""""Python docstring""""#)]
) )
.unwrap(); .unwrap();
@ -859,7 +852,7 @@ mod tests {
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
text("("), token("("),
soft_block_indent(&format_args![ soft_block_indent(&format_args![
source_text_slice( source_text_slice(
TextRange::at(TextSize::new(0), TextSize::new(19)), TextRange::at(TextSize::new(0), TextSize::new(19)),
@ -899,16 +892,16 @@ mod tests {
use Tag::*; use Tag::*;
let document = Document::from(vec![ let document = Document::from(vec![
FormatElement::StaticText { text: "[" }, FormatElement::Token { text: "[" },
FormatElement::Tag(StartGroup(tag::Group::new())), FormatElement::Tag(StartGroup(tag::Group::new())),
FormatElement::Tag(StartIndent), FormatElement::Tag(StartIndent),
FormatElement::Line(LineMode::Soft), FormatElement::Line(LineMode::Soft),
FormatElement::StaticText { text: "a" }, FormatElement::Token { text: "a" },
// Close group instead of indent // Close group instead of indent
FormatElement::Tag(EndGroup), FormatElement::Tag(EndGroup),
FormatElement::Line(LineMode::Soft), FormatElement::Line(LineMode::Soft),
FormatElement::Tag(EndIndent), FormatElement::Tag(EndIndent),
FormatElement::StaticText { text: "]" }, FormatElement::Token { text: "]" },
// End tag without start // End tag without start
FormatElement::Tag(EndIndent), FormatElement::Tag(EndIndent),
// Start tag without an end // Start tag without an end

View file

@ -34,7 +34,7 @@ pub trait MemoizeFormat<Context> {
/// let value = self.value.get(); /// let value = self.value.get();
/// self.value.set(value + 1); /// self.value.set(value + 1);
/// ///
/// write!(f, [dynamic_text(&std::format!("Formatted {value} times."), None)]) /// write!(f, [text(&std::format!("Formatted {value} times."), None)])
/// } /// }
/// } /// }
/// ///
@ -110,9 +110,9 @@ where
/// let current = self.value.get(); /// let current = self.value.get();
/// ///
/// write!(f, [ /// write!(f, [
/// text("Count:"), /// token("Count:"),
/// space(), /// space(),
/// dynamic_text(&std::format!("{current}"), None), /// text(&std::format!("{current}"), None),
/// hard_line_break() /// hard_line_break()
/// ])?; /// ])?;
/// ///
@ -127,9 +127,9 @@ where
/// let counter_content = counter.inspect(f)?; /// let counter_content = counter.inspect(f)?;
/// ///
/// if counter_content.will_break() { /// if counter_content.will_break() {
/// write!(f, [text("Counter:"), block_indent(&counter)]) /// write!(f, [token("Counter:"), block_indent(&counter)])
/// } else { /// } else {
/// write!(f, [text("Counter:"), counter]) /// write!(f, [token("Counter:"), counter])
/// }?; /// }?;
/// ///
/// write!(f, [counter]) /// write!(f, [counter])

View file

@ -52,11 +52,11 @@ impl<'buf, Context> Formatter<'buf, Context> {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| { /// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
/// f.join() /// f.join()
/// .entry(&text("a")) /// .entry(&token("a"))
/// .entry(&space()) /// .entry(&space())
/// .entry(&text("+")) /// .entry(&token("+"))
/// .entry(&space()) /// .entry(&space())
/// .entry(&text("b")) /// .entry(&token("b"))
/// .finish() /// .finish()
/// })])?; /// })])?;
/// ///
@ -83,11 +83,11 @@ impl<'buf, Context> Formatter<'buf, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| { /// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
/// f.join_with(&format_args!(text(","), space())) /// f.join_with(&format_args!(token(","), space()))
/// .entry(&text("1")) /// .entry(&token("1"))
/// .entry(&text("2")) /// .entry(&token("2"))
/// .entry(&text("3")) /// .entry(&token("3"))
/// .entry(&text("4")) /// .entry(&token("4"))
/// .finish() /// .finish()
/// })])?; /// })])?;
/// ///
@ -121,10 +121,10 @@ impl<'buf, Context> Formatter<'buf, Context> {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| { /// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
/// f.fill() /// f.fill()
/// .entry(&soft_line_break_or_space(), &text("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) /// .entry(&soft_line_break_or_space(), &token("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
/// .entry(&soft_line_break_or_space(), &text("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")) /// .entry(&soft_line_break_or_space(), &token("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
/// .entry(&soft_line_break_or_space(), &text("cccccccccccccccccccccccccccccc")) /// .entry(&soft_line_break_or_space(), &token("cccccccccccccccccccccccccccccc"))
/// .entry(&soft_line_break_or_space(), &text("dddddddddddddddddddddddddddddd")) /// .entry(&soft_line_break_or_space(), &token("dddddddddddddddddddddddddddddd"))
/// .finish() /// .finish()
/// })])?; /// })])?;
/// ///
@ -142,10 +142,10 @@ impl<'buf, Context> Formatter<'buf, Context> {
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let entries = vec![ /// let entries = vec![
/// text("<b>Important: </b>"), /// token("<b>Important: </b>"),
/// text("Please do not commit memory bugs such as segfaults, buffer overflows, etc. otherwise you "), /// token("Please do not commit memory bugs such as segfaults, buffer overflows, etc. otherwise you "),
/// text("<em>will</em>"), /// token("<em>will</em>"),
/// text(" be reprimanded") /// token(" be reprimanded")
/// ]; /// ];
/// ///
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| { /// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {

View file

@ -455,7 +455,7 @@ pub type FormatResult<F> = Result<F, FormatError>;
/// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> { /// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> {
/// write!(f, [ /// write!(f, [
/// hard_line_break(), /// hard_line_break(),
/// dynamic_text(&self.0, None), /// text(&self.0, None),
/// hard_line_break(), /// hard_line_break(),
/// ]) /// ])
/// } /// }
@ -704,7 +704,7 @@ where
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// ///
/// write!(&mut buffer, [format_args!(text("Hello World"))])?; /// write!(&mut buffer, [format_args!(token("Hello World"))])?;
/// ///
/// let formatted = Formatted::new(Document::from(buffer.into_vec()), SimpleFormatContext::default()); /// let formatted = Formatted::new(Document::from(buffer.into_vec()), SimpleFormatContext::default());
/// ///
@ -723,7 +723,7 @@ where
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// ///
/// write!(&mut buffer, [text("Hello World")])?; /// write!(&mut buffer, [token("Hello World")])?;
/// ///
/// let formatted = Formatted::new(Document::from(buffer.into_vec()), SimpleFormatContext::default()); /// let formatted = Formatted::new(Document::from(buffer.into_vec()), SimpleFormatContext::default());
/// ///
@ -754,7 +754,7 @@ pub fn write<Context>(
/// use ruff_formatter::{format, format_args}; /// use ruff_formatter::{format, format_args};
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [&format_args!(text("test"))])?; /// let formatted = format!(SimpleFormatContext::default(), [&format_args!(token("test"))])?;
/// assert_eq!("test", formatted.print()?.as_code()); /// assert_eq!("test", formatted.print()?.as_code());
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -767,7 +767,7 @@ pub fn write<Context>(
/// use ruff_formatter::{format}; /// use ruff_formatter::{format};
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [text("test")])?; /// let formatted = format!(SimpleFormatContext::default(), [token("test")])?;
/// assert_eq!("test", formatted.print()?.as_code()); /// assert_eq!("test", formatted.print()?.as_code());
/// # Ok(()) /// # Ok(())
/// # } /// # }

View file

@ -16,7 +16,7 @@
/// ///
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let formatted = format!(SimpleFormatContext::default(), [ /// let formatted = format!(SimpleFormatContext::default(), [
/// format_args!(text("Hello World")) /// format_args!(token("Hello World"))
/// ])?; /// ])?;
/// ///
/// assert_eq!("Hello World", formatted.print()?.as_code()); /// assert_eq!("Hello World", formatted.print()?.as_code());
@ -52,15 +52,15 @@ macro_rules! format_args {
/// # fn main() -> FormatResult<()> { /// # fn main() -> FormatResult<()> {
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// write!(&mut buffer, [text("Hello"), space()])?; /// write!(&mut buffer, [token("Hello"), space()])?;
/// write!(&mut buffer, [text("World")])?; /// write!(&mut buffer, [token("World")])?;
/// ///
/// assert_eq!( /// assert_eq!(
/// buffer.into_vec(), /// buffer.into_vec(),
/// vec![ /// vec![
/// FormatElement::StaticText { text: "Hello" }, /// FormatElement::Token { text: "Hello" },
/// FormatElement::Space, /// FormatElement::Space,
/// FormatElement::StaticText { text: "World" }, /// FormatElement::Token { text: "World" },
/// ] /// ]
/// ); /// );
/// # Ok(()) /// # Ok(())
@ -86,10 +86,10 @@ macro_rules! write {
/// let mut state = FormatState::new(SimpleFormatContext::default()); /// let mut state = FormatState::new(SimpleFormatContext::default());
/// let mut buffer = VecBuffer::new(&mut state); /// let mut buffer = VecBuffer::new(&mut state);
/// ///
/// dbg_write!(buffer, [text("Hello")])?; /// dbg_write!(buffer, [token("Hello")])?;
/// // ^-- prints: [src/main.rs:7][0] = StaticToken("Hello") /// // ^-- prints: [src/main.rs:7][0] = StaticToken("Hello")
/// ///
/// assert_eq!(buffer.into_vec(), vec![FormatElement::StaticText { text: "Hello" }]); /// assert_eq!(buffer.into_vec(), vec![FormatElement::Token { text: "Hello" }]);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -126,14 +126,14 @@ macro_rules! dbg_write {
/// use ruff_formatter::prelude::*; /// use ruff_formatter::prelude::*;
/// use ruff_formatter::format; /// use ruff_formatter::format;
/// ///
/// let formatted = format!(SimpleFormatContext::default(), [text("("), text("a"), text(")")]).unwrap(); /// let formatted = format!(SimpleFormatContext::default(), [token("("), token("a"), token(")")]).unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// formatted.into_document(), /// formatted.into_document(),
/// Document::from(vec![ /// Document::from(vec![
/// FormatElement::StaticText { text: "(" }, /// FormatElement::Token { text: "(" },
/// FormatElement::StaticText { text: "a" }, /// FormatElement::Token { text: "a" },
/// FormatElement::StaticText { text: ")" }, /// FormatElement::Token { text: ")" },
/// ]) /// ])
/// ); /// );
/// ``` /// ```
@ -160,49 +160,49 @@ macro_rules! format {
/// let formatted = format!( /// let formatted = format!(
/// SimpleFormatContext::default(), /// SimpleFormatContext::default(),
/// [ /// [
/// text("aVeryLongIdentifier"), /// token("aVeryLongIdentifier"),
/// best_fitting!( /// best_fitting!(
/// // Everything fits on a single line /// // Everything fits on a single line
/// format_args!( /// format_args!(
/// text("("), /// token("("),
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]") /// token("]")
/// ]), /// ]),
/// text(")") /// token(")")
/// ), /// ),
/// ///
/// // Breaks after `[`, but prints all elements on a single line /// // Breaks after `[`, but prints all elements on a single line
/// format_args!( /// format_args!(
/// text("("), /// token("("),
/// text("["), /// token("["),
/// block_indent(&text("1, 2, 3")), /// block_indent(&token("1, 2, 3")),
/// text("]"), /// token("]"),
/// text(")"), /// token(")"),
/// ), /// ),
/// ///
/// // Breaks after `[` and prints each element on a single line /// // Breaks after `[` and prints each element on a single line
/// format_args!( /// format_args!(
/// text("("), /// token("("),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("["), /// token("["),
/// block_indent(&format_args![ /// block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// hard_line_break(), /// hard_line_break(),
/// text("2,"), /// token("2,"),
/// hard_line_break(), /// hard_line_break(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]"), /// token("]"),
/// ]), /// ]),
/// text(")") /// token(")")
/// ) /// )
/// ) /// )
/// ] /// ]
@ -251,38 +251,38 @@ macro_rules! format {
/// best_fitting!( /// best_fitting!(
/// // Prints the method call on the line but breaks the array. /// // Prints the method call on the line but breaks the array.
/// format_args!( /// format_args!(
/// text("expect(a).toMatch("), /// token("expect(a).toMatch("),
/// group(&format_args![ /// group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]") /// token("]")
/// ]).should_expand(true), /// ]).should_expand(true),
/// text(")") /// token(")")
/// ), /// ),
/// ///
/// // Breaks after `(` /// // Breaks after `(`
/// format_args!( /// format_args!(
/// text("expect(a).toMatch("), /// token("expect(a).toMatch("),
/// group(&soft_block_indent( /// group(&soft_block_indent(
/// &group(&format_args![ /// &group(&format_args![
/// text("["), /// token("["),
/// soft_block_indent(&format_args![ /// soft_block_indent(&format_args![
/// text("1,"), /// token("1,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("2,"), /// token("2,"),
/// soft_line_break_or_space(), /// soft_line_break_or_space(),
/// text("3"), /// token("3"),
/// ]), /// ]),
/// text("]") /// token("]")
/// ]).should_expand(true), /// ]).should_expand(true),
/// )).should_expand(true), /// )).should_expand(true),
/// text(")") /// token(")")
/// ), /// ),
/// ) /// )
/// ] /// ]
@ -345,7 +345,7 @@ mod tests {
impl Format<()> for TestFormat { impl Format<()> for TestFormat {
fn fmt(&self, f: &mut Formatter<()>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<()>) -> FormatResult<()> {
write!(f, [text("test")]) write!(f, [token("test")])
} }
} }
@ -358,7 +358,7 @@ mod tests {
assert_eq!( assert_eq!(
buffer.into_vec(), buffer.into_vec(),
vec![FormatElement::StaticText { text: "test" }] vec![FormatElement::Token { text: "test" }]
); );
} }
@ -369,18 +369,18 @@ mod tests {
write![ write![
&mut buffer, &mut buffer,
[text("a"), space(), text("simple"), space(), TestFormat] [token("a"), space(), token("simple"), space(), TestFormat]
] ]
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
buffer.into_vec(), buffer.into_vec(),
vec![ vec![
FormatElement::StaticText { text: "a" }, FormatElement::Token { text: "a" },
FormatElement::Space, FormatElement::Space,
FormatElement::StaticText { text: "simple" }, FormatElement::Token { text: "simple" },
FormatElement::Space, FormatElement::Space,
FormatElement::StaticText { text: "test" } FormatElement::Token { text: "test" }
] ]
); );
} }
@ -394,41 +394,41 @@ mod tests {
let formatted_best_fitting = format!( let formatted_best_fitting = format!(
SimpleFormatContext::default(), SimpleFormatContext::default(),
[ [
text("aVeryLongIdentifier"), token("aVeryLongIdentifier"),
soft_line_break_or_space(), soft_line_break_or_space(),
best_fitting![ best_fitting![
format_args![text( format_args![token(
"Something that will not fit on a line with 30 character print width." "Something that will not fit on a line with 30 character print width."
)], )],
format_args![group(&format_args![ format_args![group(&format_args![
text("Start"), token("Start"),
soft_line_break(), soft_line_break(),
group(&soft_block_indent(&format_args![ group(&soft_block_indent(&format_args![
text("1,"), token("1,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("2,"), token("2,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("3"), token("3"),
])), ])),
soft_line_break_or_space(), soft_line_break_or_space(),
soft_block_indent(&format_args![ soft_block_indent(&format_args![
text("1,"), token("1,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("2,"), token("2,"),
soft_line_break_or_space(), soft_line_break_or_space(),
group(&format_args!( group(&format_args!(
text("A,"), token("A,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("B") token("B")
)), )),
soft_line_break_or_space(), soft_line_break_or_space(),
text("3") token("3")
]), ]),
soft_line_break_or_space(), soft_line_break_or_space(),
text("End") token("End")
]) ])
.should_expand(true)], .should_expand(true)],
format_args!(text("Most"), hard_line_break(), text("Expanded")) format_args!(token("Most"), hard_line_break(), token("Expanded"))
] ]
] ]
) )
@ -439,34 +439,34 @@ mod tests {
let formatted_normal_list = format!( let formatted_normal_list = format!(
SimpleFormatContext::default(), SimpleFormatContext::default(),
[ [
text("aVeryLongIdentifier"), token("aVeryLongIdentifier"),
soft_line_break_or_space(), soft_line_break_or_space(),
format_args![ format_args![
text("Start"), token("Start"),
soft_line_break(), soft_line_break(),
&group(&soft_block_indent(&format_args![ &group(&soft_block_indent(&format_args![
text("1,"), token("1,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("2,"), token("2,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("3"), token("3"),
])), ])),
soft_line_break_or_space(), soft_line_break_or_space(),
&soft_block_indent(&format_args![ &soft_block_indent(&format_args![
text("1,"), token("1,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("2,"), token("2,"),
soft_line_break_or_space(), soft_line_break_or_space(),
group(&format_args!( group(&format_args!(
text("A,"), token("A,"),
soft_line_break_or_space(), soft_line_break_or_space(),
text("B") token("B")
)), )),
soft_line_break_or_space(), soft_line_break_or_space(),
text("3") token("3")
]), ]),
soft_line_break_or_space(), soft_line_break_or_space(),
text("End") token("End")
], ],
] ]
) )

View file

@ -95,31 +95,31 @@ impl<'a> Printer<'a> {
let args = stack.top(); let args = stack.top();
match element { match element {
FormatElement::Space => self.print_text(" ", None), FormatElement::Space => self.print_text(Text::Token(" "), None),
FormatElement::StaticText { text } => self.print_text(text, None), FormatElement::Token { text } => self.print_text(Text::Token(text), None),
FormatElement::DynamicText { text } => self.print_text(text, None), FormatElement::Text { text } => self.print_text(Text::Text(text), None),
FormatElement::SourceCodeSlice { slice, .. } => { FormatElement::SourceCodeSlice { slice, .. } => {
let text = slice.text(self.source_code); let text = slice.text(self.source_code);
self.print_text(text, Some(slice.range())); self.print_text(Text::Text(text), Some(slice.range()));
} }
FormatElement::Line(line_mode) => { FormatElement::Line(line_mode) => {
if args.mode().is_flat() if args.mode().is_flat()
&& matches!(line_mode, LineMode::Soft | LineMode::SoftOrSpace) && matches!(line_mode, LineMode::Soft | LineMode::SoftOrSpace)
{ {
if line_mode == &LineMode::SoftOrSpace { if line_mode == &LineMode::SoftOrSpace {
self.print_text(" ", None); self.print_text(Text::Token(" "), None);
} }
} else if self.state.line_suffixes.has_pending() { } else if self.state.line_suffixes.has_pending() {
self.flush_line_suffixes(queue, stack, Some(element)); self.flush_line_suffixes(queue, stack, Some(element));
} else { } else {
// Only print a newline if the current line isn't already empty // Only print a newline if the current line isn't already empty
if self.state.line_width > 0 { if self.state.line_width > 0 {
self.print_str("\n"); self.print_char('\n');
} }
// Print a second line break if this is an empty line // Print a second line break if this is an empty line
if line_mode == &LineMode::Empty { if line_mode == &LineMode::Empty {
self.print_str("\n"); self.print_char('\n');
} }
self.state.pending_indent = args.indention(); self.state.pending_indent = args.indention();
@ -352,7 +352,7 @@ impl<'a> Printer<'a> {
Ok(print_mode) Ok(print_mode)
} }
fn print_text(&mut self, text: &str, source_range: Option<TextRange>) { fn print_text(&mut self, text: Text, source_range: Option<TextRange>) {
if !self.state.pending_indent.is_empty() { if !self.state.pending_indent.is_empty() {
let (indent_char, repeat_count) = match self.options.indent_style() { let (indent_char, repeat_count) = match self.options.indent_style() {
IndentStyle::Tab => ('\t', 1), IndentStyle::Tab => ('\t', 1),
@ -390,7 +390,18 @@ impl<'a> Printer<'a> {
self.push_marker(); self.push_marker();
self.print_str(text); match text {
#[allow(clippy::cast_possible_truncation)]
Text::Token(token) => {
self.state.buffer.push_str(token);
self.state.line_width += token.len() as u32;
}
Text::Text(text) => {
for char in text.chars() {
self.print_char(char);
}
}
}
if let Some(range) = source_range { if let Some(range) = source_range {
self.state.source_position = range.end(); self.state.source_position = range.end();
@ -718,12 +729,6 @@ impl<'a> Printer<'a> {
invalid_end_tag(TagKind::Entry, stack.top_kind()) invalid_end_tag(TagKind::Entry, stack.top_kind())
} }
fn print_str(&mut self, content: &str) {
for char in content.chars() {
self.print_char(char);
}
}
fn print_char(&mut self, char: char) { fn print_char(&mut self, char: char) {
if char == '\n' { if char == '\n' {
self.state self.state
@ -1047,12 +1052,12 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
let args = self.stack.top(); let args = self.stack.top();
match element { match element {
FormatElement::Space => return Ok(self.fits_text(" ", args)), FormatElement::Space => return Ok(self.fits_text(Text::Token(" "), args)),
FormatElement::Line(line_mode) => { FormatElement::Line(line_mode) => {
match args.mode() { match args.mode() {
PrintMode::Flat => match line_mode { PrintMode::Flat => match line_mode {
LineMode::SoftOrSpace => return Ok(self.fits_text(" ", args)), LineMode::SoftOrSpace => return Ok(self.fits_text(Text::Token(" "), args)),
LineMode::Soft => {} LineMode::Soft => {}
LineMode::Hard | LineMode::Empty => { LineMode::Hard | LineMode::Empty => {
return Ok(if self.must_be_flat { return Ok(if self.must_be_flat {
@ -1081,11 +1086,11 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
} }
} }
FormatElement::StaticText { text } => return Ok(self.fits_text(text, args)), FormatElement::Token { text } => return Ok(self.fits_text(Text::Token(text), args)),
FormatElement::DynamicText { text, .. } => return Ok(self.fits_text(text, args)), FormatElement::Text { text, .. } => return Ok(self.fits_text(Text::Text(text), args)),
FormatElement::SourceCodeSlice { slice, .. } => { FormatElement::SourceCodeSlice { slice, .. } => {
let text = slice.text(self.printer.source_code); let text = slice.text(self.printer.source_code);
return Ok(self.fits_text(text, args)); return Ok(self.fits_text(Text::Text(text), args));
} }
FormatElement::LineSuffixBoundary => { FormatElement::LineSuffixBoundary => {
if self.state.has_line_suffix { if self.state.has_line_suffix {
@ -1293,31 +1298,39 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
Fits::Maybe Fits::Maybe
} }
fn fits_text(&mut self, text: &str, args: PrintElementArgs) -> Fits { fn fits_text(&mut self, text: Text, args: PrintElementArgs) -> Fits {
let indent = std::mem::take(&mut self.state.pending_indent); let indent = std::mem::take(&mut self.state.pending_indent);
self.state.line_width += self.state.line_width +=
u32::from(indent.level()) * self.options().indent_width() + u32::from(indent.align()); u32::from(indent.level()) * self.options().indent_width() + u32::from(indent.align());
for c in text.chars() { match text {
let char_width = match c { #[allow(clippy::cast_possible_truncation)]
'\t' => self.options().tab_width.value(), Text::Token(token) => {
'\n' => { self.state.line_width += token.len() as u32;
if self.must_be_flat { }
return Fits::No; Text::Text(text) => {
} for c in text.chars() {
match args.measure_mode() { let char_width = match c {
MeasureMode::FirstLine => return Fits::Yes, '\t' => self.options().tab_width.value(),
MeasureMode::AllLines => { '\n' => {
self.state.line_width = 0; if self.must_be_flat {
continue; return Fits::No;
}
match args.measure_mode() {
MeasureMode::FirstLine => return Fits::Yes,
MeasureMode::AllLines => {
self.state.line_width = 0;
continue;
}
}
} }
} // SAFETY: A u32 is sufficient to format files <= 4GB
#[allow(clippy::cast_possible_truncation)]
c => c.width().unwrap_or(0) as u32,
};
self.state.line_width += char_width;
} }
// SAFETY: A u32 is sufficient to format files <= 4GB }
#[allow(clippy::cast_possible_truncation)]
c => c.width().unwrap_or(0) as u32,
};
self.state.line_width += char_width;
} }
if self.state.line_width > self.options().line_width.into() { if self.state.line_width > self.options().line_width.into() {
@ -1434,6 +1447,14 @@ impl From<BestFittingMode> for MeasureMode {
} }
} }
#[derive(Copy, Clone, Debug)]
enum Text<'a> {
/// ASCII only text that contains no line breaks or tab characters.
Token(&'a str),
/// Arbitrary text. May contain `\n` line breaks, tab characters, or unicode characters.
Text(&'a str),
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*; use crate::prelude::*;
@ -1469,10 +1490,10 @@ mod tests {
fn it_prints_a_group_on_a_single_line_if_it_fits() { fn it_prints_a_group_on_a_single_line_if_it_fits() {
let result = format(&FormatArrayElements { let result = format(&FormatArrayElements {
items: vec![ items: vec![
&text("\"a\""), &token("\"a\""),
&text("\"b\""), &token("\"b\""),
&text("\"c\""), &token("\"c\""),
&text("\"d\""), &token("\"d\""),
], ],
}); });
@ -1482,17 +1503,17 @@ mod tests {
#[test] #[test]
fn it_tracks_the_indent_for_each_token() { fn it_tracks_the_indent_for_each_token() {
let formatted = format(&format_args!( let formatted = format(&format_args!(
text("a"), token("a"),
soft_block_indent(&format_args!( soft_block_indent(&format_args!(
text("b"), token("b"),
soft_block_indent(&format_args!( soft_block_indent(&format_args!(
text("c"), token("c"),
soft_block_indent(&format_args!(text("d"), soft_line_break(), text("d"),)), soft_block_indent(&format_args!(token("d"), soft_line_break(), token("d"),)),
text("c"), token("c"),
)), )),
text("b"), token("b"),
)), )),
text("a") token("a")
)); ));
assert_eq!( assert_eq!(
@ -1517,9 +1538,9 @@ a"#,
let result = format_with_options( let result = format_with_options(
&format_args![ &format_args![
text("function main() {"), token("function main() {"),
block_indent(&text("let x = `This is a multiline\nstring`;")), block_indent(&text("let x = `This is a multiline\nstring`;", None)),
text("}"), token("}"),
hard_line_break() hard_line_break()
], ],
options, options,
@ -1535,8 +1556,8 @@ a"#,
fn it_breaks_a_group_if_a_string_contains_a_newline() { fn it_breaks_a_group_if_a_string_contains_a_newline() {
let result = format(&FormatArrayElements { let result = format(&FormatArrayElements {
items: vec![ items: vec![
&text("`This is a string spanning\ntwo lines`"), &text("`This is a string spanning\ntwo lines`", None),
&text("\"b\""), &token("\"b\""),
], ],
}); });
@ -1551,7 +1572,7 @@ two lines`,
} }
#[test] #[test]
fn it_breaks_a_group_if_it_contains_a_hard_line_break() { fn it_breaks_a_group_if_it_contains_a_hard_line_break() {
let result = format(&group(&format_args![text("a"), block_indent(&text("b"))])); let result = format(&group(&format_args![token("a"), block_indent(&token("b"))]));
assert_eq!("a\n b\n", result.as_code()); assert_eq!("a\n b\n", result.as_code());
} }
@ -1560,17 +1581,17 @@ two lines`,
fn it_breaks_parent_groups_if_they_dont_fit_on_a_single_line() { fn it_breaks_parent_groups_if_they_dont_fit_on_a_single_line() {
let result = format(&FormatArrayElements { let result = format(&FormatArrayElements {
items: vec![ items: vec![
&text("\"a\""), &token("\"a\""),
&text("\"b\""), &token("\"b\""),
&text("\"c\""), &token("\"c\""),
&text("\"d\""), &token("\"d\""),
&FormatArrayElements { &FormatArrayElements {
items: vec![ items: vec![
&text("\"0123456789\""), &token("\"0123456789\""),
&text("\"0123456789\""), &token("\"0123456789\""),
&text("\"0123456789\""), &token("\"0123456789\""),
&text("\"0123456789\""), &token("\"0123456789\""),
&text("\"0123456789\""), &token("\"0123456789\""),
], ],
}, },
], ],
@ -1599,7 +1620,7 @@ two lines`,
let result = format_with_options( let result = format_with_options(
&FormatArrayElements { &FormatArrayElements {
items: vec![&text("'a'"), &text("'b'"), &text("'c'"), &text("'d'")], items: vec![&token("'a'"), &token("'b'"), &token("'c'"), &token("'d'")],
}, },
options, options,
); );
@ -1610,11 +1631,11 @@ two lines`,
#[test] #[test]
fn it_prints_consecutive_hard_lines_as_one() { fn it_prints_consecutive_hard_lines_as_one() {
let result = format(&format_args![ let result = format(&format_args![
text("a"), token("a"),
hard_line_break(), hard_line_break(),
hard_line_break(), hard_line_break(),
hard_line_break(), hard_line_break(),
text("b"), token("b"),
]); ]);
assert_eq!("a\nb", result.as_code()); assert_eq!("a\nb", result.as_code());
@ -1623,11 +1644,11 @@ two lines`,
#[test] #[test]
fn it_prints_consecutive_empty_lines_as_many() { fn it_prints_consecutive_empty_lines_as_many() {
let result = format(&format_args![ let result = format(&format_args![
text("a"), token("a"),
empty_line(), empty_line(),
empty_line(), empty_line(),
empty_line(), empty_line(),
text("b"), token("b"),
]); ]);
assert_eq!("a\n\n\n\nb", result.as_code()); assert_eq!("a\n\n\n\nb", result.as_code());
@ -1636,12 +1657,12 @@ two lines`,
#[test] #[test]
fn it_prints_consecutive_mixed_lines_as_many() { fn it_prints_consecutive_mixed_lines_as_many() {
let result = format(&format_args![ let result = format(&format_args![
text("a"), token("a"),
empty_line(), empty_line(),
hard_line_break(), hard_line_break(),
empty_line(), empty_line(),
hard_line_break(), hard_line_break(),
text("b"), token("b"),
]); ]);
assert_eq!("a\n\n\nb", result.as_code()); assert_eq!("a\n\n\nb", result.as_code());
@ -1658,37 +1679,37 @@ two lines`,
// These all fit on the same line together // These all fit on the same line together
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("1"), text(",")), &format_args!(token("1"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("2"), text(",")), &format_args!(token("2"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("3"), text(",")), &format_args!(token("3"), token(",")),
) )
// This one fits on a line by itself, // This one fits on a line by itself,
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("723493294"), text(",")), &format_args!(token("723493294"), token(",")),
) )
// fits without breaking // fits without breaking
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&group(&format_args!( &group(&format_args!(
text("["), token("["),
soft_block_indent(&text("5")), soft_block_indent(&token("5")),
text("],") token("],")
)), )),
) )
// this one must be printed in expanded mode to fit // this one must be printed in expanded mode to fit
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&group(&format_args!( &group(&format_args!(
text("["), token("["),
soft_block_indent(&text("123456789")), soft_block_indent(&token("123456789")),
text("]"), token("]"),
)), )),
) )
.finish() .finish()
@ -1713,27 +1734,27 @@ two lines`,
fn line_suffix_printed_at_end() { fn line_suffix_printed_at_end() {
let printed = format(&format_args![ let printed = format(&format_args![
group(&format_args![ group(&format_args![
text("["), token("["),
soft_block_indent(&format_with(|f| { soft_block_indent(&format_with(|f| {
f.fill() f.fill()
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("1"), text(",")), &format_args!(token("1"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("2"), text(",")), &format_args!(token("2"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("3"), if_group_breaks(&text(","))), &format_args!(token("3"), if_group_breaks(&token(","))),
) )
.finish() .finish()
})), })),
text("]") token("]")
]), ]),
text(";"), token(";"),
line_suffix(&format_args![space(), text("// trailing")], 0) line_suffix(&format_args![space(), token("// trailing")], 0)
]); ]);
assert_eq!(printed.as_code(), "[1, 2, 3]; // trailing"); assert_eq!(printed.as_code(), "[1, 2, 3]; // trailing");
@ -1743,27 +1764,27 @@ two lines`,
fn line_suffix_with_reserved_width() { fn line_suffix_with_reserved_width() {
let printed = format(&format_args![ let printed = format(&format_args![
group(&format_args![ group(&format_args![
text("["), token("["),
soft_block_indent(&format_with(|f| { soft_block_indent(&format_with(|f| {
f.fill() f.fill()
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("1"), text(",")), &format_args!(token("1"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("2"), text(",")), &format_args!(token("2"), token(",")),
) )
.entry( .entry(
&soft_line_break_or_space(), &soft_line_break_or_space(),
&format_args!(text("3"), if_group_breaks(&text(","))), &format_args!(token("3"), if_group_breaks(&token(","))),
) )
.finish() .finish()
})), })),
text("]") token("]")
]), ]),
text(";"), token(";"),
line_suffix(&format_args![space(), text("// Using reserved width causes this content to not fit even though it's a line suffix element")], 93) line_suffix(&format_args![space(), token("// Using reserved width causes this content to not fit even though it's a line suffix element")], 93)
]); ]);
assert_eq!(printed.as_code(), "[\n 1, 2, 3\n]; // Using reserved width causes this content to not fit even though it's a line suffix element"); assert_eq!(printed.as_code(), "[\n 1, 2, 3\n]; // Using reserved width causes this content to not fit even though it's a line suffix element");
@ -1777,15 +1798,15 @@ two lines`,
f, f,
[ [
group(&format_args![ group(&format_args![
text("The referenced group breaks."), token("The referenced group breaks."),
hard_line_break() hard_line_break()
]) ])
.with_group_id(Some(group_id)), .with_group_id(Some(group_id)),
group(&format_args![ group(&format_args![
text("This group breaks because:"), token("This group breaks because:"),
soft_line_break_or_space(), soft_line_break_or_space(),
if_group_fits_on_line(&text("This content fits but should not be printed.")).with_group_id(Some(group_id)), if_group_fits_on_line(&token("This content fits but should not be printed.")).with_group_id(Some(group_id)),
if_group_breaks(&text("It measures with the 'if_group_breaks' variant because the referenced group breaks and that's just way too much text.")).with_group_id(Some(group_id)), if_group_breaks(&token("It measures with the 'if_group_breaks' variant because the referenced group breaks and that's just way too much text.")).with_group_id(Some(group_id)),
]) ])
] ]
) )
@ -1805,7 +1826,7 @@ two lines`,
write!( write!(
f, f,
[ [
group(&text("Group with id-2")).with_group_id(Some(id_2)), group(&token("Group with id-2")).with_group_id(Some(id_2)),
hard_line_break() hard_line_break()
] ]
)?; )?;
@ -1813,7 +1834,7 @@ two lines`,
write!( write!(
f, f,
[ [
group(&text("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_group_id(Some(id_1)), group(&token("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_group_id(Some(id_1)),
hard_line_break() hard_line_break()
] ]
)?; )?;
@ -1821,9 +1842,9 @@ two lines`,
write!( write!(
f, f,
[ [
if_group_fits_on_line(&text("Group 2 fits")).with_group_id(Some(id_2)), if_group_fits_on_line(&token("Group 2 fits")).with_group_id(Some(id_2)),
hard_line_break(), hard_line_break(),
if_group_breaks(&text("Group 1 breaks")).with_group_id(Some(id_1)) if_group_breaks(&token("Group 1 breaks")).with_group_id(Some(id_1))
] ]
) )
}); });
@ -1848,15 +1869,15 @@ Group 1 breaks"#
write!( write!(
f, f,
[group(&format_args!( [group(&format_args!(
text("["), token("["),
soft_block_indent(&format_args!( soft_block_indent(&format_args!(
format_with(|f| f format_with(|f| f
.join_with(format_args!(text(","), soft_line_break_or_space())) .join_with(format_args!(token(","), soft_line_break_or_space()))
.entries(&self.items) .entries(&self.items)
.finish()), .finish()),
if_group_breaks(&text(",")), if_group_breaks(&token(",")),
)), )),
text("]") token("]")
))] ))]
) )
} }

View file

@ -27,9 +27,9 @@ impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
if_group_breaks(&text("(")), if_group_breaks(&token("(")),
soft_block_indent(&Arguments::from(&self.inner)), soft_block_indent(&Arguments::from(&self.inner)),
if_group_breaks(&text(")")), if_group_breaks(&token(")")),
])] ])]
) )
} }
@ -152,7 +152,7 @@ impl<'fmt, 'ast, 'buf> JoinCommaSeparatedBuilder<'fmt, 'ast, 'buf> {
{ {
self.result = self.result.and_then(|_| { self.result = self.result.and_then(|_| {
if self.entries.is_one_or_more() { if self.entries.is_one_or_more() {
write!(self.fmt, [text(","), separator])?; write!(self.fmt, [token(","), separator])?;
} }
self.entries = self.entries.next(node.end()); self.entries = self.entries.next(node.end());
@ -204,7 +204,7 @@ impl<'fmt, 'ast, 'buf> JoinCommaSeparatedBuilder<'fmt, 'ast, 'buf> {
|| self.trailing_comma == TrailingComma::OneOrMore || self.trailing_comma == TrailingComma::OneOrMore
|| self.entries.is_more_than_one() || self.entries.is_more_than_one()
{ {
if_group_breaks(&text(",")).fmt(self.fmt)?; if_group_breaks(&token(",")).fmt(self.fmt)?;
} }
if magic_trailing_comma { if magic_trailing_comma {

View file

@ -434,7 +434,7 @@ impl Format<PyFormatContext<'_>> for FormatNormalizedComment<'_> {
write!( write!(
f, f,
[ [
dynamic_text(owned, Some(self.range.start())), text(owned, Some(self.range.start())),
source_position(self.range.end()) source_position(self.range.end())
] ]
) )

View file

@ -113,7 +113,7 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
f, f,
[ [
dangling_comments(before_dot), dangling_comments(before_dot),
text("."), token("."),
dangling_comments(after_dot), dangling_comments(after_dot),
attr.format() attr.format()
] ]

View file

@ -16,7 +16,7 @@ impl FormatNodeRule<ExprAwait> for FormatExprAwait {
write!( write!(
f, f,
[ [
text("await"), token("await"),
space(), space(),
maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks)
] ]

View file

@ -260,7 +260,7 @@ impl FormatRule<Operator, PyFormatContext<'_>> for FormatOperator {
Operator::FloorDiv => "//", Operator::FloorDiv => "//",
}; };
text(operator).fmt(f) token(operator).fmt(f)
} }
} }

View file

@ -132,6 +132,6 @@ impl FormatRule<BoolOp, PyFormatContext<'_>> for FormatBoolOp {
BoolOp::Or => "or", BoolOp::Or => "or",
}; };
text(operator).fmt(f) token(operator).fmt(f)
} }
} }

View file

@ -113,6 +113,6 @@ impl FormatRule<CmpOp, PyFormatContext<'_>> for FormatCmpOp {
CmpOp::NotIn => "not in", CmpOp::NotIn => "not in",
}; };
text(operator).fmt(f) token(operator).fmt(f)
} }
} }

View file

@ -42,11 +42,11 @@ impl FormatNodeRule<ExprConstant> for FormatExprConstant {
} = item; } = item;
match value { match value {
Constant::Ellipsis => text("...").fmt(f), Constant::Ellipsis => token("...").fmt(f),
Constant::None => text("None").fmt(f), Constant::None => token("None").fmt(f),
Constant::Bool(value) => match value { Constant::Bool(value) => match value {
true => text("True").fmt(f), true => token("True").fmt(f),
false => text("False").fmt(f), false => token("False").fmt(f),
}, },
Constant::Int(_) => FormatInt::new(item).fmt(f), Constant::Int(_) => FormatInt::new(item).fmt(f),
Constant::Float(_) => FormatFloat::new(item).fmt(f), Constant::Float(_) => FormatFloat::new(item).fmt(f),

View file

@ -34,7 +34,7 @@ impl Format<PyFormatContext<'_>> for KeyValuePair<'_> {
f, f,
[group(&format_args![ [group(&format_args![
key.format(), key.format(),
text(":"), token(":"),
space(), space(),
self.value.format() self.value.format()
])] ])]
@ -49,7 +49,7 @@ impl Format<PyFormatContext<'_>> for KeyValuePair<'_> {
[ [
// make sure the leading comments are hoisted past the `**` // make sure the leading comments are hoisted past the `**`
leading_comments(leading_value_comments), leading_comments(leading_value_comments),
group(&format_args![text("**"), self.value.format()]) group(&format_args![token("**"), self.value.format()])
] ]
) )
} }

View file

@ -1,5 +1,5 @@
use ruff_formatter::prelude::{ use ruff_formatter::prelude::{
format_args, format_with, group, soft_line_break_or_space, space, text, format_args, format_with, group, soft_line_break_or_space, space, token,
}; };
use ruff_formatter::write; use ruff_formatter::write;
use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::node::AnyNodeRef;
@ -35,7 +35,7 @@ impl FormatNodeRule<ExprDictComp> for FormatExprDictComp {
"{", "{",
&group(&format_args!( &group(&format_args!(
group(&key.format()), group(&key.format()),
text(":"), token(":"),
space(), space(),
value.format(), value.format(),
soft_line_break_or_space(), soft_line_break_or_space(),

View file

@ -62,12 +62,12 @@ impl FormatNodeRule<ExprIfExp> for FormatExprIfExp {
body.format(), body.format(),
in_parentheses_only_soft_line_break_or_space(), in_parentheses_only_soft_line_break_or_space(),
leading_comments(comments.leading(test.as_ref())), leading_comments(comments.leading(test.as_ref())),
text("if"), token("if"),
space(), space(),
test.format(), test.format(),
in_parentheses_only_soft_line_break_or_space(), in_parentheses_only_soft_line_break_or_space(),
leading_comments(comments.leading(orelse.as_ref())), leading_comments(comments.leading(orelse.as_ref())),
text("else"), token("else"),
space(), space(),
] ]
)?; )?;

View file

@ -21,7 +21,7 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
let comments = f.context().comments().clone(); let comments = f.context().comments().clone();
let dangling = comments.dangling(item); let dangling = comments.dangling(item);
write!(f, [text("lambda")])?; write!(f, [token("lambda")])?;
if let Some(parameters) = parameters { if let Some(parameters) = parameters {
write!( write!(
@ -35,7 +35,7 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
)?; )?;
} }
write!(f, [text(":")])?; write!(f, [token(":")])?;
if dangling.is_empty() { if dangling.is_empty() {
write!(f, [space()])?; write!(f, [space()])?;

View file

@ -25,7 +25,7 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
f, f,
[ [
group(&format_args!(target.format(), soft_line_break_or_space())), group(&format_args!(target.format(), soft_line_break_or_space())),
text(":=") token(":=")
] ]
)?; )?;

View file

@ -91,7 +91,7 @@ impl FormatNodeRule<ExprSlice> for FormatExprSlice {
if !all_simple && lower.is_some() { if !all_simple && lower.is_some() {
space().fmt(f)?; space().fmt(f)?;
} }
text(":").fmt(f)?; token(":").fmt(f)?;
// No upper node, no need for a space, e.g. `x[a() :]` // No upper node, no need for a space, e.g. `x[a() :]`
if !all_simple && upper.is_some() { if !all_simple && upper.is_some() {
space().fmt(f)?; space().fmt(f)?;
@ -125,7 +125,7 @@ impl FormatNodeRule<ExprSlice> for FormatExprSlice {
if !all_simple && (upper.is_some() || step.is_none()) { if !all_simple && (upper.is_some() || step.is_none()) {
space().fmt(f)?; space().fmt(f)?;
} }
text(":").fmt(f)?; token(":").fmt(f)?;
// No step node, no need for a space // No step node, no need for a space
if !all_simple && step.is_some() { if !all_simple && step.is_some() {
space().fmt(f)?; space().fmt(f)?;

View file

@ -21,7 +21,7 @@ impl FormatNodeRule<ExprStarred> for FormatExprStarred {
let comments = f.context().comments().clone(); let comments = f.context().comments().clone();
let dangling = comments.dangling(item); let dangling = comments.dangling(item);
write!(f, [text("*"), dangling_comments(dangling), value.format()]) write!(f, [token("*"), dangling_comments(dangling), value.format()])
} }
fn fmt_dangling_comments( fn fmt_dangling_comments(

View file

@ -70,10 +70,10 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
text("["), token("["),
trailing_comments(dangling_comments), trailing_comments(dangling_comments),
soft_block_indent(&format_slice), soft_block_indent(&format_slice),
text("]") token("]")
])] ])]
) )
} }

View file

@ -140,12 +140,12 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
TupleParentheses::Preserve TupleParentheses::Preserve
if !is_tuple_parenthesized(item, f.context().source()) => if !is_tuple_parenthesized(item, f.context().source()) =>
{ {
write!(f, [single.format(), text(",")]) write!(f, [single.format(), token(",")])
} }
_ => _ =>
// A single element tuple always needs parentheses and a trailing comma, except when inside of a subscript // A single element tuple always needs parentheses and a trailing comma, except when inside of a subscript
{ {
parenthesized("(", &format_args![single.format(), text(",")], ")") parenthesized("(", &format_args![single.format(), token(",")], ")")
.with_dangling_comments(dangling) .with_dangling_comments(dangling)
.fmt(f) .fmt(f)
} }
@ -166,7 +166,7 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
_ => match self.parentheses { _ => match self.parentheses {
TupleParentheses::Never => { TupleParentheses::Never => {
let separator = let separator =
format_with(|f| group(&format_args![text(","), space()]).fmt(f)); format_with(|f| group(&format_args![token(","), space()]).fmt(f));
f.join_with(separator) f.join_with(separator)
.entries(elts.iter().formatted()) .entries(elts.iter().formatted())
.finish() .finish()

View file

@ -26,7 +26,7 @@ impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
UnaryOp::USub => "-", UnaryOp::USub => "-",
}; };
text(operator).fmt(f)?; token(operator).fmt(f)?;
let comments = f.context().comments().clone(); let comments = f.context().comments().clone();

View file

@ -87,14 +87,14 @@ impl Format<PyFormatContext<'_>> for AnyExpressionYield<'_> {
write!( write!(
f, f,
[ [
text(keyword), token(keyword),
space(), space(),
maybe_parenthesize_expression(val, self, Parenthesize::Optional) maybe_parenthesize_expression(val, self, Parenthesize::Optional)
] ]
)?; )?;
} else { } else {
// ExprYieldFrom always has Some(value) so we should never get a bare `yield from` // ExprYieldFrom always has Some(value) so we should never get a bare `yield from`
write!(f, [&text(keyword)])?; write!(f, [&token(keyword)])?;
} }
Ok(()) Ok(())
} }

View file

@ -248,9 +248,9 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
// The group here is necessary because `format_expression` may contain IR elements // The group here is necessary because `format_expression` may contain IR elements
// that refer to the group id // that refer to the group id
group(&format_args![ group(&format_args![
text("("), token("("),
soft_block_indent(&format_expression), soft_block_indent(&format_expression),
text(")") token(")")
]) ])
.with_group_id(Some(group_id)) .with_group_id(Some(group_id))
.fmt(f) .fmt(f)
@ -267,9 +267,9 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
// Variant 2: // Variant 2:
// Try to fit the expression by adding parentheses and indenting the expression. // Try to fit the expression by adding parentheses and indenting the expression.
group(&format_args![ group(&format_args![
text("("), token("("),
soft_block_indent(&format_expression), soft_block_indent(&format_expression),
text(")") token(")")
]) ])
.with_group_id(Some(group_id)) .with_group_id(Some(group_id))
.should_expand(true), .should_expand(true),

View file

@ -25,7 +25,7 @@ impl Format<PyFormatContext<'_>> for FormatInt<'_> {
match normalized { match normalized {
Cow::Borrowed(_) => source_text_slice(range, ContainsNewlines::No).fmt(f), Cow::Borrowed(_) => source_text_slice(range, ContainsNewlines::No).fmt(f),
Cow::Owned(normalized) => dynamic_text(&normalized, Some(range.start())).fmt(f), Cow::Owned(normalized) => text(&normalized, Some(range.start())).fmt(f),
} }
} }
} }
@ -50,7 +50,7 @@ impl Format<PyFormatContext<'_>> for FormatFloat<'_> {
match normalized { match normalized {
Cow::Borrowed(_) => source_text_slice(range, ContainsNewlines::No).fmt(f), Cow::Borrowed(_) => source_text_slice(range, ContainsNewlines::No).fmt(f),
Cow::Owned(normalized) => dynamic_text(&normalized, Some(range.start())).fmt(f), Cow::Owned(normalized) => text(&normalized, Some(range.start())).fmt(f),
} }
} }
} }
@ -78,11 +78,11 @@ impl Format<PyFormatContext<'_>> for FormatComplex<'_> {
source_text_slice(range.sub_end(TextSize::from(1)), ContainsNewlines::No).fmt(f)?; source_text_slice(range.sub_end(TextSize::from(1)), ContainsNewlines::No).fmt(f)?;
} }
Cow::Owned(normalized) => { Cow::Owned(normalized) => {
dynamic_text(&normalized, Some(range.start())).fmt(f)?; text(&normalized, Some(range.start())).fmt(f)?;
} }
} }
text("j").fmt(f) token("j").fmt(f)
} }
} }

View file

@ -172,10 +172,10 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatParenthesized<'_, 'ast> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'ast>>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<PyFormatContext<'ast>>) -> FormatResult<()> {
let inner = format_with(|f| { let inner = format_with(|f| {
group(&format_args![ group(&format_args![
text(self.left), token(self.left),
dangling_open_parenthesis_comments(self.comments), dangling_open_parenthesis_comments(self.comments),
soft_block_indent(&Arguments::from(&self.content)), soft_block_indent(&Arguments::from(&self.content)),
text(self.right) token(self.right)
]) ])
.fmt(f) .fmt(f)
}); });
@ -232,13 +232,13 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatOptionalParentheses<'_, 'ast>
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
if_group_breaks(&text("(")), if_group_breaks(&token("(")),
indent_if_group_breaks( indent_if_group_breaks(
&format_args![soft_line_break(), Arguments::from(&self.content)], &format_args![soft_line_break(), Arguments::from(&self.content)],
parens_id parens_id
), ),
soft_line_break(), soft_line_break(),
if_group_breaks(&text(")")) if_group_breaks(&token(")"))
]) ])
.with_group_id(Some(parens_id))] .with_group_id(Some(parens_id))]
) )
@ -375,7 +375,7 @@ impl Format<PyFormatContext<'_>> for FormatEmptyParenthesized<'_> {
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
text(self.left), token(self.left),
// end-of-line comments // end-of-line comments
trailing_comments(&self.comments[..end_of_line_split]), trailing_comments(&self.comments[..end_of_line_split]),
// Avoid unstable formatting with // Avoid unstable formatting with
@ -390,7 +390,7 @@ impl Format<PyFormatContext<'_>> for FormatEmptyParenthesized<'_> {
(!self.comments[..end_of_line_split].is_empty()).then_some(hard_line_break()), (!self.comments[..end_of_line_split].is_empty()).then_some(hard_line_break()),
// own line comments, which need to be indented // own line comments, which need to be indented
soft_block_indent(&dangling_comments(&self.comments[end_of_line_split..])), soft_block_indent(&dangling_comments(&self.comments[end_of_line_split..])),
text(self.right) token(self.right)
])] ])]
) )
} }

View file

@ -326,7 +326,7 @@ impl Format<PyFormatContext<'_>> for FormatStringPart {
source_text_slice(self.range(), contains_newlines).fmt(f)?; source_text_slice(self.range(), contains_newlines).fmt(f)?;
} }
Cow::Owned(normalized) => { Cow::Owned(normalized) => {
dynamic_text(&normalized, Some(self.start())).fmt(f)?; text(&normalized, Some(self.start())).fmt(f)?;
} }
} }
self.preferred_quotes.fmt(f) self.preferred_quotes.fmt(f)
@ -386,17 +386,17 @@ impl Format<PyFormatContext<'_>> for StringPrefix {
// Retain the casing for the raw prefix: // Retain the casing for the raw prefix:
// https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#r-strings-and-r-strings // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#r-strings-and-r-strings
if self.contains(StringPrefix::RAW) { if self.contains(StringPrefix::RAW) {
text("r").fmt(f)?; token("r").fmt(f)?;
} else if self.contains(StringPrefix::RAW_UPPER) { } else if self.contains(StringPrefix::RAW_UPPER) {
text("R").fmt(f)?; token("R").fmt(f)?;
} }
if self.contains(StringPrefix::BYTE) { if self.contains(StringPrefix::BYTE) {
text("b").fmt(f)?; token("b").fmt(f)?;
} }
if self.contains(StringPrefix::F_STRING) { if self.contains(StringPrefix::F_STRING) {
text("f").fmt(f)?; token("f").fmt(f)?;
} }
// Remove the unicode prefix `u` if any because it is meaningless in Python 3+. // Remove the unicode prefix `u` if any because it is meaningless in Python 3+.
@ -596,7 +596,7 @@ impl Format<PyFormatContext<'_>> for StringQuotes {
(QuoteStyle::Double, true) => "\"\"\"", (QuoteStyle::Double, true) => "\"\"\"",
}; };
text(quotes).fmt(f) token(quotes).fmt(f)
} }
} }
@ -839,7 +839,7 @@ fn format_docstring(string_part: &FormatStringPart, f: &mut PyFormatter) -> Form
if already_normalized { if already_normalized {
source_text_slice(trimmed_line_range, ContainsNewlines::No).fmt(f)?; source_text_slice(trimmed_line_range, ContainsNewlines::No).fmt(f)?;
} else { } else {
dynamic_text(trim_both, Some(trimmed_line_range.start())).fmt(f)?; text(trim_both, Some(trimmed_line_range.start())).fmt(f)?;
} }
} }
offset += first.text_len(); offset += first.text_len();
@ -947,7 +947,7 @@ fn format_docstring_line(
let indent_len = let indent_len =
count_indentation_like_black(trim_end, f.options().tab_width()) - stripped_indentation; count_indentation_like_black(trim_end, f.options().tab_width()) - stripped_indentation;
let in_docstring_indent = " ".repeat(indent_len.to_usize()) + trim_end.trim_start(); let in_docstring_indent = " ".repeat(indent_len.to_usize()) + trim_end.trim_start();
dynamic_text(&in_docstring_indent, Some(offset)).fmt(f)?; text(&in_docstring_indent, Some(offset)).fmt(f)?;
} else { } else {
// Take the string with the trailing whitespace removed, then also skip the leading // Take the string with the trailing whitespace removed, then also skip the leading
// whitespace // whitespace
@ -957,7 +957,7 @@ fn format_docstring_line(
source_text_slice(trimmed_line_range, ContainsNewlines::No).fmt(f)?; source_text_slice(trimmed_line_range, ContainsNewlines::No).fmt(f)?;
} else { } else {
// All indents are ascii spaces, so the slicing is correct // All indents are ascii spaces, so the slicing is correct
dynamic_text( text(
&trim_end[stripped_indentation.to_usize()..], &trim_end[stripped_indentation.to_usize()..],
Some(trimmed_line_range.start()), Some(trimmed_line_range.start()),
) )

View file

@ -276,16 +276,16 @@ for converter in connection.ops.get_db_converters(
f: &mut ruff_formatter::formatter::Formatter<SimpleFormatContext>, f: &mut ruff_formatter::formatter::Formatter<SimpleFormatContext>,
) -> FormatResult<()> { ) -> FormatResult<()> {
let format_str = format_with(|f| { let format_str = format_with(|f| {
write!(f, [text("\"")])?; write!(f, [token("\"")])?;
let mut words = self.0.split_whitespace().peekable(); let mut words = self.0.split_whitespace().peekable();
let mut fill = f.fill(); let mut fill = f.fill();
let separator = format_with(|f| { let separator = format_with(|f| {
group(&format_args![ group(&format_args![
if_group_breaks(&text("\"")), if_group_breaks(&token("\"")),
soft_line_break_or_space(), soft_line_break_or_space(),
if_group_breaks(&text("\" ")) if_group_breaks(&token("\" "))
]) ])
.fmt(f) .fmt(f)
}); });
@ -293,10 +293,10 @@ for converter in connection.ops.get_db_converters(
while let Some(word) = words.next() { while let Some(word) = words.next() {
let is_last = words.peek().is_none(); let is_last = words.peek().is_none();
let format_word = format_with(|f| { let format_word = format_with(|f| {
write!(f, [dynamic_text(word, None)])?; write!(f, [text(word, None)])?;
if is_last { if is_last {
write!(f, [text("\"")])?; write!(f, [token("\"")])?;
} }
Ok(()) Ok(())
@ -311,9 +311,9 @@ for converter in connection.ops.get_db_converters(
write!( write!(
f, f,
[group(&format_args![ [group(&format_args![
if_group_breaks(&text("(")), if_group_breaks(&token("(")),
soft_block_indent(&format_str), soft_block_indent(&format_str),
if_group_breaks(&text(")")) if_group_breaks(&token(")"))
])] ])]
) )
} }

View file

@ -15,7 +15,7 @@ impl FormatNodeRule<Alias> for FormatAlias {
} = item; } = item;
name.format().fmt(f)?; name.format().fmt(f)?;
if let Some(asname) = asname { if let Some(asname) = asname {
write!(f, [space(), text("as"), space(), asname.format()])?; write!(f, [space(), token("as"), space(), asname.format()])?;
} }
Ok(()) Ok(())
} }

View file

@ -32,7 +32,7 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
} = item; } = item;
if *is_async { if *is_async {
write!(f, [text("async"), space()])?; write!(f, [token("async"), space()])?;
} }
let comments = f.context().comments().clone(); let comments = f.context().comments().clone();
@ -54,14 +54,14 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
write!( write!(
f, f,
[ [
text("for"), token("for"),
trailing_comments(before_target_comments), trailing_comments(before_target_comments),
group(&format_args!( group(&format_args!(
Spacer(target), Spacer(target),
ExprTupleWithoutParentheses(target), ExprTupleWithoutParentheses(target),
in_spacer, in_spacer,
leading_comments(before_in_comments), leading_comments(before_in_comments),
text("in"), token("in"),
trailing_comments(trailing_in_comments), trailing_comments(trailing_in_comments),
Spacer(iter), Spacer(iter),
iter.format(), iter.format(),
@ -81,7 +81,7 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
); );
joiner.entry(&group(&format_args!( joiner.entry(&group(&format_args!(
leading_comments(own_line_if_comments), leading_comments(own_line_if_comments),
text("if"), token("if"),
trailing_comments(end_of_line_if_comments), trailing_comments(end_of_line_if_comments),
Spacer(if_case), Spacer(if_case),
if_case.format(), if_case.format(),

View file

@ -19,7 +19,7 @@ impl FormatNodeRule<Decorator> for FormatDecorator {
write!( write!(
f, f,
[ [
text("@"), token("@"),
maybe_parenthesize_expression(expression, item, Parenthesize::Optional) maybe_parenthesize_expression(expression, item, Parenthesize::Optional)
] ]
) )

View file

@ -57,10 +57,10 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
write!( write!(
f, f,
[ [
text("except"), token("except"),
match self.except_handler_kind { match self.except_handler_kind {
ExceptHandlerKind::Regular => None, ExceptHandlerKind::Regular => None,
ExceptHandlerKind::Starred => Some(text("*")), ExceptHandlerKind::Starred => Some(token("*")),
} }
] ]
)?; )?;
@ -78,7 +78,7 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
] ]
)?; )?;
if let Some(name) = name { if let Some(name) = name {
write!(f, [space(), text("as"), space(), name.format()])?; write!(f, [space(), token("as"), space(), name.format()])?;
} }
} }

View file

@ -15,9 +15,9 @@ impl FormatNodeRule<Keyword> for FormatKeyword {
} = item; } = item;
// Comments after the `=` or `**` are reassigned as leading comments on the value. // Comments after the `=` or `**` are reassigned as leading comments on the value.
if let Some(arg) = arg { if let Some(arg) = arg {
write!(f, [arg.format(), text("="), value.format()]) write!(f, [arg.format(), token("="), value.format()])
} else { } else {
write!(f, [text("**"), value.format()]) write!(f, [token("**"), value.format()])
} }
} }
} }

View file

@ -30,7 +30,7 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase {
ClauseHeader::MatchCase(item), ClauseHeader::MatchCase(item),
dangling_item_comments, dangling_item_comments,
&format_with(|f| { &format_with(|f| {
write!(f, [text("case"), space()])?; write!(f, [token("case"), space()])?;
let has_comments = comments.has_leading(pattern) let has_comments = comments.has_leading(pattern)
|| comments.has_trailing_own_line(pattern); || comments.has_trailing_own_line(pattern);
@ -58,7 +58,7 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase {
} }
if let Some(guard) = guard { if let Some(guard) = guard {
write!(f, [space(), text("if"), space(), guard.format()])?; write!(f, [space(), token("if"), space(), guard.format()])?;
} }
Ok(()) Ok(())

View file

@ -17,7 +17,7 @@ impl FormatNodeRule<Parameter> for FormatParameter {
name.format().fmt(f)?; name.format().fmt(f)?;
if let Some(annotation) = annotation { if let Some(annotation) = annotation {
write!(f, [text(":"), space(), annotation.format()])?; write!(f, [token(":"), space(), annotation.format()])?;
} }
Ok(()) Ok(())

View file

@ -18,7 +18,7 @@ impl FormatNodeRule<ParameterWithDefault> for FormatParameterWithDefault {
if let Some(default) = default { if let Some(default) = default {
let space = parameter.annotation.is_some().then_some(space()); let space = parameter.annotation.is_some().then_some(space());
write!(f, [space, text("="), space, group(&default.format())])?; write!(f, [space, token("="), space, group(&default.format())])?;
} }
Ok(()) Ok(())

View file

@ -102,7 +102,7 @@ impl FormatNodeRule<Parameters> for FormatParameters {
dangling.split_at(parenthesis_comments_end); dangling.split_at(parenthesis_comments_end);
let format_inner = format_with(|f: &mut PyFormatter| { let format_inner = format_with(|f: &mut PyFormatter| {
let separator = format_with(|f| write!(f, [text(","), soft_line_break_or_space()])); let separator = format_with(|f| write!(f, [token(","), soft_line_break_or_space()]));
let mut joiner = f.join_with(separator); let mut joiner = f.join_with(separator);
let mut last_node: Option<AnyNodeRef> = None; let mut last_node: Option<AnyNodeRef> = None;
@ -156,7 +156,7 @@ impl FormatNodeRule<Parameters> for FormatParameters {
if let Some(vararg) = vararg { if let Some(vararg) = vararg {
joiner.entry(&format_args![ joiner.entry(&format_args![
leading_node_comments(vararg.as_ref()), leading_node_comments(vararg.as_ref()),
text("*"), token("*"),
vararg.format() vararg.format()
]); ]);
last_node = Some(vararg.as_any_node_ref()); last_node = Some(vararg.as_any_node_ref());
@ -192,7 +192,7 @@ impl FormatNodeRule<Parameters> for FormatParameters {
if let Some(kwarg) = kwarg { if let Some(kwarg) = kwarg {
joiner.entry(&format_args![ joiner.entry(&format_args![
leading_node_comments(kwarg.as_ref()), leading_node_comments(kwarg.as_ref()),
text("**"), token("**"),
kwarg.format() kwarg.format()
]); ]);
last_node = Some(kwarg.as_any_node_ref()); last_node = Some(kwarg.as_any_node_ref());
@ -216,10 +216,10 @@ impl FormatNodeRule<Parameters> for FormatParameters {
// For lambdas (no parentheses), preserve the trailing comma. It doesn't // For lambdas (no parentheses), preserve the trailing comma. It doesn't
// behave like a magic trailing comma, it's just preserved // behave like a magic trailing comma, it's just preserved
if has_trailing_comma(item, last_node, f.context().source()) { if has_trailing_comma(item, last_node, f.context().source()) {
write!(f, [text(",")])?; write!(f, [token(",")])?;
} }
} else { } else {
write!(f, [if_group_breaks(&text(","))])?; write!(f, [if_group_breaks(&token(","))])?;
if f.options().magic_trailing_comma().is_respect() if f.options().magic_trailing_comma().is_respect()
&& has_trailing_comma(item, last_node, f.context().source()) && has_trailing_comma(item, last_node, f.context().source())
@ -252,10 +252,10 @@ impl FormatNodeRule<Parameters> for FormatParameters {
write!( write!(
f, f,
[ [
text("("), token("("),
dangling_open_parenthesis_comments(parenthesis_dangling), dangling_open_parenthesis_comments(parenthesis_dangling),
soft_block_indent(&group(&format_inner)), soft_block_indent(&group(&format_inner)),
text(")") token(")")
] ]
) )
} }
@ -279,7 +279,7 @@ struct CommentsAroundText<'a> {
impl Format<PyFormatContext<'_>> for CommentsAroundText<'_> { impl Format<PyFormatContext<'_>> for CommentsAroundText<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
if self.comments.is_empty() { if self.comments.is_empty() {
text(self.text).fmt(f) token(self.text).fmt(f)
} else { } else {
// There might be own line comments in trailing, but those are weird and we can kinda // There might be own line comments in trailing, but those are weird and we can kinda
// ignore them // ignore them
@ -301,7 +301,7 @@ impl Format<PyFormatContext<'_>> for CommentsAroundText<'_> {
f, f,
[ [
leading_comments(leading), leading_comments(leading),
text(self.text), token(self.text),
trailing_comments(trailing) trailing_comments(trailing)
] ]
) )

View file

@ -30,7 +30,7 @@ impl FormatNodeRule<WithItem> for FormatWithItem {
)?; )?;
if let Some(optional_vars) = optional_vars { if let Some(optional_vars) = optional_vars {
write!(f, [space(), text("as"), space()])?; write!(f, [space(), token("as"), space()])?;
if trailing_as_comments.is_empty() { if trailing_as_comments.is_empty() {
write!(f, [optional_vars.format()])?; write!(f, [optional_vars.format()])?;

View file

@ -12,6 +12,6 @@ impl FormatNodeRule<PatternKeyword> for FormatPatternKeyword {
attr, attr,
pattern, pattern,
} = item; } = item;
write!(f, [attr.format(), text("="), pattern.format()]) write!(f, [attr.format(), token("="), pattern.format()])
} }
} }

View file

@ -29,7 +29,7 @@ impl FormatNodeRule<PatternMatchAs> for FormatPatternMatchAs {
write!(f, [space()])?; write!(f, [space()])?;
} }
write!(f, [text("as")])?; write!(f, [token("as")])?;
let trailing_as_comments = comments.dangling(item); let trailing_as_comments = comments.dangling(item);
if trailing_as_comments.is_empty() { if trailing_as_comments.is_empty() {
@ -45,7 +45,7 @@ impl FormatNodeRule<PatternMatchAs> for FormatPatternMatchAs {
name.format().fmt(f) name.format().fmt(f)
} else { } else {
debug_assert!(pattern.is_none()); debug_assert!(pattern.is_none());
text("_").fmt(f) token("_").fmt(f)
} }
} }

View file

@ -130,7 +130,7 @@ impl Format<PyFormatContext<'_>> for RestPattern<'_> {
f, f,
[ [
leading_comments(self.comments), leading_comments(self.comments),
text("**"), token("**"),
self.identifier.format() self.identifier.format()
] ]
) )
@ -156,7 +156,7 @@ impl Format<PyFormatContext<'_>> for KeyPatternPair<'_> {
f, f,
[group(&format_args![ [group(&format_args![
self.key.format(), self.key.format(),
text(":"), token(":"),
space(), space(),
self.pattern.format() self.pattern.format()
])] ])]

View file

@ -35,7 +35,7 @@ impl FormatNodeRule<PatternMatchOr> for FormatPatternMatchOr {
[hard_line_break(), leading_comments(leading_value_comments)] [hard_line_break(), leading_comments(leading_value_comments)]
)?; )?;
} }
write!(f, [text("|"), space(), pattern.format()])?; write!(f, [token("|"), space(), pattern.format()])?;
} }
Ok(()) Ok(())

View file

@ -10,9 +10,9 @@ pub struct FormatPatternMatchSingleton;
impl FormatNodeRule<PatternMatchSingleton> for FormatPatternMatchSingleton { impl FormatNodeRule<PatternMatchSingleton> for FormatPatternMatchSingleton {
fn fmt_fields(&self, item: &PatternMatchSingleton, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &PatternMatchSingleton, f: &mut PyFormatter) -> FormatResult<()> {
match item.value { match item.value {
Constant::None => text("None").fmt(f), Constant::None => token("None").fmt(f),
Constant::Bool(true) => text("True").fmt(f), Constant::Bool(true) => token("True").fmt(f),
Constant::Bool(false) => text("False").fmt(f), Constant::Bool(false) => token("False").fmt(f),
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -16,11 +16,11 @@ impl FormatNodeRule<PatternMatchStar> for FormatPatternMatchStar {
let comments = f.context().comments().clone(); let comments = f.context().comments().clone();
let dangling = comments.dangling(item); let dangling = comments.dangling(item);
write!(f, [text("*"), dangling_comments(dangling)])?; write!(f, [token("*"), dangling_comments(dangling)])?;
match name { match name {
Some(name) => write!(f, [name.format()]), Some(name) => write!(f, [name.format()]),
None => write!(f, [text("_")]), None => write!(f, [token("_")]),
} }
} }

View file

@ -349,7 +349,7 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatClauseHeader<'_, 'ast> {
write_suppressed_clause_header(self.header, f)?; write_suppressed_clause_header(self.header, f)?;
} else { } else {
f.write_fmt(Arguments::from(&self.formatter))?; f.write_fmt(Arguments::from(&self.formatter))?;
text(":").fmt(f)?; token(":").fmt(f)?;
} }
trailing_comments(self.trailing_colon_comment).fmt(f) trailing_comments(self.trailing_colon_comment).fmt(f)

View file

@ -23,7 +23,7 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
f, f,
[ [
target.format(), target.format(),
text(":"), token(":"),
space(), space(),
maybe_parenthesize_expression(annotation, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(annotation, item, Parenthesize::IfBreaks)
] ]
@ -34,7 +34,7 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
f, f,
[ [
space(), space(),
text("="), token("="),
space(), space(),
maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks)
] ]

View file

@ -1,4 +1,4 @@
use ruff_formatter::prelude::{space, text}; use ruff_formatter::prelude::{space, token};
use ruff_formatter::write; use ruff_formatter::write;
use ruff_python_ast::StmtAssert; use ruff_python_ast::StmtAssert;
@ -22,7 +22,7 @@ impl FormatNodeRule<StmtAssert> for FormatStmtAssert {
write!( write!(
f, f,
[ [
text("assert"), token("assert"),
space(), space(),
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks)
] ]
@ -32,7 +32,7 @@ impl FormatNodeRule<StmtAssert> for FormatStmtAssert {
write!( write!(
f, f,
[ [
text(","), token(","),
space(), space(),
maybe_parenthesize_expression(msg, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(msg, item, Parenthesize::IfBreaks),
] ]

View file

@ -27,7 +27,7 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
[ [
first.format(), first.format(),
space(), space(),
text("="), token("="),
space(), space(),
FormatTargets { targets: rest } FormatTargets { targets: rest }
] ]
@ -89,9 +89,9 @@ impl Format<PyFormatContext<'_>> for FormatTargets<'_> {
write!( write!(
f, f,
[ [
if_group_breaks(&text("(")), if_group_breaks(&token("(")),
soft_block_indent(&first.format().with_options(Parentheses::Never)), soft_block_indent(&first.format().with_options(Parentheses::Never)),
if_group_breaks(&text(")")) if_group_breaks(&token(")"))
] ]
) )
} }
@ -103,7 +103,7 @@ impl Format<PyFormatContext<'_>> for FormatTargets<'_> {
[group(&format_args![ [group(&format_args![
format_first, format_first,
space(), space(),
text("="), token("="),
space(), space(),
FormatTargets { targets: rest } FormatTargets { targets: rest }
]) ])

View file

@ -24,7 +24,7 @@ impl FormatNodeRule<StmtAugAssign> for FormatStmtAugAssign {
target.format(), target.format(),
space(), space(),
op.format(), op.format(),
text("="), token("="),
space(), space(),
maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks)
] ]

View file

@ -8,7 +8,7 @@ pub struct FormatStmtBreak;
impl FormatNodeRule<StmtBreak> for FormatStmtBreak { impl FormatNodeRule<StmtBreak> for FormatStmtBreak {
fn fmt_fields(&self, _item: &StmtBreak, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, _item: &StmtBreak, f: &mut PyFormatter) -> FormatResult<()> {
text("break").fmt(f) token("break").fmt(f)
} }
fn is_suppressed( fn is_suppressed(

View file

@ -44,7 +44,7 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
ClauseHeader::Class(item), ClauseHeader::Class(item),
trailing_definition_comments, trailing_definition_comments,
&format_with(|f| { &format_with(|f| {
write!(f, [text("class"), space(), name.format()])?; write!(f, [token("class"), space(), name.format()])?;
if let Some(type_params) = type_params.as_deref() { if let Some(type_params) = type_params.as_deref() {
write!(f, [type_params.format()])?; write!(f, [type_params.format()])?;

View file

@ -8,7 +8,7 @@ pub struct FormatStmtContinue;
impl FormatNodeRule<StmtContinue> for FormatStmtContinue { impl FormatNodeRule<StmtContinue> for FormatStmtContinue {
fn fmt_fields(&self, _item: &StmtContinue, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, _item: &StmtContinue, f: &mut PyFormatter) -> FormatResult<()> {
text("continue").fmt(f) token("continue").fmt(f)
} }
fn is_suppressed( fn is_suppressed(

View file

@ -15,7 +15,7 @@ impl FormatNodeRule<StmtDelete> for FormatStmtDelete {
fn fmt_fields(&self, item: &StmtDelete, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &StmtDelete, f: &mut PyFormatter) -> FormatResult<()> {
let StmtDelete { range: _, targets } = item; let StmtDelete { range: _, targets } = item;
write!(f, [text("del"), space()])?; write!(f, [token("del"), space()])?;
match targets.as_slice() { match targets.as_slice() {
[] => { [] => {
@ -27,9 +27,9 @@ impl FormatNodeRule<StmtDelete> for FormatStmtDelete {
// del ( // del (
// # Dangling comment // # Dangling comment
// ) // )
text("("), token("("),
block_indent(&dangling_node_comments(item)), block_indent(&dangling_node_comments(item)),
text(")"), token(")"),
] ]
) )
} }

View file

@ -54,12 +54,12 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
ClauseHeader::For(item), ClauseHeader::For(item),
trailing_condition_comments, trailing_condition_comments,
&format_args![ &format_args![
is_async.then_some(format_args![text("async"), space()]), is_async.then_some(format_args![token("async"), space()]),
text("for"), token("for"),
space(), space(),
ExprTupleWithoutParentheses(target), ExprTupleWithoutParentheses(target),
space(), space(),
text("in"), token("in"),
space(), space(),
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
], ],
@ -83,7 +83,7 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
clause_header( clause_header(
ClauseHeader::OrElse(ElseClause::For(item)), ClauseHeader::OrElse(ElseClause::For(item)),
trailing, trailing,
&text("else"), &token("else"),
) )
.with_leading_comments(leading, body.last()), .with_leading_comments(leading, body.last()),
clause_body(orelse, trailing), clause_body(orelse, trailing),

View file

@ -50,10 +50,10 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
trailing_definition_comments, trailing_definition_comments,
&format_with(|f| { &format_with(|f| {
if *is_async { if *is_async {
write!(f, [text("async"), space()])?; write!(f, [token("async"), space()])?;
} }
write!(f, [text("def"), space(), name.format()])?; write!(f, [token("def"), space(), name.format()])?;
if let Some(type_params) = type_params.as_ref() { if let Some(type_params) = type_params.as_ref() {
write!(f, [type_params.format()])?; write!(f, [type_params.format()])?;
@ -63,7 +63,7 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
write!(f, [parameters.format()])?; write!(f, [parameters.format()])?;
if let Some(return_annotation) = returns.as_ref() { if let Some(return_annotation) = returns.as_ref() {
write!(f, [space(), text("->"), space()])?; write!(f, [space(), token("->"), space()])?;
if return_annotation.is_tuple_expr() { if return_annotation.is_tuple_expr() {
let parentheses = let parentheses =

View file

@ -15,18 +15,18 @@ impl FormatNodeRule<StmtGlobal> for FormatStmtGlobal {
// move the comment "off" of the `global` statement. // move the comment "off" of the `global` statement.
if f.context().comments().has_trailing(item.as_any_node_ref()) { if f.context().comments().has_trailing(item.as_any_node_ref()) {
let joined = format_with(|f| { let joined = format_with(|f| {
f.join_with(format_args![text(","), space()]) f.join_with(format_args![token(","), space()])
.entries(item.names.iter().formatted()) .entries(item.names.iter().formatted())
.finish() .finish()
}); });
write!(f, [text("global"), space(), &joined]) write!(f, [token("global"), space(), &joined])
} else { } else {
let joined = format_with(|f| { let joined = format_with(|f| {
f.join_with(&format_args![ f.join_with(&format_args![
text(","), token(","),
space(), space(),
if_group_breaks(&text("\\")), if_group_breaks(&token("\\")),
soft_line_break(), soft_line_break(),
]) ])
.entries(item.names.iter().formatted()) .entries(item.names.iter().formatted())
@ -36,10 +36,10 @@ impl FormatNodeRule<StmtGlobal> for FormatStmtGlobal {
write!( write!(
f, f,
[ [
text("global"), token("global"),
space(), space(),
group(&format_args!( group(&format_args!(
if_group_breaks(&text("\\")), if_group_breaks(&token("\\")),
soft_line_break(), soft_line_break(),
soft_block_indent(&joined) soft_block_indent(&joined)
)) ))

View file

@ -30,7 +30,7 @@ impl FormatNodeRule<StmtIf> for FormatStmtIf {
ClauseHeader::If(item), ClauseHeader::If(item),
trailing_colon_comment, trailing_colon_comment,
&format_args![ &format_args![
text("if"), token("if"),
space(), space(),
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
], ],
@ -86,13 +86,13 @@ pub(crate) fn format_elif_else_clause(
write!( write!(
f, f,
[ [
text("elif"), token("elif"),
space(), space(),
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
] ]
) )
} else { } else {
text("else").fmt(f) token("else").fmt(f)
} }
}), }),
) )

View file

@ -11,11 +11,11 @@ impl FormatNodeRule<StmtImport> for FormatStmtImport {
fn fmt_fields(&self, item: &StmtImport, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &StmtImport, f: &mut PyFormatter) -> FormatResult<()> {
let StmtImport { names, range: _ } = item; let StmtImport { names, range: _ } = item;
let names = format_with(|f| { let names = format_with(|f| {
f.join_with(&format_args![text(","), space()]) f.join_with(&format_args![token(","), space()])
.entries(names.iter().formatted()) .entries(names.iter().formatted())
.finish() .finish()
}); });
write!(f, [text("import"), space(), names]) write!(f, [token("import"), space(), names])
} }
fn is_suppressed( fn is_suppressed(

View file

@ -27,12 +27,12 @@ impl FormatNodeRule<StmtImportFrom> for FormatStmtImportFrom {
write!( write!(
f, f,
[ [
text("from"), token("from"),
space(), space(),
dynamic_text(&level_str, None), text(&level_str, None),
module.as_ref().map(AsFormat::format), module.as_ref().map(AsFormat::format),
space(), space(),
text("import"), token("import"),
space(), space(),
] ]
)?; )?;
@ -40,7 +40,7 @@ impl FormatNodeRule<StmtImportFrom> for FormatStmtImportFrom {
if let [name] = names.as_slice() { if let [name] = names.as_slice() {
// star can't be surrounded by parentheses // star can't be surrounded by parentheses
if name.name.as_str() == "*" { if name.name.as_str() == "*" {
return text("*").fmt(f); return token("*").fmt(f);
} }
} }

View file

@ -29,7 +29,7 @@ impl FormatNodeRule<StmtMatch> for FormatStmtMatch {
ClauseHeader::Match(item), ClauseHeader::Match(item),
dangling_item_comments, dangling_item_comments,
&format_args![ &format_args![
text("match"), token("match"),
space(), space(),
maybe_parenthesize_expression(subject, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(subject, item, Parenthesize::IfBreaks),
], ],

View file

@ -15,18 +15,18 @@ impl FormatNodeRule<StmtNonlocal> for FormatStmtNonlocal {
// move the comment "off" of the `nonlocal` statement. // move the comment "off" of the `nonlocal` statement.
if f.context().comments().has_trailing(item.as_any_node_ref()) { if f.context().comments().has_trailing(item.as_any_node_ref()) {
let joined = format_with(|f| { let joined = format_with(|f| {
f.join_with(format_args![text(","), space()]) f.join_with(format_args![token(","), space()])
.entries(item.names.iter().formatted()) .entries(item.names.iter().formatted())
.finish() .finish()
}); });
write!(f, [text("nonlocal"), space(), &joined]) write!(f, [token("nonlocal"), space(), &joined])
} else { } else {
let joined = format_with(|f| { let joined = format_with(|f| {
f.join_with(&format_args![ f.join_with(&format_args![
text(","), token(","),
space(), space(),
if_group_breaks(&text("\\")), if_group_breaks(&token("\\")),
soft_line_break(), soft_line_break(),
]) ])
.entries(item.names.iter().formatted()) .entries(item.names.iter().formatted())
@ -36,10 +36,10 @@ impl FormatNodeRule<StmtNonlocal> for FormatStmtNonlocal {
write!( write!(
f, f,
[ [
text("nonlocal"), token("nonlocal"),
space(), space(),
group(&format_args!( group(&format_args!(
if_group_breaks(&text("\\")), if_group_breaks(&token("\\")),
soft_line_break(), soft_line_break(),
soft_block_indent(&joined) soft_block_indent(&joined)
)) ))

View file

@ -8,7 +8,7 @@ pub struct FormatStmtPass;
impl FormatNodeRule<StmtPass> for FormatStmtPass { impl FormatNodeRule<StmtPass> for FormatStmtPass {
fn fmt_fields(&self, _item: &StmtPass, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, _item: &StmtPass, f: &mut PyFormatter) -> FormatResult<()> {
text("pass").fmt(f) token("pass").fmt(f)
} }
fn is_suppressed( fn is_suppressed(

View file

@ -17,7 +17,7 @@ impl FormatNodeRule<StmtRaise> for FormatStmtRaise {
cause, cause,
} = item; } = item;
text("raise").fmt(f)?; token("raise").fmt(f)?;
if let Some(value) = exc { if let Some(value) = exc {
write!( write!(
@ -34,7 +34,7 @@ impl FormatNodeRule<StmtRaise> for FormatStmtRaise {
f, f,
[ [
space(), space(),
text("from"), token("from"),
space(), space(),
maybe_parenthesize_expression(value, item, Parenthesize::Optional) maybe_parenthesize_expression(value, item, Parenthesize::Optional)
] ]

View file

@ -14,7 +14,7 @@ impl FormatNodeRule<StmtReturn> for FormatStmtReturn {
fn fmt_fields(&self, item: &StmtReturn, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &StmtReturn, f: &mut PyFormatter) -> FormatResult<()> {
let StmtReturn { range: _, value } = item; let StmtReturn { range: _, value } = item;
text("return").fmt(f)?; token("return").fmt(f)?;
match value.as_deref() { match value.as_deref() {
Some(Expr::Tuple(tuple)) if !f.context().comments().has_leading(tuple) => { Some(Expr::Tuple(tuple)) if !f.context().comments().has_leading(tuple) => {

View file

@ -136,7 +136,7 @@ fn format_case<'a>(
write!( write!(
f, f,
[ [
clause_header(header, trailing_case_comments, &text(kind.keyword())) clause_header(header, trailing_case_comments, &token(kind.keyword()))
.with_leading_comments(leading_case_comments, previous_node), .with_leading_comments(leading_case_comments, previous_node),
clause_body(body, trailing_case_comments), clause_body(body, trailing_case_comments),
] ]

View file

@ -18,7 +18,7 @@ impl FormatNodeRule<StmtTypeAlias> for FormatStmtTypeAlias {
range: _, range: _,
} = item; } = item;
write!(f, [text("type"), space(), name.as_ref().format()])?; write!(f, [token("type"), space(), name.as_ref().format()])?;
if let Some(type_params) = type_params { if let Some(type_params) = type_params {
write!(f, [type_params.format()])?; write!(f, [type_params.format()])?;
@ -28,7 +28,7 @@ impl FormatNodeRule<StmtTypeAlias> for FormatStmtTypeAlias {
f, f,
[ [
space(), space(),
text("="), token("="),
space(), space(),
maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks) maybe_parenthesize_expression(value, item, Parenthesize::IfBreaks)
] ]

View file

@ -38,7 +38,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
ClauseHeader::While(item), ClauseHeader::While(item),
trailing_condition_comments, trailing_condition_comments,
&format_args![ &format_args![
text("while"), token("while"),
space(), space(),
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks), maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
] ]
@ -60,7 +60,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
clause_header( clause_header(
ClauseHeader::OrElse(ElseClause::While(item)), ClauseHeader::OrElse(ElseClause::While(item)),
trailing, trailing,
&text("else") &token("else")
) )
.with_leading_comments(leading, body.last()), .with_leading_comments(leading, body.last()),
clause_body(orelse, trailing), clause_body(orelse, trailing),

View file

@ -52,8 +52,8 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
f, f,
[ [
item.is_async item.is_async
.then_some(format_args![text("async"), space()]), .then_some(format_args![token("async"), space()]),
text("with"), token("with"),
space() space()
] ]
)?; )?;
@ -92,7 +92,7 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
item.format().fmt(f)?; item.format().fmt(f)?;
} }
} else { } else {
f.join_with(format_args![text(","), space()]) f.join_with(format_args![token(","), space()])
.entries(item.items.iter().formatted()) .entries(item.items.iter().formatted())
.finish()?; .finish()?;
} }

View file

@ -9,6 +9,6 @@ pub struct FormatTypeParamParamSpec;
impl FormatNodeRule<TypeParamParamSpec> for FormatTypeParamParamSpec { impl FormatNodeRule<TypeParamParamSpec> for FormatTypeParamParamSpec {
fn fmt_fields(&self, item: &TypeParamParamSpec, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &TypeParamParamSpec, f: &mut PyFormatter) -> FormatResult<()> {
let TypeParamParamSpec { range: _, name } = item; let TypeParamParamSpec { range: _, name } = item;
write!(f, [text("**"), name.format()]) write!(f, [token("**"), name.format()])
} }
} }

View file

@ -15,7 +15,7 @@ impl FormatNodeRule<TypeParamTypeVar> for FormatTypeParamTypeVar {
} = item; } = item;
name.format().fmt(f)?; name.format().fmt(f)?;
if let Some(bound) = bound { if let Some(bound) = bound {
write!(f, [text(":"), space(), bound.format()])?; write!(f, [token(":"), space(), bound.format()])?;
} }
Ok(()) Ok(())
} }

View file

@ -9,6 +9,6 @@ pub struct FormatTypeParamTypeVarTuple;
impl FormatNodeRule<TypeParamTypeVarTuple> for FormatTypeParamTypeVarTuple { impl FormatNodeRule<TypeParamTypeVarTuple> for FormatTypeParamTypeVarTuple {
fn fmt_fields(&self, item: &TypeParamTypeVarTuple, f: &mut PyFormatter) -> FormatResult<()> { fn fmt_fields(&self, item: &TypeParamTypeVarTuple, f: &mut PyFormatter) -> FormatResult<()> {
let TypeParamTypeVarTuple { range: _, name } = item; let TypeParamTypeVarTuple { range: _, name } = item;
write!(f, [text("*"), name.format()]) write!(f, [token("*"), name.format()])
} }
} }

View file

@ -871,7 +871,7 @@ impl Format<PyFormatContext<'_>> for VerbatimText {
write!( write!(
f, f,
[ [
dynamic_text(&cleaned, Some(self.verbatim_range.start())), text(&cleaned, Some(self.verbatim_range.start())),
source_position(self.verbatim_range.end()) source_position(self.verbatim_range.end())
] ]
)?; )?;