diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/yield.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/yield.py index ff587bd8120cb7..0959855cf53bd7 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/yield.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/yield.py @@ -66,6 +66,13 @@ def foo(): 1 ) + yield ( + "# * Make sure each ForeignKey and OneToOneField has `on_delete` set " + "to the desired behavior" + ) + + yield aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccccccccccccccccccccccccccccccccc + yield ("Cache key will cause errors if used with memcached: %r " "(longer than %s)" % ( key, MEMCACHE_MAX_KEY_LENGTH, diff --git a/crates/ruff_python_formatter/src/expression/expr_call.rs b/crates/ruff_python_formatter/src/expression/expr_call.rs index c517f2ce8585cd..a8b0d70cd789b2 100644 --- a/crates/ruff_python_formatter/src/expression/expr_call.rs +++ b/crates/ruff_python_formatter/src/expression/expr_call.rs @@ -1,5 +1,6 @@ use crate::expression::CallChainLayout; -use ruff_formatter::FormatRuleWithOptions; + +use ruff_formatter::{format_args, write, FormatRuleWithOptions}; use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::{Expr, ExprCall}; @@ -31,15 +32,11 @@ impl FormatNodeRule for FormatExprCall { let call_chain_layout = self.call_chain_layout.apply_in_node(item, f); - let fmt_inner = format_with(|f| { - match func.as_ref() { - Expr::Attribute(expr) => expr.format().with_options(call_chain_layout).fmt(f)?, - Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f)?, - Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f)?, - _ => func.format().fmt(f)?, - } - - arguments.format().fmt(f) + let fmt_func = format_with(|f| match func.as_ref() { + Expr::Attribute(expr) => expr.format().with_options(call_chain_layout).fmt(f), + Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f), + Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f), + _ => func.format().fmt(f), }); // Allow to indent the parentheses while @@ -51,9 +48,9 @@ impl FormatNodeRule for FormatExprCall { if call_chain_layout == CallChainLayout::Fluent && self.call_chain_layout == CallChainLayout::Default { - group(&fmt_inner).fmt(f) + group(&format_args![fmt_func, arguments.format()]).fmt(f) } else { - fmt_inner.fmt(f) + write!(f, [fmt_func, arguments.format()]) } } } @@ -70,7 +67,7 @@ impl NeedsParentheses for ExprCall { OptionalParentheses::Multiline } else { match self.func.needs_parentheses(self.into(), context) { - OptionalParentheses::IfFits => OptionalParentheses::Never, + OptionalParentheses::IfFits => OptionalParentheses::IfFits, parentheses => parentheses, } } diff --git a/crates/ruff_python_formatter/src/expression/parentheses.rs b/crates/ruff_python_formatter/src/expression/parentheses.rs index 32cf48ff10583a..d1d23c7903c0a8 100644 --- a/crates/ruff_python_formatter/src/expression/parentheses.rs +++ b/crates/ruff_python_formatter/src/expression/parentheses.rs @@ -53,7 +53,6 @@ pub(crate) enum Parenthesize { /// Parenthesizes the expression if the group doesn't fit on a line (e.g., even name expressions are parenthesized), or if /// the expression doesn't break, but _does_ reports that it always requires parentheses in this position (e.g., walrus /// operators in function return annotations). - // TODO can this be replaced by IfBreaks IfBreaksOrIfRequired, } diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 4303eac0d8f067..a11e3beaf2aaa8 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -249,10 +249,11 @@ if True: #[test] fn quick_test() { let src = r#" -for converter in connection.ops.get_db_converters( - expression -) + expression.get_db_converters(connection): - ... + +a1 = Blog.objects.filter(entry__headline__contains="Lennon").filter( + entry__pub_date__year=2008 + ) + "#; // Tokenize once let mut tokens = Vec::new(); diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap index 3abf27d802c1b1..514ea6258ad1e1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap @@ -56,6 +56,18 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( # Example from https://github.com/psf/black/issues/3229 +@@ -43,8 +41,6 @@ + ) + + # Regression test for https://github.com/psf/black/issues/3414. +-assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( +- xxxxxxxxx +-).xxxxxxxxxxxxxxxxxx(), ( +- "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +-) ++assert ( ++ xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx() ++), "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` ## Ruff Output @@ -104,11 +116,9 @@ assert ( ) # Regression test for https://github.com/psf/black/issues/3414. -assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( - xxxxxxxxx -).xxxxxxxxxxxxxxxxxx(), ( - "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -) +assert ( + xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx() +), "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap index f8090247fce2f8..7c78a361807a9a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap @@ -72,6 +72,13 @@ def foo(): 1 ) + yield ( + "# * Make sure each ForeignKey and OneToOneField has `on_delete` set " + "to the desired behavior" + ) + + yield aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccccccccccccccccccccccccccccccccc + yield ("Cache key will cause errors if used with memcached: %r " "(longer than %s)" % ( key, MEMCACHE_MAX_KEY_LENGTH, @@ -168,6 +175,17 @@ def foo(): ) ) + yield ( + "# * Make sure each ForeignKey and OneToOneField has `on_delete` set " + "to the desired behavior" + ) + + yield ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + + ccccccccccccccccccccccccccccccccccccccccccccccccccccccc + ) + yield ( "Cache key will cause errors if used with memcached: %r "