From c97caf1c75b93b846196f2a3724dff0f00d228ed Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Sat, 8 Jul 2023 10:01:04 +0200 Subject: [PATCH] Use new IR to format expressions --- crates/ruff_python_formatter/src/builders.rs | 29 ++- .../src/comments/format.rs | 4 +- crates/ruff_python_formatter/src/context.rs | 9 +- .../src/expression/binary_like.rs | 216 ------------------ .../src/expression/expr_bin_op.rs | 55 +---- .../src/expression/expr_bool_op.rs | 107 +++------ .../src/expression/expr_compare.rs | 73 ++---- .../src/expression/mod.rs | 42 ++-- .../src/expression/parentheses.rs | 60 ++++- crates/ruff_python_formatter/src/lib.rs | 17 +- .../src/other/arguments.rs | 3 +- ...tibility@simple_cases__composition.py.snap | 12 +- ...ses__composition_no_trailing_comma.py.snap | 12 +- ...tibility@simple_cases__empty_lines.py.snap | 131 ++--------- ...@simple_cases__remove_await_parens.py.snap | 97 ++------ ...ompatibility@simple_cases__torture.py.snap | 83 ++----- ...s__trailing_comma_optional_parens1.py.snap | 56 +---- .../format@expression__binary.py.snap | 6 +- .../format@expression__compare.py.snap | 53 ++--- .../format@expression__unary.py.snap | 11 +- 20 files changed, 293 insertions(+), 783 deletions(-) delete mode 100644 crates/ruff_python_formatter/src/expression/binary_like.rs diff --git a/crates/ruff_python_formatter/src/builders.rs b/crates/ruff_python_formatter/src/builders.rs index 4541acda740eec..1cacfde44b0eae 100644 --- a/crates/ruff_python_formatter/src/builders.rs +++ b/crates/ruff_python_formatter/src/builders.rs @@ -19,14 +19,31 @@ pub(crate) struct OptionalParentheses<'a, 'ast> { inner: Argument<'a, PyFormatContext<'ast>>, } +// TODO move this advanced logic to `FormatExpr` instead? I dont think it should apply to other nodes impl<'ast> Format> for OptionalParentheses<'_, 'ast> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { - group(&format_args![ + let saved_level = f.context().node_level(); + + let parens_id = f.group_id("optional_parentheses"); + + f.context_mut() + .set_node_level(NodeLevel::Expression(Some(parens_id))); + + let result = group(&format_args![ if_group_breaks(&text("(")), - soft_block_indent(&Arguments::from(&self.inner)), + indent_if_group_breaks( + &format_args![soft_line_break(), Arguments::from(&self.inner)], + parens_id + ), + soft_line_break(), if_group_breaks(&text(")")) ]) - .fmt(f) + .with_group_id(Some(parens_id)) + .fmt(f); + + f.context_mut().set_node_level(saved_level); + + result } } @@ -113,7 +130,9 @@ impl<'fmt, 'ast, 'buf> JoinNodesBuilder<'fmt, 'ast, 'buf> { 0 | 1 => hard_line_break().fmt(self.fmt), _ => empty_line().fmt(self.fmt), }, - NodeLevel::Expression => hard_line_break().fmt(self.fmt), + NodeLevel::Expression(_) | NodeLevel::ParenthesizedExpression => { + hard_line_break().fmt(self.fmt) + } }?; } @@ -353,7 +372,7 @@ no_leading_newline = 30"# // Removes all empty lines #[test] fn ranged_builder_parenthesized_level() { - let printed = format_ranged(NodeLevel::Expression); + let printed = format_ranged(NodeLevel::Expression(None)); assert_eq!( &printed, diff --git a/crates/ruff_python_formatter/src/comments/format.rs b/crates/ruff_python_formatter/src/comments/format.rs index 31c7e42cd133a7..c0a0a2a5bd3fc5 100644 --- a/crates/ruff_python_formatter/src/comments/format.rs +++ b/crates/ruff_python_formatter/src/comments/format.rs @@ -318,7 +318,9 @@ impl Format> for FormatEmptyLines { }, // Remove all whitespace in parenthesized expressions - NodeLevel::Expression => write!(f, [hard_line_break()]), + NodeLevel::Expression(_) | NodeLevel::ParenthesizedExpression => { + write!(f, [hard_line_break()]) + } } } } diff --git a/crates/ruff_python_formatter/src/context.rs b/crates/ruff_python_formatter/src/context.rs index cdf587c35fd9a2..2683641dd861ad 100644 --- a/crates/ruff_python_formatter/src/context.rs +++ b/crates/ruff_python_formatter/src/context.rs @@ -1,6 +1,6 @@ use crate::comments::Comments; use crate::PyFormatOptions; -use ruff_formatter::{FormatContext, SourceCode}; +use ruff_formatter::{FormatContext, GroupId, SourceCode}; use ruff_python_ast::source_code::Locator; use std::fmt::{Debug, Formatter}; @@ -78,6 +78,9 @@ pub(crate) enum NodeLevel { /// (`if`, `while`, `match`, etc.). CompoundStatement, - /// Formatting nodes that are enclosed in a parenthesized expression. - Expression, + /// The root or any sub-expression. + Expression(Option), + + /// Formatting nodes that are enclosed by a parenthesized (any `[]`, `{}` or `()`) expression. + ParenthesizedExpression, } diff --git a/crates/ruff_python_formatter/src/expression/binary_like.rs b/crates/ruff_python_formatter/src/expression/binary_like.rs deleted file mode 100644 index c934322e55a64b..00000000000000 --- a/crates/ruff_python_formatter/src/expression/binary_like.rs +++ /dev/null @@ -1,216 +0,0 @@ -//! This module provides helper utilities to format an expression that has a left side, an operator, -//! and a right side (binary like). - -use rustpython_parser::ast::{self, Expr}; - -use ruff_formatter::{format_args, write}; - -use crate::expression::parentheses::{is_expression_parenthesized, Parentheses}; -use crate::prelude::*; - -/// Trait to implement a binary like syntax that has a left operand, an operator, and a right operand. -pub(super) trait FormatBinaryLike<'ast> { - /// The type implementing the formatting of the operator. - type FormatOperator: Format>; - - /// Formats the binary like expression to `f`. - fn fmt_binary( - &self, - parentheses: Option, - f: &mut PyFormatter<'ast, '_>, - ) -> FormatResult<()> { - let left = self.left()?; - let operator = self.operator(); - let right = self.right()?; - - let layout = if parentheses == Some(Parentheses::Custom) { - self.binary_layout(f.context().contents()) - } else { - BinaryLayout::Default - }; - - match layout { - BinaryLayout::Default => self.fmt_default(f), - BinaryLayout::ExpandLeft => { - let left = left.format().memoized(); - let right = right.format().memoized(); - write!( - f, - [best_fitting![ - // Everything on a single line - format_args![group(&left), space(), operator, space(), right], - // Break the left over multiple lines, keep the right flat - format_args![ - group(&left).should_expand(true), - space(), - operator, - space(), - right - ], - // The content doesn't fit, indent the content and break before the operator. - format_args![ - text("("), - block_indent(&format_args![ - left, - hard_line_break(), - operator, - space(), - right - ]), - text(")") - ] - ] - .with_mode(BestFittingMode::AllLines)] - ) - } - BinaryLayout::ExpandRight => { - let left_group = f.group_id("BinaryLeft"); - - write!( - f, - [ - // Wrap the left in a group and gives it an id. The printer first breaks the - // right side if `right` contains any line break because the printer breaks - // sequences of groups from right to left. - // Indents the left side if the group breaks. - group(&format_args![ - if_group_breaks(&text("(")), - indent_if_group_breaks( - &format_args![ - soft_line_break(), - left.format(), - soft_line_break_or_space(), - operator, - space() - ], - left_group - ) - ]) - .with_group_id(Some(left_group)), - // Wrap the right in a group and indents its content but only if the left side breaks - group(&indent_if_group_breaks(&right.format(), left_group)), - // If the left side breaks, insert a hard line break to finish the indent and close the open paren. - if_group_breaks(&format_args![hard_line_break(), text(")")]) - .with_group_id(Some(left_group)) - ] - ) - } - BinaryLayout::ExpandRightThenLeft => { - // The formatter expands group-sequences from right to left, and expands both if - // there isn't enough space when expanding only one of them. - write!( - f, - [ - group(&left.format()), - space(), - operator, - space(), - group(&right.format()) - ] - ) - } - } - } - - /// Determines which binary layout to use. - fn binary_layout(&self, source: &str) -> BinaryLayout { - if let (Ok(left), Ok(right)) = (self.left(), self.right()) { - BinaryLayout::from_left_right(left, right, source) - } else { - BinaryLayout::Default - } - } - - /// Formats the node according to the default layout. - fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()>; - - /// Returns the left operator - fn left(&self) -> FormatResult<&Expr>; - - /// Returns the right operator. - fn right(&self) -> FormatResult<&Expr>; - - /// Returns the object that formats the operator. - fn operator(&self) -> Self::FormatOperator; -} - -fn can_break_expr(expr: &Expr, source: &str) -> bool { - let can_break = match expr { - Expr::Tuple(ast::ExprTuple { - elts: expressions, .. - }) - | Expr::List(ast::ExprList { - elts: expressions, .. - }) - | Expr::Set(ast::ExprSet { - elts: expressions, .. - }) - | Expr::Dict(ast::ExprDict { - values: expressions, - .. - }) => !expressions.is_empty(), - Expr::Call(ast::ExprCall { args, keywords, .. }) => { - !(args.is_empty() && keywords.is_empty()) - } - Expr::ListComp(_) | Expr::SetComp(_) | Expr::DictComp(_) | Expr::GeneratorExp(_) => true, - Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => can_break_expr(operand.as_ref(), source), - _ => false, - }; - - can_break || is_expression_parenthesized(expr.into(), source) -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub(super) enum BinaryLayout { - /// Put each operand on their own line if either side expands - Default, - - /// Try to expand the left to make it fit. Add parentheses if the left or right don't fit. - /// - ///```python - /// [ - /// a, - /// b - /// ] & c - ///``` - ExpandLeft, - - /// Try to expand the right to make it fix. Add parentheses if the left or right don't fit. - /// - /// ```python - /// a & [ - /// b, - /// c - /// ] - /// ``` - ExpandRight, - - /// Both the left and right side can be expanded. Try in the following order: - /// * expand the right side - /// * expand the left side - /// * expand both sides - /// - /// to make the expression fit - /// - /// ```python - /// [ - /// a, - /// b - /// ] & [ - /// c, - /// d - /// ] - /// ``` - ExpandRightThenLeft, -} - -impl BinaryLayout { - pub(super) fn from_left_right(left: &Expr, right: &Expr, source: &str) -> BinaryLayout { - match (can_break_expr(left, source), can_break_expr(right, source)) { - (false, false) => BinaryLayout::Default, - (true, false) => BinaryLayout::ExpandLeft, - (false, true) => BinaryLayout::ExpandRight, - (true, true) => BinaryLayout::ExpandRightThenLeft, - } - } -} diff --git a/crates/ruff_python_formatter/src/expression/expr_bin_op.rs b/crates/ruff_python_formatter/src/expression/expr_bin_op.rs index 859e03ce4f2d36..9307ed204320d8 100644 --- a/crates/ruff_python_formatter/src/expression/expr_bin_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_bin_op.rs @@ -1,8 +1,7 @@ use crate::comments::{trailing_comments, trailing_node_comments, Comments}; -use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike}; use crate::expression::parentheses::{ - default_expression_needs_parentheses, is_expression_parenthesized, NeedsParentheses, - Parenthesize, + default_expression_needs_parentheses, in_parentheses_only_group, is_expression_parenthesized, + NeedsParentheses, Parenthesize, }; use crate::expression::Parentheses; use crate::prelude::*; @@ -31,24 +30,11 @@ impl FormatRuleWithOptions> for FormatExprBinOp { impl FormatNodeRule for FormatExprBinOp { fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> { - item.fmt_binary(self.parentheses, f) - } - - fn fmt_dangling_comments(&self, _node: &ExprBinOp, _f: &mut PyFormatter) -> FormatResult<()> { - // Handled inside of `fmt_fields` - Ok(()) - } -} - -impl<'ast> FormatBinaryLike<'ast> for ExprBinOp { - type FormatOperator = FormatOwnedWithRule>; - - fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> { let comments = f.context().comments().clone(); let format_inner = format_with(|f: &mut PyFormatter| { let source = f.context().contents(); - let binary_chain: SmallVec<[&ExprBinOp; 4]> = iter::successors(Some(self), |parent| { + let binary_chain: SmallVec<[&ExprBinOp; 4]> = iter::successors(Some(item), |parent| { parent.left.as_bin_op_expr().and_then(|bin_expression| { if is_expression_parenthesized(bin_expression.as_any_node_ref(), source) { None @@ -63,7 +49,7 @@ impl<'ast> FormatBinaryLike<'ast> for ExprBinOp { let left_most = binary_chain.last().unwrap(); // Format the left most expression - group(&left_most.left.format()).fmt(f)?; + in_parentheses_only_group(&left_most.left.format()).fmt(f)?; // Iterate upwards in the binary expression tree and, for each level, format the operator // and the right expression. @@ -100,13 +86,13 @@ impl<'ast> FormatBinaryLike<'ast> for ExprBinOp { space().fmt(f)?; } - group(&right.format()).fmt(f)?; + in_parentheses_only_group(&right.format()).fmt(f)?; // It's necessary to format the trailing comments because the code bypasses // `FormatNodeRule::fmt` for the nested binary expressions. // Don't call the formatting function for the most outer binary expression because // these comments have already been formatted. - if current != self { + if current != item { trailing_node_comments(current).fmt(f)?; } } @@ -114,19 +100,12 @@ impl<'ast> FormatBinaryLike<'ast> for ExprBinOp { Ok(()) }); - group(&format_inner).fmt(f) + in_parentheses_only_group(&format_inner).fmt(f) } - fn left(&self) -> FormatResult<&Expr> { - Ok(&self.left) - } - - fn right(&self) -> FormatResult<&Expr> { - Ok(&self.right) - } - - fn operator(&self) -> Self::FormatOperator { - self.op.into_format() + fn fmt_dangling_comments(&self, _node: &ExprBinOp, _f: &mut PyFormatter) -> FormatResult<()> { + // Handled inside of `fmt_fields` + Ok(()) } } @@ -200,18 +179,6 @@ impl NeedsParentheses for ExprBinOp { source: &str, comments: &Comments, ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) { - Parentheses::Optional => { - if self.binary_layout(source) == BinaryLayout::Default - || comments.has_leading_comments(self.right.as_ref()) - || comments.has_dangling_comments(self) - { - Parentheses::Optional - } else { - Parentheses::Custom - } - } - parentheses => parentheses, - } + default_expression_needs_parentheses(self.into(), parenthesize, source, comments) } } diff --git a/crates/ruff_python_formatter/src/expression/expr_bool_op.rs b/crates/ruff_python_formatter/src/expression/expr_bool_op.rs index 8fb9b42eb22075..5a350645095ce0 100644 --- a/crates/ruff_python_formatter/src/expression/expr_bool_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_bool_op.rs @@ -1,13 +1,11 @@ use crate::comments::{leading_comments, Comments}; -use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike}; use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, + default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, + Parenthesize, }; use crate::prelude::*; -use ruff_formatter::{ - write, FormatError, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions, -}; -use rustpython_parser::ast::{BoolOp, Expr, ExprBoolOp}; +use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions}; +use rustpython_parser::ast::{BoolOp, ExprBoolOp}; #[derive(Default)] pub struct FormatExprBoolOp { @@ -24,64 +22,48 @@ impl FormatRuleWithOptions> for FormatExprBoolOp impl FormatNodeRule for FormatExprBoolOp { fn fmt_fields(&self, item: &ExprBoolOp, f: &mut PyFormatter) -> FormatResult<()> { - item.fmt_binary(self.parentheses, f) - } -} - -impl<'ast> FormatBinaryLike<'ast> for ExprBoolOp { - type FormatOperator = FormatOwnedWithRule>; - - fn binary_layout(&self, source: &str) -> BinaryLayout { - match self.values.as_slice() { - [left, right] => BinaryLayout::from_left_right(left, right, source), - [..] => BinaryLayout::Default, - } - } - - fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> { let ExprBoolOp { range: _, op, values, - } = self; - - let mut values = values.iter(); - let comments = f.context().comments().clone(); - - let Some(first) = values.next() else { - return Ok(()); - }; - - write!(f, [group(&first.format())])?; + } = item; + + let inner = format_with(|f: &mut PyFormatter| { + let mut values = values.iter(); + let comments = f.context().comments().clone(); + + let Some(first) = values.next() else { + return Ok(()); + }; + + write!(f, [in_parentheses_only_group(&first.format())])?; + + for value in values { + let leading_value_comments = comments.leading_comments(value); + // Format the expressions leading comments **before** the operator + if leading_value_comments.is_empty() { + write!(f, [soft_line_break_or_space()])?; + } else { + write!( + f, + [hard_line_break(), leading_comments(leading_value_comments)] + )?; + } - for value in values { - let leading_value_comments = comments.leading_comments(value); - // Format the expressions leading comments **before** the operator - if leading_value_comments.is_empty() { - write!(f, [soft_line_break_or_space()])?; - } else { write!( f, - [hard_line_break(), leading_comments(leading_value_comments)] + [ + op.format(), + space(), + in_parentheses_only_group(&value.format()) + ] )?; } - write!(f, [op.format(), space(), group(&value.format())])?; - } + Ok(()) + }); - Ok(()) - } - - fn left(&self) -> FormatResult<&Expr> { - self.values.first().ok_or(FormatError::SyntaxError) - } - - fn right(&self) -> FormatResult<&Expr> { - self.values.last().ok_or(FormatError::SyntaxError) - } - - fn operator(&self) -> Self::FormatOperator { - self.op.into_format() + in_parentheses_only_group(&inner).fmt(f) } } @@ -92,24 +74,7 @@ impl NeedsParentheses for ExprBoolOp { source: &str, comments: &Comments, ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) { - Parentheses::Optional => match self.binary_layout(source) { - BinaryLayout::Default => Parentheses::Optional, - - BinaryLayout::ExpandRight - | BinaryLayout::ExpandLeft - | BinaryLayout::ExpandRightThenLeft - if self - .values - .last() - .map_or(false, |right| comments.has_leading_comments(right)) => - { - Parentheses::Optional - } - _ => Parentheses::Custom, - }, - parentheses => parentheses, - } + default_expression_needs_parentheses(self.into(), parenthesize, source, comments) } } diff --git a/crates/ruff_python_formatter/src/expression/expr_compare.rs b/crates/ruff_python_formatter/src/expression/expr_compare.rs index 094344be614928..10bffe948b0568 100644 --- a/crates/ruff_python_formatter/src/expression/expr_compare.rs +++ b/crates/ruff_python_formatter/src/expression/expr_compare.rs @@ -1,14 +1,11 @@ use crate::comments::{leading_comments, Comments}; -use crate::expression::binary_like::{BinaryLayout, FormatBinaryLike}; use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, + default_expression_needs_parentheses, in_parentheses_only_group, NeedsParentheses, Parentheses, + Parenthesize, }; use crate::prelude::*; use crate::FormatNodeRule; -use ruff_formatter::{ - write, FormatError, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions, -}; -use rustpython_parser::ast::Expr; +use ruff_formatter::{write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions}; use rustpython_parser::ast::{CmpOp, ExprCompare}; #[derive(Default)] @@ -27,35 +24,16 @@ impl FormatRuleWithOptions> for FormatExprCompa impl FormatNodeRule for FormatExprCompare { fn fmt_fields(&self, item: &ExprCompare, f: &mut PyFormatter) -> FormatResult<()> { - item.fmt_binary(self.parentheses, f) - } -} - -impl<'ast> FormatBinaryLike<'ast> for ExprCompare { - type FormatOperator = FormatOwnedWithRule>; - - fn binary_layout(&self, source: &str) -> BinaryLayout { - if self.ops.len() == 1 { - match self.comparators.as_slice() { - [right] => BinaryLayout::from_left_right(&self.left, right, source), - [..] => BinaryLayout::Default, - } - } else { - BinaryLayout::Default - } - } - - fn fmt_default(&self, f: &mut PyFormatter<'ast, '_>) -> FormatResult<()> { let ExprCompare { range: _, left, ops, comparators, - } = self; + } = item; let comments = f.context().comments().clone(); - write!(f, [group(&left.format())])?; + write!(f, [in_parentheses_only_group(&left.format())])?; assert_eq!(comparators.len(), ops.len()); @@ -74,24 +52,18 @@ impl<'ast> FormatBinaryLike<'ast> for ExprCompare { )?; } - write!(f, [operator.format(), space(), group(&comparator.format())])?; + write!( + f, + [ + operator.format(), + space(), + in_parentheses_only_group(&comparator.format()) + ] + )?; } Ok(()) } - - fn left(&self) -> FormatResult<&Expr> { - Ok(self.left.as_ref()) - } - - fn right(&self) -> FormatResult<&Expr> { - self.comparators.last().ok_or(FormatError::SyntaxError) - } - - fn operator(&self) -> Self::FormatOperator { - let op = *self.ops.first().unwrap(); - op.into_format() - } } impl NeedsParentheses for ExprCompare { @@ -101,24 +73,7 @@ impl NeedsParentheses for ExprCompare { source: &str, comments: &Comments, ) -> Parentheses { - match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) { - parentheses @ Parentheses::Optional => match self.binary_layout(source) { - BinaryLayout::Default => parentheses, - - BinaryLayout::ExpandRight - | BinaryLayout::ExpandLeft - | BinaryLayout::ExpandRightThenLeft - if self - .comparators - .last() - .map_or(false, |right| comments.has_leading_comments(right)) => - { - parentheses - } - _ => Parentheses::Custom, - }, - parentheses => parentheses, - } + default_expression_needs_parentheses(self.into(), parenthesize, source, comments) } } diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index ac3a4d682c6370..99fc86e616c143 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -2,15 +2,12 @@ use crate::builders::optional_parentheses; use crate::comments::Comments; use crate::context::NodeLevel; use crate::expression::expr_tuple::TupleParentheses; -use crate::expression::parentheses::{NeedsParentheses, Parentheses, Parenthesize}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, Parentheses, Parenthesize}; use crate::expression::string::StringLayout; use crate::prelude::*; -use ruff_formatter::{ - format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions, -}; +use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions}; use rustpython_parser::ast::Expr; -pub(crate) mod binary_like; pub(crate) mod expr_attribute; pub(crate) mod expr_await; pub(crate) mod expr_bin_op; @@ -99,26 +96,29 @@ impl FormatRule> for FormatExpr { Expr::Slice(expr) => expr.format().fmt(f), }); - let saved_level = f.context().node_level(); - f.context_mut().set_node_level(NodeLevel::Expression); - let result = match parentheses { - Parentheses::Always => { - write!( - f, - [group(&format_args![ - text("("), - soft_block_indent(&format_expr), - text(")") - ])] - ) - } + Parentheses::Always => parenthesized("(", &format_expr, ")").fmt(f), // Add optional parentheses. Ignore if the item renders parentheses itself. Parentheses::Optional => optional_parentheses(&format_expr).fmt(f), - Parentheses::Custom | Parentheses::Never => Format::fmt(&format_expr, f), - }; + Parentheses::Custom | Parentheses::Never => { + let saved_level = f.context().node_level(); + + let new_level = match saved_level { + NodeLevel::TopLevel | NodeLevel::CompoundStatement => { + NodeLevel::Expression(None) + } + level @ (NodeLevel::Expression(_) | NodeLevel::ParenthesizedExpression) => { + level + } + }; - f.context_mut().set_node_level(saved_level); + f.context_mut().set_node_level(new_level); + + let result = Format::fmt(&format_expr, f); + f.context_mut().set_node_level(saved_level); + result + } + }; result } diff --git a/crates/ruff_python_formatter/src/expression/parentheses.rs b/crates/ruff_python_formatter/src/expression/parentheses.rs index c73b5773ce4080..a3087eebe43cda 100644 --- a/crates/ruff_python_formatter/src/expression/parentheses.rs +++ b/crates/ruff_python_formatter/src/expression/parentheses.rs @@ -1,7 +1,9 @@ use crate::comments::Comments; +use crate::context::NodeLevel; use crate::prelude::*; use crate::trivia::{first_non_trivia_token, first_non_trivia_token_rev, Token, TokenKind}; -use ruff_formatter::{format_args, write, Argument, Arguments}; +use ruff_formatter::prelude::tag::Condition; +use ruff_formatter::{format_args, Argument, Arguments}; use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::Ranged; @@ -145,13 +147,59 @@ pub(crate) struct FormatParenthesized<'content, 'ast> { impl<'ast> Format> for FormatParenthesized<'_, 'ast> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { - write!( - f, - [group(&format_args![ + let inner = format_with(|f| { + group(&format_args![ text(self.left), &soft_block_indent(&Arguments::from(&self.content)), text(self.right) - ])] - ) + ]) + .fmt(f) + }); + + let current_level = f.context().node_level(); + + f.context_mut() + .set_node_level(NodeLevel::ParenthesizedExpression); + + let result = if let NodeLevel::Expression(Some(group_id)) = current_level { + fits_expanded(&inner) + .with_condition(Some(Condition::if_group_fits_on_line(group_id))) + .fmt(f) + } else { + inner.fmt(f) + }; + + f.context_mut().set_node_level(current_level); + + result + } +} + +pub(crate) fn in_parentheses_only_group<'content, 'ast, Content>( + content: &'content Content, +) -> FormatInParenthesesOnlyGroup<'content, 'ast> +where + Content: Format>, +{ + FormatInParenthesesOnlyGroup { + content: Argument::new(content), + } +} + +pub(crate) struct FormatInParenthesesOnlyGroup<'content, 'ast> { + content: Argument<'content, PyFormatContext<'ast>>, +} + +impl<'ast> Format> for FormatInParenthesesOnlyGroup<'_, 'ast> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + if let NodeLevel::Expression(Some(group_id)) = f.context().node_level() { + conditional_group( + &Arguments::from(&self.content), + Condition::if_group_breaks(group_id), + ) + .fmt(f) + } else { + group(&Arguments::from(&self.content)).fmt(f) + } } } diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index dcdf4497950be5..f55824c5631050 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -280,12 +280,9 @@ if True: #[test] fn quick_test() { let src = r#" -if [ - aaaaaa, - BBBB,ccccccccc,ddddddd,eeeeeeeeee,ffffff -] & bbbbbbbbbbbbbbbbbbddddddddddddddddddddddddddddbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: - ... -"#; +[ + AAAAAAAAAAAAA +] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAASAAAAAAAA"#; // Tokenize once let mut tokens = Vec::new(); let mut comment_ranges = CommentRangesBuilder::default(); @@ -312,10 +309,10 @@ if [ // Uncomment the `dbg` to print the IR. // Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR // inside of a `Format` implementation - // use ruff_formatter::FormatContext; - // dbg!(formatted - // .document() - // .display(formatted.context().source_code())); + use ruff_formatter::FormatContext; + dbg!(formatted + .document() + .display(formatted.context().source_code())); // // dbg!(formatted // .context() diff --git a/crates/ruff_python_formatter/src/other/arguments.rs b/crates/ruff_python_formatter/src/other/arguments.rs index e6d91f7b69fb1d..0682a25632c480 100644 --- a/crates/ruff_python_formatter/src/other/arguments.rs +++ b/crates/ruff_python_formatter/src/other/arguments.rs @@ -31,7 +31,8 @@ impl FormatNodeRule for FormatArguments { } = item; let saved_level = f.context().node_level(); - f.context_mut().set_node_level(NodeLevel::Expression); + f.context_mut() + .set_node_level(NodeLevel::ParenthesizedExpression); let comments = f.context().comments().clone(); let dangling = comments.dangling_comments(item); diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap index 399e4ab91541fa..d6de11c9cae1f0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap @@ -203,7 +203,7 @@ class C: print(i) xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( push_manager=context.request.resource_manager, -@@ -47,113 +47,46 @@ +@@ -47,10 +47,20 @@ def omitting_trailers(self) -> None: get_collection( hey_this_is_a_very_long_call, it_has_funny_attributes, really=True @@ -226,10 +226,7 @@ class C: d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ 22 ] - assignment = ( -- some.rather.elaborate.rule() and another.rule.ending_with.index[123] -+ some.rather.elaborate.rule() -+ and another.rule.ending_with.index[123] +@@ -59,101 +69,23 @@ ) def easy_asserts(self) -> None: @@ -340,7 +337,7 @@ class C: %3d 0 LOAD_FAST 1 (x) 2 LOAD_CONST 1 (1) 4 COMPARE_OP 2 (==) -@@ -161,21 +94,8 @@ +@@ -161,21 +93,8 @@ 8 STORE_ATTR 0 (x) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE @@ -437,8 +434,7 @@ class C: 22 ] assignment = ( - some.rather.elaborate.rule() - and another.rule.ending_with.index[123] + some.rather.elaborate.rule() and another.rule.ending_with.index[123] ) def easy_asserts(self) -> None: diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap index 69415159cec317..d4ca2fcc125c1a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap @@ -203,7 +203,7 @@ class C: print(i) xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( push_manager=context.request.resource_manager, -@@ -47,113 +47,46 @@ +@@ -47,10 +47,20 @@ def omitting_trailers(self) -> None: get_collection( hey_this_is_a_very_long_call, it_has_funny_attributes, really=True @@ -226,10 +226,7 @@ class C: d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ 22 ] - assignment = ( -- some.rather.elaborate.rule() and another.rule.ending_with.index[123] -+ some.rather.elaborate.rule() -+ and another.rule.ending_with.index[123] +@@ -59,101 +69,23 @@ ) def easy_asserts(self) -> None: @@ -340,7 +337,7 @@ class C: %3d 0 LOAD_FAST 1 (x) 2 LOAD_CONST 1 (1) 4 COMPARE_OP 2 (==) -@@ -161,21 +94,8 @@ +@@ -161,21 +93,8 @@ 8 STORE_ATTR 0 (x) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE @@ -437,8 +434,7 @@ class C: 22 ] assignment = ( - some.rather.elaborate.rule() - and another.rule.ending_with.index[123] + some.rather.elaborate.rule() and another.rule.ending_with.index[123] ) def easy_asserts(self) -> None: diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap index 598235141bf6eb..4cfde0d1a6179d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap @@ -113,61 +113,15 @@ def g(): prev = leaf.prev_sibling if not prev: -@@ -25,23 +25,31 @@ - return NO - - if prevp.type == token.EQUAL: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.argument, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.argument, -+ } -+ ): - return NO - - elif prevp.type == token.DOUBLESTAR: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.dictsetmaker, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.dictsetmaker, -+ } -+ ): - return NO - - -@@ -49,7 +57,6 @@ +@@ -48,7 +48,6 @@ + ############################################################################### # SECTION BECAUSE SECTIONS ############################################################################### - - + def g(): NO = "" - SPACE = " " -@@ -67,7 +74,7 @@ +@@ -67,7 +66,7 @@ return DOUBLESPACE # Another comment because more comments @@ -176,29 +130,6 @@ def g(): prev = leaf.prev_sibling if not prev: -@@ -79,11 +86,15 @@ - return NO - - if prevp.type == token.EQUAL: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.argument, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.argument, -+ } -+ ): - return NO ``` ## Ruff Output @@ -231,31 +162,23 @@ def f(): return NO if prevp.type == token.EQUAL: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.argument, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: return NO elif prevp.type == token.DOUBLESTAR: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.dictsetmaker, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: return NO @@ -292,17 +215,13 @@ def g(): return NO if prevp.type == token.EQUAL: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.argument, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: return NO ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap index d529f18f490880..2468093c5fa954 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap @@ -93,7 +93,7 @@ async def main(): ```diff --- Black +++ Ruff -@@ -8,59 +8,70 @@ +@@ -8,28 +8,33 @@ # Remove brackets for short coroutine/task async def main(): @@ -122,10 +122,8 @@ async def main(): async def main(): - await asyncio.sleep(1) # Hello -+ ( -+ await ( -+ asyncio.sleep(1) # Hello -+ ) ++ await ( ++ asyncio.sleep(1) # Hello + ) @@ -135,50 +133,7 @@ async def main(): # Long lines - async def main(): -- await asyncio.gather( -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -+ ( -+ await asyncio.gather( -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ ) - ) - - - # Same as above but with magic trailing comma in function - async def main(): -- await asyncio.gather( -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -+ ( -+ await asyncio.gather( -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ ) - ) - +@@ -60,7 +65,7 @@ # Cr@zY Br@ck3Tz async def main(): @@ -187,7 +142,7 @@ async def main(): # Keep brackets around non power operations and nested awaits -@@ -78,16 +89,16 @@ +@@ -78,16 +83,16 @@ async def main(): @@ -243,10 +198,8 @@ async def main(): async def main(): - ( - await ( - asyncio.sleep(1) # Hello - ) + await ( + asyncio.sleep(1) # Hello ) @@ -256,31 +209,27 @@ async def main(): # Long lines async def main(): - ( - await asyncio.gather( - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - ) + await asyncio.gather( + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), ) # Same as above but with magic trailing comma in function async def main(): - ( - await asyncio.gather( - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - ) + await asyncio.gather( + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), + asyncio.sleep(1), ) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap index 246fcc69bc0eee..058819773d2642 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap @@ -64,7 +64,7 @@ assert ( importA 0 -@@ -25,34 +15,33 @@ +@@ -25,9 +15,7 @@ class A: def foo(self): for _ in range(10): @@ -75,46 +75,7 @@ assert ( def test(self, othr): -- return 1 == 2 and ( -- name, -- description, -- self.default, -- self.selected, -- self.auto_generated, -- self.parameters, -- self.meta_data, -- self.schedule, -- ) == ( -- name, -- description, -- othr.default, -- othr.selected, -- othr.auto_generated, -- othr.parameters, -- othr.meta_data, -- othr.schedule, -+ return ( -+ 1 == 2 -+ and ( -+ name, -+ description, -+ self.default, -+ self.selected, -+ self.auto_generated, -+ self.parameters, -+ self.meta_data, -+ self.schedule, -+ ) -+ == ( -+ name, -+ description, -+ othr.default, -+ othr.selected, -+ othr.auto_generated, -+ othr.parameters, -+ othr.meta_data, -+ othr.schedule, -+ ) +@@ -52,7 +40,4 @@ ) @@ -149,28 +110,24 @@ class A: def test(self, othr): - return ( - 1 == 2 - and ( - name, - description, - self.default, - self.selected, - self.auto_generated, - self.parameters, - self.meta_data, - self.schedule, - ) - == ( - name, - description, - othr.default, - othr.selected, - othr.auto_generated, - othr.parameters, - othr.meta_data, - othr.schedule, - ) + return 1 == 2 and ( + name, + description, + self.default, + self.selected, + self.auto_generated, + self.parameters, + self.meta_data, + self.schedule, + ) == ( + name, + description, + othr.default, + othr.selected, + othr.auto_generated, + othr.parameters, + othr.meta_data, + othr.schedule, ) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_comma_optional_parens1.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_comma_optional_parens1.py.snap index 35f9fc85aa1d28..7fb41572f30e81 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_comma_optional_parens1.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_comma_optional_parens1.py.snap @@ -37,17 +37,7 @@ class A: ```diff --- Black +++ Ruff -@@ -1,18 +1,16 @@ --if e1234123412341234.winerror not in ( -- _winapi.ERROR_SEM_TIMEOUT, -- _winapi.ERROR_PIPE_BUSY, --) or _check_timeout(t): -+if ( -+ e1234123412341234.winerror -+ not in (_winapi.ERROR_SEM_TIMEOUT, _winapi.ERROR_PIPE_BUSY) -+ or _check_timeout(t) -+): - pass +@@ -6,13 +6,10 @@ if x: if y: @@ -65,36 +55,15 @@ class A: class X: -@@ -26,9 +24,14 @@ - - class A: - def b(self): -- if self.connection.mysql_is_mariadb and ( -- 10, -- 4, -- 3, -- ) < self.connection.mysql_version < (10, 5, 2): -+ if ( -+ self.connection.mysql_is_mariadb -+ and ( -+ 10, -+ 4, -+ 3, -+ ) -+ < self.connection.mysql_version -+ < (10, 5, 2) -+ ): - pass ``` ## Ruff Output ```py -if ( - e1234123412341234.winerror - not in (_winapi.ERROR_SEM_TIMEOUT, _winapi.ERROR_PIPE_BUSY) - or _check_timeout(t) -): +if e1234123412341234.winerror not in ( + _winapi.ERROR_SEM_TIMEOUT, + _winapi.ERROR_PIPE_BUSY, +) or _check_timeout(t): pass if x: @@ -116,16 +85,11 @@ class X: class A: def b(self): - if ( - self.connection.mysql_is_mariadb - and ( - 10, - 4, - 3, - ) - < self.connection.mysql_version - < (10, 5, 2) - ): + if self.connection.mysql_is_mariadb and ( + 10, + 4, + 3, + ) < self.connection.mysql_version < (10, 5, 2): pass ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap index 40a69332f5efeb..84047fbde18c6d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap @@ -476,9 +476,9 @@ if ( # Unstable formatting in https://github.com/realtyem/synapse-unraid/blob/unraid_develop/synapse/handlers/presence.py -for user_id in set( - target_user_ids -) - {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set}: +for user_id in ( + set(target_user_ids) - {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} +): updates.append(UserPresenceState.default(user_id)) # Keeps parenthesized left hand sides diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__compare.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__compare.py.snap index 03d75d057bf060..3e67442fe6edae 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__compare.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__compare.py.snap @@ -100,38 +100,29 @@ a < b > c == d == ddddddddddddddddddddd ) -( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - < [ - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, - ff, - ] - < [ccccccccccccccccccccccccccccc, dddd] - < ddddddddddddddddddddddddddddddddddddddddddd -) +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa < [ + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ff, +] < [ccccccccccccccccccccccccccccc, dddd] < ddddddddddddddddddddddddddddddddddddddddddd -return ( - 1 == 2 - and ( - name, - description, - self_default, - self_selected, - self_auto_generated, - self_parameters, - self_meta_data, - self_schedule, - ) - == ( - name, - description, - othr_default, - othr_selected, - othr_auto_generated, - othr_parameters, - othr_meta_data, - othr_schedule, - ) +return 1 == 2 and ( + name, + description, + self_default, + self_selected, + self_auto_generated, + self_parameters, + self_meta_data, + self_schedule, +) == ( + name, + description, + othr_default, + othr_selected, + othr_auto_generated, + othr_parameters, + othr_meta_data, + othr_schedule, ) ( diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__unary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__unary.py.snap index c6f1534b318fb2..e0325c897bd0b3 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__unary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__unary.py.snap @@ -256,13 +256,10 @@ if aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & ( ): pass -if ( - not ( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - ) - & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -): +if not ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: pass