Skip to content

Commit 0550d40

Browse files
committed
Prefer splitting right hand side of assignments
1 parent 1de4fcd commit 0550d40

26 files changed

+462
-233
lines changed

crates/ruff_python_formatter/src/builders.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ruff_formatter::{write, Argument, Arguments};
1+
use ruff_formatter::{write, Argument, Arguments, GroupId};
22
use ruff_text_size::{Ranged, TextRange, TextSize};
33

44
use crate::context::{NodeLevel, WithNodeLevel};
@@ -12,12 +12,14 @@ where
1212
{
1313
ParenthesizeIfExpands {
1414
inner: Argument::new(content),
15+
group_id: None,
1516
indent: true,
1617
}
1718
}
1819

1920
pub(crate) struct ParenthesizeIfExpands<'a, 'ast> {
2021
inner: Argument<'a, PyFormatContext<'ast>>,
22+
group_id: Option<GroupId>,
2123
indent: bool,
2224
}
2325

@@ -26,6 +28,11 @@ impl ParenthesizeIfExpands<'_, '_> {
2628
self.indent = indent;
2729
self
2830
}
31+
32+
pub(crate) fn with_group_id(mut self, id: Option<GroupId>) -> Self {
33+
self.group_id = id;
34+
self
35+
}
2936
}
3037

3138
impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
@@ -45,7 +52,8 @@ impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
4552
};
4653

4754
if_group_breaks(&token(")")).fmt(f)
48-
}))]
55+
}))
56+
.with_group_id(self.group_id)]
4957
)
5058
}
5159
}

crates/ruff_python_formatter/src/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ impl<'a> PyFormatContext<'a> {
7474
..self
7575
}
7676
}
77+
78+
pub(crate) const fn is_preview(&self) -> bool {
79+
self.options.is_preview()
80+
}
7781
}
7882

7983
impl FormatContext for PyFormatContext<'_> {

crates/ruff_python_formatter/src/expression/expr_attribute.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::expression::parentheses::{
1010
};
1111
use crate::expression::CallChainLayout;
1212
use crate::prelude::*;
13+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
14+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
1315

1416
#[derive(Default)]
1517
pub struct FormatExprAttribute {
@@ -137,7 +139,7 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
137139
impl NeedsParentheses for ExprAttribute {
138140
fn needs_parentheses(
139141
&self,
140-
_parent: AnyNodeRef,
142+
parent: AnyNodeRef,
141143
context: &PyFormatContext,
142144
) -> OptionalParentheses {
143145
// Checks if there are any own line comments in an attribute chain (a.b.c).
@@ -156,6 +158,13 @@ impl NeedsParentheses for ExprAttribute {
156158
context.source(),
157159
) {
158160
OptionalParentheses::Never
161+
} else if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
162+
&& is_assignment_with_splittable_targets(parent, context)
163+
{
164+
match self.value.needs_parentheses(self.into(), context) {
165+
OptionalParentheses::BestFit => OptionalParentheses::Multiline,
166+
parentheses => parentheses,
167+
}
159168
} else {
160169
self.value.needs_parentheses(self.into(), context)
161170
}

crates/ruff_python_formatter/src/expression/expr_boolean_literal.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use ruff_python_ast::ExprBooleanLiteral;
33

44
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
55
use crate::prelude::*;
6+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
7+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
68

79
#[derive(Default)]
810
pub struct FormatExprBooleanLiteral;
@@ -20,9 +22,15 @@ impl FormatNodeRule<ExprBooleanLiteral> for FormatExprBooleanLiteral {
2022
impl NeedsParentheses for ExprBooleanLiteral {
2123
fn needs_parentheses(
2224
&self,
23-
_parent: AnyNodeRef,
24-
_context: &PyFormatContext,
25+
parent: AnyNodeRef,
26+
context: &PyFormatContext,
2527
) -> OptionalParentheses {
26-
OptionalParentheses::BestFit
28+
if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
29+
&& is_assignment_with_splittable_targets(parent, context)
30+
{
31+
OptionalParentheses::Multiline
32+
} else {
33+
OptionalParentheses::BestFit
34+
}
2735
}
2836
}

crates/ruff_python_formatter/src/expression/expr_bytes_literal.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::expression::expr_string_literal::is_multiline_string;
66
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
77
use crate::expression::string::{AnyString, FormatString};
88
use crate::prelude::*;
9+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
10+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
911

1012
#[derive(Default)]
1113
pub struct FormatExprBytesLiteral;
@@ -28,13 +30,17 @@ impl FormatNodeRule<ExprBytesLiteral> for FormatExprBytesLiteral {
2830
impl NeedsParentheses for ExprBytesLiteral {
2931
fn needs_parentheses(
3032
&self,
31-
_parent: AnyNodeRef,
33+
parent: AnyNodeRef,
3234
context: &PyFormatContext,
3335
) -> OptionalParentheses {
3436
if self.value.is_implicit_concatenated() {
3537
OptionalParentheses::Multiline
3638
} else if is_multiline_string(self.into(), context.source()) {
3739
OptionalParentheses::Never
40+
} else if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
41+
&& is_assignment_with_splittable_targets(parent, context)
42+
{
43+
OptionalParentheses::Multiline
3844
} else {
3945
OptionalParentheses::BestFit
4046
}

crates/ruff_python_formatter/src/expression/expr_call.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use crate::expression::parentheses::{
88
};
99
use crate::expression::CallChainLayout;
1010
use crate::prelude::*;
11+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
12+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
1113

1214
#[derive(Default)]
1315
pub struct FormatExprCall {
@@ -87,7 +89,7 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
8789
impl NeedsParentheses for ExprCall {
8890
fn needs_parentheses(
8991
&self,
90-
_parent: AnyNodeRef,
92+
parent: AnyNodeRef,
9193
context: &PyFormatContext,
9294
) -> OptionalParentheses {
9395
if CallChainLayout::from_expression(
@@ -105,6 +107,13 @@ impl NeedsParentheses for ExprCall {
105107
context.source(),
106108
) {
107109
OptionalParentheses::Never
110+
} else if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
111+
&& is_assignment_with_splittable_targets(parent, context)
112+
{
113+
match self.func.needs_parentheses(self.into(), context) {
114+
OptionalParentheses::BestFit => OptionalParentheses::Multiline,
115+
parentheses => parentheses,
116+
}
108117
} else {
109118
self.func.needs_parentheses(self.into(), context)
110119
}

crates/ruff_python_formatter/src/expression/expr_f_string.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use ruff_python_ast::ExprFString;
77

88
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
99
use crate::prelude::*;
10+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
11+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
1012

1113
use super::string::{AnyString, FormatString};
1214

@@ -31,13 +33,19 @@ impl FormatNodeRule<ExprFString> for FormatExprFString {
3133
impl NeedsParentheses for ExprFString {
3234
fn needs_parentheses(
3335
&self,
34-
_parent: AnyNodeRef,
36+
parent: AnyNodeRef,
3537
context: &PyFormatContext,
3638
) -> OptionalParentheses {
3739
if self.value.is_implicit_concatenated() {
3840
OptionalParentheses::Multiline
3941
} else if memchr2(b'\n', b'\r', context.source()[self.range].as_bytes()).is_none() {
40-
OptionalParentheses::BestFit
42+
if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
43+
&& is_assignment_with_splittable_targets(parent, context)
44+
{
45+
OptionalParentheses::Multiline
46+
} else {
47+
OptionalParentheses::BestFit
48+
}
4149
} else {
4250
OptionalParentheses::Never
4351
}

crates/ruff_python_formatter/src/expression/expr_name.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use ruff_python_ast::ExprName;
55
use crate::comments::SourceComment;
66
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
77
use crate::prelude::*;
8+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
9+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
810

911
#[derive(Default)]
1012
pub struct FormatExprName;
@@ -38,10 +40,16 @@ impl FormatNodeRule<ExprName> for FormatExprName {
3840
impl NeedsParentheses for ExprName {
3941
fn needs_parentheses(
4042
&self,
41-
_parent: AnyNodeRef,
42-
_context: &PyFormatContext,
43+
parent: AnyNodeRef,
44+
context: &PyFormatContext,
4345
) -> OptionalParentheses {
44-
OptionalParentheses::BestFit
46+
if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
47+
&& is_assignment_with_splittable_targets(parent, context)
48+
{
49+
OptionalParentheses::Multiline
50+
} else {
51+
OptionalParentheses::BestFit
52+
}
4553
}
4654
}
4755

crates/ruff_python_formatter/src/expression/expr_none_literal.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use ruff_python_ast::ExprNoneLiteral;
33

44
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
55
use crate::prelude::*;
6+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
7+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
68

79
#[derive(Default)]
810
pub struct FormatExprNoneLiteral;
@@ -16,9 +18,15 @@ impl FormatNodeRule<ExprNoneLiteral> for FormatExprNoneLiteral {
1618
impl NeedsParentheses for ExprNoneLiteral {
1719
fn needs_parentheses(
1820
&self,
19-
_parent: AnyNodeRef,
20-
_context: &PyFormatContext,
21+
parent: AnyNodeRef,
22+
context: &PyFormatContext,
2123
) -> OptionalParentheses {
22-
OptionalParentheses::BestFit
24+
if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
25+
&& is_assignment_with_splittable_targets(parent, context)
26+
{
27+
OptionalParentheses::Multiline
28+
} else {
29+
OptionalParentheses::BestFit
30+
}
2331
}
2432
}

crates/ruff_python_formatter/src/expression/expr_number_literal.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use ruff_text_size::{Ranged, TextSize};
66

77
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
88
use crate::prelude::*;
9+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
10+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
911

1012
#[derive(Default)]
1113
pub struct FormatExprNumberLiteral;
@@ -56,10 +58,16 @@ impl FormatNodeRule<ExprNumberLiteral> for FormatExprNumberLiteral {
5658
impl NeedsParentheses for ExprNumberLiteral {
5759
fn needs_parentheses(
5860
&self,
59-
_parent: AnyNodeRef,
60-
_context: &PyFormatContext,
61+
parent: AnyNodeRef,
62+
context: &PyFormatContext,
6163
) -> OptionalParentheses {
62-
OptionalParentheses::BestFit
64+
if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
65+
&& is_assignment_with_splittable_targets(parent, context)
66+
{
67+
OptionalParentheses::Multiline
68+
} else {
69+
OptionalParentheses::BestFit
70+
}
6371
}
6472
}
6573

crates/ruff_python_formatter/src/expression/expr_string_literal.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crate::expression::string::{
99
AnyString, FormatString, StringLayout, StringPrefix, StringQuotes,
1010
};
1111
use crate::prelude::*;
12+
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
13+
use crate::statement::stmt_assign::is_assignment_with_splittable_targets;
1214

1315
#[derive(Default)]
1416
pub struct FormatExprStringLiteral {
@@ -43,13 +45,17 @@ impl FormatNodeRule<ExprStringLiteral> for FormatExprStringLiteral {
4345
impl NeedsParentheses for ExprStringLiteral {
4446
fn needs_parentheses(
4547
&self,
46-
_parent: AnyNodeRef,
48+
parent: AnyNodeRef,
4749
context: &PyFormatContext,
4850
) -> OptionalParentheses {
4951
if self.value.is_implicit_concatenated() {
5052
OptionalParentheses::Multiline
5153
} else if is_multiline_string(self.into(), context.source()) {
5254
OptionalParentheses::Never
55+
} else if is_prefer_splitting_right_hand_side_of_assignments_enabled(context)
56+
&& is_assignment_with_splittable_targets(parent, context)
57+
{
58+
OptionalParentheses::Multiline
5359
} else {
5460
OptionalParentheses::BestFit
5561
}

crates/ruff_python_formatter/src/expression/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::expression::parentheses::{
2121
OptionalParentheses, Parentheses, Parenthesize,
2222
};
2323
use crate::prelude::*;
24-
use crate::PyFormatOptions;
24+
use crate::preview::is_hug_parens_with_braces_and_square_brackets_enabled;
2525

2626
mod binary_like;
2727
pub(crate) mod expr_attribute;
@@ -129,7 +129,7 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
129129
let node_comments = comments.leading_dangling_trailing(expression);
130130
if !node_comments.has_leading() && !node_comments.has_trailing() {
131131
parenthesized("(", &format_expr, ")")
132-
.with_indent(!is_expression_huggable(expression, f.options()))
132+
.with_indent(!is_expression_huggable(expression, f.context()))
133133
.fmt(f)
134134
} else {
135135
format_with_parentheses_comments(expression, &node_comments, f)
@@ -448,7 +448,7 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
448448
OptionalParentheses::Never => match parenthesize {
449449
Parenthesize::IfBreaksOrIfRequired => {
450450
parenthesize_if_expands(&expression.format().with_options(Parentheses::Never))
451-
.with_indent(!is_expression_huggable(expression, f.options()))
451+
.with_indent(!is_expression_huggable(expression, f.context()))
452452
.fmt(f)
453453
}
454454

@@ -1061,8 +1061,8 @@ pub(crate) fn has_own_parentheses(
10611061
/// ]
10621062
/// )
10631063
/// ```
1064-
pub(crate) fn is_expression_huggable(expr: &Expr, options: &PyFormatOptions) -> bool {
1065-
if !options.preview().is_enabled() {
1064+
pub(crate) fn is_expression_huggable(expr: &Expr, context: &PyFormatContext) -> bool {
1065+
if !is_hug_parens_with_braces_and_square_brackets_enabled(context) {
10661066
return false;
10671067
}
10681068

crates/ruff_python_formatter/src/lib.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod options;
3232
pub(crate) mod other;
3333
pub(crate) mod pattern;
3434
mod prelude;
35+
mod preview;
3536
mod shared_traits;
3637
pub(crate) mod statement;
3738
pub(crate) mod type_param;
@@ -180,7 +181,7 @@ mod tests {
180181

181182
use ruff_python_parser::{parse_ok_tokens, AsMode};
182183

183-
use crate::{format_module_ast, format_module_source, PyFormatOptions};
184+
use crate::{format_module_ast, format_module_source, PreviewMode, PyFormatOptions};
184185

185186
/// Very basic test intentionally kept very similar to the CLI
186187
#[test]
@@ -208,13 +209,7 @@ if True:
208209
#[test]
209210
fn quick_test() {
210211
let source = r#"
211-
def main() -> None:
212-
if True:
213-
some_very_long_variable_name_abcdefghijk = Foo()
214-
some_very_long_variable_name_abcdefghijk = some_very_long_variable_name_abcdefghijk[
215-
some_very_long_variable_name_abcdefghijk.some_very_long_attribute_name
216-
== "This is a very long string abcdefghijk"
217-
]
212+
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0
218213
219214
"#;
220215
let source_type = PySourceType::Python;
@@ -223,7 +218,8 @@ def main() -> None:
223218
// Parse the AST.
224219
let source_path = "code_inline.py";
225220
let module = parse_ok_tokens(tokens, source, source_type.as_mode(), source_path).unwrap();
226-
let options = PyFormatOptions::from_extension(Path::new(source_path));
221+
let options = PyFormatOptions::from_extension(Path::new(source_path))
222+
.with_preview(PreviewMode::Enabled);
227223
let formatted = format_module_ast(&module, &comment_ranges, source, options).unwrap();
228224

229225
// Uncomment the `dbg` to print the IR.

0 commit comments

Comments
 (0)