mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 02:12:22 +00:00
prefer_splitting_right_hand_side_of_assignments
preview style (#8943)
This commit is contained in:
parent
1a65e544c5
commit
45f603000d
18 changed files with 1473 additions and 332 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"preview": "enabled"
|
||||||
|
}
|
||||||
|
]
|
218
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.py
vendored
Normal file
218
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.py
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#######
|
||||||
|
# Unsplittable target and value
|
||||||
|
|
||||||
|
# Only parenthesize the value if it makes it fit, otherwise avoid parentheses.
|
||||||
|
b = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
bbbbbbbbbbbbbbbb = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvv
|
||||||
|
|
||||||
|
# Avoid parenthesizing the value even if the target exceeds the configured width
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = bbb
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Splittable targets
|
||||||
|
|
||||||
|
# Does not double-parenthesize tuples
|
||||||
|
(
|
||||||
|
first_item,
|
||||||
|
second_item,
|
||||||
|
) = some_looooooooong_module.some_loooooog_function_name(
|
||||||
|
first_argument, second_argument, third_argument
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Preserve parentheses around the first target
|
||||||
|
(
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
]["destinations"]["destination"][0]["ip_address"]
|
||||||
|
) = dst
|
||||||
|
|
||||||
|
# Augmented assignment
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
] += dst
|
||||||
|
|
||||||
|
# Always parenthesize the value if it avoids splitting the target, regardless of the value's width.
|
||||||
|
_a: a[aaaa] = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
)
|
||||||
|
|
||||||
|
#####
|
||||||
|
# Avoid parenthesizing the value if the expression right before the `=` splits to avoid an unnecessary pair of parentheses
|
||||||
|
|
||||||
|
# The type annotation is guaranteed to split because it is too long.
|
||||||
|
_a: a[
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target is too long
|
||||||
|
(
|
||||||
|
aaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target splits because of a magic trailing comma
|
||||||
|
(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The targets split because of a comment
|
||||||
|
(
|
||||||
|
# leading
|
||||||
|
a
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a
|
||||||
|
# trailing
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a, # nested
|
||||||
|
b
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Multi targets
|
||||||
|
|
||||||
|
# Black always parenthesizes the right if using multiple targets regardless if the parenthesized value exceeds the
|
||||||
|
# the configured line width or not
|
||||||
|
aaaa = bbbbbbbbbbbbbbbb = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
)
|
||||||
|
|
||||||
|
# Black does parenthesize the target if the target itself exceeds the line width and only parenthesizes
|
||||||
|
# the values if it makes it fit.
|
||||||
|
# The second target is too long to ever fit into the configured line width.
|
||||||
|
aaaa = (
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdddd
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
# Does also apply for other multi target assignments, as soon as a single target exceeds the configured
|
||||||
|
# width
|
||||||
|
aaaaaa = a["aaa"] = bbbbb[aa, bbb, cccc] = dddddddddd = eeeeee = (
|
||||||
|
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Call expressions:
|
||||||
|
# For unsplittable targets: Parenthesize the call expression if it makes it fit.
|
||||||
|
#
|
||||||
|
# For splittable targets:
|
||||||
|
# Only parenthesize a call expression if the parens of the call don't fit on the same line
|
||||||
|
# as the target. Don't parenthesize the call expression if the target (or annotation) right before
|
||||||
|
# splits.
|
||||||
|
|
||||||
|
# Don't parenthesize the function call if the left is unsplittable.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = a.b.function(
|
||||||
|
arg1, arg2, arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = (
|
||||||
|
function()
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = (
|
||||||
|
a.b.function(arg1, arg2, arg3)
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function()
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
####### Fluent call expressions
|
||||||
|
# Uses the regular `Multiline` layout where the entire `value` gets parenthesized
|
||||||
|
# if it doesn't fit on the line.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use = (
|
||||||
|
function().b().c([1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Test comment inlining
|
||||||
|
value.__dict__[key] = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Don't parenthesize the value because the target's trailing comma forces it to split.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
|
||||||
|
# Parenthesize the value, but don't duplicate the comment.
|
||||||
|
a[aaaaaaa, b] = (
|
||||||
|
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format both as flat, but don't loos the comment.
|
||||||
|
a[aaaaaaa, b] = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # comment
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# Test the case where a parenthesized value now fits:
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b
|
||||||
|
] = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Splits the target but not the value because of the magic trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Splits the second target because of the comment and the first target because of the trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = (
|
||||||
|
# leading comment
|
||||||
|
b
|
||||||
|
) = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
########
|
||||||
|
# Type Alias Statement
|
||||||
|
type A[str, int, number] = VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtin
|
||||||
|
|
||||||
|
type A[VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtinthatExceedsTheWidth] = str
|
||||||
|
|
|
@ -952,7 +952,7 @@ impl OwnParentheses {
|
||||||
/// Differs from [`has_own_parentheses`] in that it returns [`OwnParentheses::NonEmpty`] for
|
/// Differs from [`has_own_parentheses`] in that it returns [`OwnParentheses::NonEmpty`] for
|
||||||
/// parenthesized expressions, like `(1)` or `([1])`, regardless of whether those expression have
|
/// parenthesized expressions, like `(1)` or `([1])`, regardless of whether those expression have
|
||||||
/// their _own_ parentheses.
|
/// their _own_ parentheses.
|
||||||
fn has_parentheses(expr: &Expr, context: &PyFormatContext) -> Option<OwnParentheses> {
|
pub(crate) fn has_parentheses(expr: &Expr, context: &PyFormatContext) -> Option<OwnParentheses> {
|
||||||
let own_parentheses = has_own_parentheses(expr, context);
|
let own_parentheses = has_own_parentheses(expr, context);
|
||||||
|
|
||||||
// If the node has its own non-empty parentheses, we don't need to check for surrounding
|
// If the node has its own non-empty parentheses, we don't need to check for surrounding
|
||||||
|
|
|
@ -17,3 +17,10 @@ pub(crate) const fn is_hug_parens_with_braces_and_square_brackets_enabled(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
context.is_preview()
|
context.is_preview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the [`prefer_splitting_right_hand_side_of_assignments`](https://github.com/astral-sh/ruff/issues/6975) preview style is enabled.
|
||||||
|
pub(crate) const fn is_prefer_splitting_right_hand_side_of_assignments_enabled(
|
||||||
|
context: &PyFormatContext,
|
||||||
|
) -> bool {
|
||||||
|
context.is_preview()
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,12 @@ use ruff_formatter::write;
|
||||||
use ruff_python_ast::StmtAnnAssign;
|
use ruff_python_ast::StmtAnnAssign;
|
||||||
|
|
||||||
use crate::comments::{SourceComment, SuppressionKind};
|
use crate::comments::{SourceComment, SuppressionKind};
|
||||||
|
use crate::expression::has_parentheses;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::stmt_assign::FormatStatementsLastExpression;
|
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
|
||||||
|
use crate::statement::stmt_assign::{
|
||||||
|
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
|
||||||
|
};
|
||||||
use crate::statement::trailing_semicolon;
|
use crate::statement::trailing_semicolon;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -19,21 +23,33 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
|
||||||
simple: _,
|
simple: _,
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
write!(
|
write!(f, [target.format(), token(":"), space()])?;
|
||||||
f,
|
|
||||||
[target.format(), token(":"), space(), annotation.format(),]
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
write!(
|
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context())
|
||||||
f,
|
&& has_parentheses(annotation, f.context()).is_some()
|
||||||
[
|
{
|
||||||
space(),
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
token("="),
|
before_operator: AnyBeforeOperator::Expression(annotation),
|
||||||
space(),
|
operator: AnyAssignmentOperator::Assign,
|
||||||
FormatStatementsLastExpression::new(value, item)
|
value,
|
||||||
]
|
statement: item.into(),
|
||||||
)?;
|
}
|
||||||
|
.fmt(f)?;
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
annotation.format(),
|
||||||
|
space(),
|
||||||
|
token("="),
|
||||||
|
space(),
|
||||||
|
FormatStatementsLastExpression::left_to_right(value, item)
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
annotation.format().fmt(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.options().source_type().is_ipynb()
|
if f.options().source_type().is_ipynb()
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
use ruff_formatter::{format_args, write, FormatError};
|
use ruff_formatter::{format_args, write, FormatError};
|
||||||
use ruff_python_ast::{AnyNodeRef, Expr, StmtAssign};
|
use ruff_python_ast::{AnyNodeRef, Expr, Operator, StmtAssign, TypeParams};
|
||||||
|
|
||||||
use crate::comments::{trailing_comments, SourceComment, SuppressionKind};
|
use crate::builders::parenthesize_if_expands;
|
||||||
|
use crate::comments::{
|
||||||
|
trailing_comments, Comments, LeadingDanglingTrailingComments, SourceComment, SuppressionKind,
|
||||||
|
};
|
||||||
use crate::context::{NodeLevel, WithNodeLevel};
|
use crate::context::{NodeLevel, WithNodeLevel};
|
||||||
use crate::expression::parentheses::{
|
use crate::expression::parentheses::{
|
||||||
NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize,
|
is_expression_parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize,
|
||||||
};
|
};
|
||||||
use crate::expression::{has_own_parentheses, maybe_parenthesize_expression};
|
use crate::expression::{has_own_parentheses, maybe_parenthesize_expression};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
|
||||||
use crate::statement::trailing_semicolon;
|
use crate::statement::trailing_semicolon;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -25,24 +29,66 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
|
||||||
"Expected at least on assignment target",
|
"Expected at least on assignment target",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
write!(
|
// The first target is special because it never gets parenthesized nor does the formatter remove parentheses if unnecessary.
|
||||||
f,
|
let format_first = FormatTargetWithEqualOperator {
|
||||||
[
|
target: first,
|
||||||
first.format(),
|
preserve_parentheses: true,
|
||||||
space(),
|
};
|
||||||
token("="),
|
|
||||||
space(),
|
|
||||||
FormatTargets { targets: rest }
|
|
||||||
]
|
|
||||||
)?;
|
|
||||||
|
|
||||||
FormatStatementsLastExpression::new(value, item).fmt(f)?;
|
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context()) {
|
||||||
|
// Avoid parenthesizing the value if the last target before the assigned value expands.
|
||||||
|
if let Some((last, head)) = rest.split_last() {
|
||||||
|
format_first.fmt(f)?;
|
||||||
|
|
||||||
|
for target in head {
|
||||||
|
FormatTargetWithEqualOperator {
|
||||||
|
target,
|
||||||
|
preserve_parentheses: false,
|
||||||
|
}
|
||||||
|
.fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
|
before_operator: AnyBeforeOperator::Expression(last),
|
||||||
|
operator: AnyAssignmentOperator::Assign,
|
||||||
|
value,
|
||||||
|
statement: item.into(),
|
||||||
|
}
|
||||||
|
.fmt(f)?;
|
||||||
|
}
|
||||||
|
// Avoid parenthesizing the value for single-target assignments that where the
|
||||||
|
// target has its own parentheses (list, dict, tuple, ...) and the target expands.
|
||||||
|
else if has_target_own_parentheses(first, f.context())
|
||||||
|
&& !is_expression_parenthesized(
|
||||||
|
first.into(),
|
||||||
|
f.context().comments().ranges(),
|
||||||
|
f.context().source(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
|
before_operator: AnyBeforeOperator::Expression(first),
|
||||||
|
operator: AnyAssignmentOperator::Assign,
|
||||||
|
value,
|
||||||
|
statement: item.into(),
|
||||||
|
}
|
||||||
|
.fmt(f)?;
|
||||||
|
}
|
||||||
|
// For single targets that have no split points, parenthesize the value only
|
||||||
|
// if it makes it fit. Otherwise omit the parentheses.
|
||||||
|
else {
|
||||||
|
format_first.fmt(f)?;
|
||||||
|
FormatStatementsLastExpression::left_to_right(value, item).fmt(f)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(f, [format_first, FormatTargets { targets: rest }])?;
|
||||||
|
|
||||||
|
FormatStatementsLastExpression::left_to_right(value, item).fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
if f.options().source_type().is_ipynb()
|
if f.options().source_type().is_ipynb()
|
||||||
&& f.context().node_level().is_last_top_level_statement()
|
&& f.context().node_level().is_last_top_level_statement()
|
||||||
&& rest.is_empty()
|
|
||||||
&& first.is_name_expr()
|
|
||||||
&& trailing_semicolon(item.into(), f.context().source()).is_some()
|
&& trailing_semicolon(item.into(), f.context().source()).is_some()
|
||||||
|
&& matches!(targets.as_slice(), [Expr::Name(_)])
|
||||||
{
|
{
|
||||||
token(";").fmt(f)?;
|
token(";").fmt(f)?;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +105,7 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats the targets so that they split left-to right.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FormatTargets<'a> {
|
struct FormatTargets<'a> {
|
||||||
targets: &'a [Expr],
|
targets: &'a [Expr],
|
||||||
|
@ -71,7 +118,7 @@ impl Format<PyFormatContext<'_>> for FormatTargets<'_> {
|
||||||
|
|
||||||
let parenthesize = if comments.has_leading(first) || comments.has_trailing(first) {
|
let parenthesize = if comments.has_leading(first) || comments.has_trailing(first) {
|
||||||
ParenthesizeTarget::Always
|
ParenthesizeTarget::Always
|
||||||
} else if has_own_parentheses(first, f.context()).is_some() {
|
} else if has_target_own_parentheses(first, f.context()) {
|
||||||
ParenthesizeTarget::Never
|
ParenthesizeTarget::Never
|
||||||
} else {
|
} else {
|
||||||
ParenthesizeTarget::IfBreaks
|
ParenthesizeTarget::IfBreaks
|
||||||
|
@ -129,10 +176,50 @@ enum ParenthesizeTarget {
|
||||||
IfBreaks,
|
IfBreaks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats a single target with the equal operator.
|
||||||
|
struct FormatTargetWithEqualOperator<'a> {
|
||||||
|
target: &'a Expr,
|
||||||
|
|
||||||
|
/// Whether parentheses should be preserved as in the source or if the target
|
||||||
|
/// should only be parenthesized if necessary (because of comments or because it doesn't fit).
|
||||||
|
preserve_parentheses: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format<PyFormatContext<'_>> for FormatTargetWithEqualOperator<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
|
// Preserve parentheses for the first target or around targets with leading or trailing comments.
|
||||||
|
if self.preserve_parentheses
|
||||||
|
|| f.context().comments().has_leading(self.target)
|
||||||
|
|| f.context().comments().has_trailing(self.target)
|
||||||
|
{
|
||||||
|
self.target.format().fmt(f)?;
|
||||||
|
} else if has_target_own_parentheses(self.target, f.context()) {
|
||||||
|
self.target
|
||||||
|
.format()
|
||||||
|
.with_options(Parentheses::Never)
|
||||||
|
.fmt(f)?;
|
||||||
|
} else {
|
||||||
|
parenthesize_if_expands(&self.target.format().with_options(Parentheses::Never))
|
||||||
|
.fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, [space(), token("="), space()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Formats the last expression in statements that start with a keyword (like `return`) or after an operator (assignments).
|
/// Formats the last expression in statements that start with a keyword (like `return`) or after an operator (assignments).
|
||||||
///
|
///
|
||||||
/// It avoids parenthesizing unsplittable values (like `None`, `True`, `False`, Names, a subset of strings) just to make
|
/// The implementation avoids parenthesizing unsplittable values (like `None`, `True`, `False`, Names, a subset of strings)
|
||||||
/// the trailing comment fit and inlines a trailing comment if the value itself exceeds the configured line width:
|
/// if the value won't fit even when parenthesized.
|
||||||
|
///
|
||||||
|
/// ## Trailing comments
|
||||||
|
/// Trailing comments are inlined inside the `value`'s parentheses rather than formatted at the end
|
||||||
|
/// of the statement for unsplittable values if the `value` gets parenthesized.
|
||||||
|
///
|
||||||
|
/// Inlining the trailing comments prevent situations where the parenthesized value
|
||||||
|
/// still exceeds the configured line width, but parenthesizing helps to make the trailing comment fit.
|
||||||
|
/// Instead, it only parenthesizes `value` if it makes both the `value` and the trailing comment fit.
|
||||||
|
/// See [PR 8431](https://github.com/astral-sh/ruff/pull/8431) for more details.
|
||||||
///
|
///
|
||||||
/// The implementation formats the statement's and value's trailing end of line comments:
|
/// The implementation formats the statement's and value's trailing end of line comments:
|
||||||
/// * after the expression if the expression needs no parentheses (necessary or the `expand_parent` makes the group never fit).
|
/// * after the expression if the expression needs no parentheses (necessary or the `expand_parent` makes the group never fit).
|
||||||
|
@ -155,123 +242,337 @@ enum ParenthesizeTarget {
|
||||||
/// b = short # with comment
|
/// b = short # with comment
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The long name gets parenthesized because it exceeds the configured line width and the trailing comma of the
|
/// The long name gets parenthesized because it exceeds the configured line width and the trailing comment of the
|
||||||
/// statement gets formatted inside (instead of outside) the parentheses.
|
/// statement gets formatted inside (instead of outside) the parentheses.
|
||||||
///
|
///
|
||||||
/// The `short` name gets unparenthesized because it fits into the configured line length, regardless of whether
|
/// No parentheses are added for `short` because it fits into the configured line length, regardless of whether
|
||||||
/// the comment exceeds the line width or not.
|
/// the comment exceeds the line width or not.
|
||||||
///
|
///
|
||||||
/// This logic isn't implemented in [`place_comment`] by associating trailing statement comments to the expression because
|
/// This logic isn't implemented in [`place_comment`] by associating trailing statement comments to the expression because
|
||||||
/// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement.
|
/// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement.
|
||||||
pub(super) struct FormatStatementsLastExpression<'a> {
|
pub(super) enum FormatStatementsLastExpression<'a> {
|
||||||
expression: &'a Expr,
|
/// Prefers to split what's left of `value` before splitting the value.
|
||||||
parent: AnyNodeRef<'a>,
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[bbbbbbbb] = some_long_value
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This layout splits `aaaaaaa[bbbbbbbb]` first assuming the whole statements exceeds the line width, resulting in
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[
|
||||||
|
/// bbbbbbbb
|
||||||
|
/// ] = some_long_value
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This layout is preferred over [`RightToLeft`] if the left is unsplittable (single keyword like `return` or a Name)
|
||||||
|
/// because it has better performance characteristics.
|
||||||
|
LeftToRight {
|
||||||
|
/// The right side of an assignment or the value returned in a return statement.
|
||||||
|
value: &'a Expr,
|
||||||
|
|
||||||
|
/// The parent statement that encloses the `value` expression.
|
||||||
|
statement: AnyNodeRef<'a>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Prefers parenthesizing the value before splitting the left side. Specific to assignments.
|
||||||
|
///
|
||||||
|
/// Formats what's left of `value` together with the assignment operator and the assigned `value`.
|
||||||
|
/// This layout prefers parenthesizing the value over parenthesizing the left (target or type annotation):
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[bbbbbbbb] = some_long_value
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// gets formatted to...
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[bbbbbbbb] = (
|
||||||
|
/// some_long_value
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ... regardless whether the value will fit or not.
|
||||||
|
///
|
||||||
|
/// The left only gets parenthesized if the left exceeds the configured line width on its own or
|
||||||
|
/// is forced to split because of a magical trailing comma or contains comments:
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[bbbbbbbb_exceeds_the_line_width] = some_long_value
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// gets formatted to
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[
|
||||||
|
/// bbbbbbbb_exceeds_the_line_width
|
||||||
|
/// ] = some_long_value
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The layout avoids parenthesizing the value when the left splits to avoid
|
||||||
|
/// unnecessary parentheses. Adding the parentheses, as shown in the below example, reduces readability.
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// aaaaaaa[
|
||||||
|
/// bbbbbbbb_exceeds_the_line_width
|
||||||
|
/// ] = (
|
||||||
|
/// some_long_value
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// ## Non-fluent Call Expressions
|
||||||
|
/// Non-fluent call expressions in the `value` position are only parenthesized if the opening parentheses
|
||||||
|
/// exceeds the configured line length. The layout prefers splitting after the opening parentheses
|
||||||
|
/// if the `callee` expression and the opening parentheses fit.
|
||||||
|
/// fits on the line.
|
||||||
|
RightToLeft {
|
||||||
|
/// The expression that comes before the assignment operator. This is either
|
||||||
|
/// the last target, or the type annotation of an annotated assignment.
|
||||||
|
before_operator: AnyBeforeOperator<'a>,
|
||||||
|
|
||||||
|
/// The assignment operator. Either `Assign` (`=`) or the operator used by the augmented assignment statement.
|
||||||
|
operator: AnyAssignmentOperator,
|
||||||
|
|
||||||
|
/// The assigned `value`.
|
||||||
|
value: &'a Expr,
|
||||||
|
|
||||||
|
/// The assignment statement.
|
||||||
|
statement: AnyNodeRef<'a>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatStatementsLastExpression<'a> {
|
impl<'a> FormatStatementsLastExpression<'a> {
|
||||||
pub(super) fn new<P: Into<AnyNodeRef<'a>>>(expression: &'a Expr, parent: P) -> Self {
|
pub(super) fn left_to_right<S: Into<AnyNodeRef<'a>>>(value: &'a Expr, statement: S) -> Self {
|
||||||
Self {
|
Self::LeftToRight {
|
||||||
expression,
|
value,
|
||||||
parent: parent.into(),
|
statement: statement.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
let can_inline_comment = match self.expression {
|
match self {
|
||||||
Expr::Name(_)
|
FormatStatementsLastExpression::LeftToRight { value, statement } => {
|
||||||
| Expr::NoneLiteral(_)
|
let can_inline_comment = should_inline_comments(value, *statement, f.context());
|
||||||
| Expr::NumberLiteral(_)
|
|
||||||
| Expr::BooleanLiteral(_) => true,
|
|
||||||
Expr::StringLiteral(string) => {
|
|
||||||
string.needs_parentheses(self.parent, f.context()) == OptionalParentheses::BestFit
|
|
||||||
}
|
|
||||||
Expr::BytesLiteral(bytes) => {
|
|
||||||
bytes.needs_parentheses(self.parent, f.context()) == OptionalParentheses::BestFit
|
|
||||||
}
|
|
||||||
Expr::FString(fstring) => {
|
|
||||||
fstring.needs_parentheses(self.parent, f.context()) == OptionalParentheses::BestFit
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !can_inline_comment {
|
if !can_inline_comment {
|
||||||
return maybe_parenthesize_expression(
|
return maybe_parenthesize_expression(
|
||||||
self.expression,
|
value,
|
||||||
self.parent,
|
*statement,
|
||||||
Parenthesize::IfBreaks,
|
Parenthesize::IfBreaks,
|
||||||
)
|
)
|
||||||
.fmt(f);
|
.fmt(f);
|
||||||
}
|
|
||||||
|
|
||||||
let comments = f.context().comments().clone();
|
|
||||||
let expression_comments = comments.leading_dangling_trailing(self.expression);
|
|
||||||
|
|
||||||
if expression_comments.has_leading() {
|
|
||||||
// Preserve the parentheses if the expression has any leading comments,
|
|
||||||
// same as `maybe_parenthesize_expression`
|
|
||||||
return self
|
|
||||||
.expression
|
|
||||||
.format()
|
|
||||||
.with_options(Parentheses::Always)
|
|
||||||
.fmt(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
let statement_trailing_comments = comments.trailing(self.parent);
|
|
||||||
let after_end_of_line = statement_trailing_comments
|
|
||||||
.partition_point(|comment| comment.line_position().is_end_of_line());
|
|
||||||
let (stmt_inline_comments, _) = statement_trailing_comments.split_at(after_end_of_line);
|
|
||||||
|
|
||||||
let after_end_of_line = expression_comments
|
|
||||||
.trailing
|
|
||||||
.partition_point(|comment| comment.line_position().is_end_of_line());
|
|
||||||
|
|
||||||
let (expression_inline_comments, expression_trailing_comments) =
|
|
||||||
expression_comments.trailing.split_at(after_end_of_line);
|
|
||||||
|
|
||||||
if expression_trailing_comments.is_empty() {
|
|
||||||
let inline_comments = OptionalParenthesesInlinedComments {
|
|
||||||
expression: expression_inline_comments,
|
|
||||||
statement: stmt_inline_comments,
|
|
||||||
};
|
|
||||||
|
|
||||||
let group_id = f.group_id("optional_parentheses");
|
|
||||||
let f = &mut WithNodeLevel::new(NodeLevel::Expression(Some(group_id)), f);
|
|
||||||
|
|
||||||
best_fit_parenthesize(&format_with(|f| {
|
|
||||||
inline_comments.mark_formatted();
|
|
||||||
|
|
||||||
self.expression
|
|
||||||
.format()
|
|
||||||
.with_options(Parentheses::Never)
|
|
||||||
.fmt(f)?;
|
|
||||||
|
|
||||||
if !inline_comments.is_empty() {
|
|
||||||
// If the expressions exceeds the line width, format the comments in the parentheses
|
|
||||||
if_group_breaks(&inline_comments)
|
|
||||||
.with_group_id(Some(group_id))
|
|
||||||
.fmt(f)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
let comments = f.context().comments().clone();
|
||||||
}))
|
let expression_comments = comments.leading_dangling_trailing(*value);
|
||||||
.with_group_id(Some(group_id))
|
|
||||||
.fmt(f)?;
|
|
||||||
|
|
||||||
if !inline_comments.is_empty() {
|
if let Some(inline_comments) = OptionalParenthesesInlinedComments::new(
|
||||||
// If the line fits into the line width, format the comments after the parenthesized expression
|
&expression_comments,
|
||||||
if_group_fits_on_line(&inline_comments)
|
*statement,
|
||||||
|
&comments,
|
||||||
|
) {
|
||||||
|
let group_id = f.group_id("optional_parentheses");
|
||||||
|
|
||||||
|
let f = &mut WithNodeLevel::new(NodeLevel::Expression(Some(group_id)), f);
|
||||||
|
|
||||||
|
best_fit_parenthesize(&format_with(|f| {
|
||||||
|
inline_comments.mark_formatted();
|
||||||
|
|
||||||
|
value.format().with_options(Parentheses::Never).fmt(f)?;
|
||||||
|
|
||||||
|
if !inline_comments.is_empty() {
|
||||||
|
// If the expressions exceeds the line width, format the comments in the parentheses
|
||||||
|
if_group_breaks(&inline_comments).fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
.with_group_id(Some(group_id))
|
.with_group_id(Some(group_id))
|
||||||
.fmt(f)?;
|
.fmt(f)?;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
if !inline_comments.is_empty() {
|
||||||
} else {
|
// If the line fits into the line width, format the comments after the parenthesized expression
|
||||||
self.expression
|
if_group_fits_on_line(&inline_comments)
|
||||||
.format()
|
.with_group_id(Some(group_id))
|
||||||
.with_options(Parentheses::Always)
|
.fmt(f)?;
|
||||||
.fmt(f)
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// Preserve the parentheses if the expression has any leading or trailing comments,
|
||||||
|
// to avoid syntax errors, similar to `maybe_parenthesize_expression`.
|
||||||
|
value.format().with_options(Parentheses::Always).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
|
before_operator,
|
||||||
|
operator,
|
||||||
|
value,
|
||||||
|
statement,
|
||||||
|
} => {
|
||||||
|
let should_inline_comments = should_inline_comments(value, *statement, f.context());
|
||||||
|
|
||||||
|
// Use the normal `maybe_parenthesize_layout` for splittable `value`s.
|
||||||
|
if !should_inline_comments
|
||||||
|
&& !should_non_inlineable_use_best_fit(value, *statement, f.context())
|
||||||
|
{
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
before_operator,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
maybe_parenthesize_expression(
|
||||||
|
value,
|
||||||
|
*statement,
|
||||||
|
Parenthesize::IfBreaks
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let expression_comments = comments.leading_dangling_trailing(*value);
|
||||||
|
|
||||||
|
// Don't inline comments for attribute and call expressions for black compatibility
|
||||||
|
let inline_comments = if should_inline_comments {
|
||||||
|
OptionalParenthesesInlinedComments::new(
|
||||||
|
&expression_comments,
|
||||||
|
*statement,
|
||||||
|
&comments,
|
||||||
|
)
|
||||||
|
} else if expression_comments.has_leading()
|
||||||
|
|| expression_comments.has_trailing_own_line()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(OptionalParenthesesInlinedComments::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(inline_comments) = inline_comments else {
|
||||||
|
// Preserve the parentheses if the expression has any leading or trailing own line comments
|
||||||
|
// same as `maybe_parenthesize_expression`
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
before_operator,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
value.format().with_options(Parentheses::Always)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prevent inline comments to be formatted as part of the expression.
|
||||||
|
inline_comments.mark_formatted();
|
||||||
|
|
||||||
|
let mut last_target = before_operator.memoized();
|
||||||
|
|
||||||
|
// Don't parenthesize the `value` if it is known that the target will break.
|
||||||
|
// This is mainly a performance optimisation that avoids unnecessary memoization
|
||||||
|
// and using the costly `BestFitting` layout if it is already known that only the last variant
|
||||||
|
// can ever fit because the left breaks.
|
||||||
|
if last_target.inspect(f)?.will_break() {
|
||||||
|
return write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
last_target,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
value.format().with_options(Parentheses::Never),
|
||||||
|
inline_comments
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_value = value.format().with_options(Parentheses::Never).memoized();
|
||||||
|
|
||||||
|
// Tries to fit the `left` and the `value` on a single line:
|
||||||
|
// ```python
|
||||||
|
// a = b = c
|
||||||
|
// ```
|
||||||
|
let format_flat = format_with(|f| {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
last_target,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
format_value,
|
||||||
|
inline_comments
|
||||||
|
]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't break the last assignment target but parenthesize the value to see if it fits (break right first).
|
||||||
|
//
|
||||||
|
// ```python
|
||||||
|
// a["bbbbb"] = (
|
||||||
|
// c
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
let format_parenthesize_value = format_with(|f| {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
last_target,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
token("("),
|
||||||
|
block_indent(&format_args![format_value, inline_comments]),
|
||||||
|
token(")")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fall back to parenthesizing (or splitting) the last target part if we can't make the value
|
||||||
|
// fit. Don't parenthesize the value to avoid unnecessary parentheses.
|
||||||
|
//
|
||||||
|
// ```python
|
||||||
|
// a[
|
||||||
|
// "bbbbb"
|
||||||
|
// ] = c
|
||||||
|
// ```
|
||||||
|
let format_split_left = format_with(|f| {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
last_target,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
format_value,
|
||||||
|
inline_comments
|
||||||
|
]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// For call expressions, prefer breaking after the call expression's opening parentheses
|
||||||
|
// over parenthesizing the entire call expression.
|
||||||
|
if value.is_call_expr() {
|
||||||
|
best_fitting![
|
||||||
|
format_flat,
|
||||||
|
// Avoid parenthesizing the call expression if the `(` fit on the line
|
||||||
|
format_args![
|
||||||
|
last_target,
|
||||||
|
space(),
|
||||||
|
operator,
|
||||||
|
space(),
|
||||||
|
group(&format_value).should_expand(true),
|
||||||
|
],
|
||||||
|
format_parenthesize_value,
|
||||||
|
format_split_left
|
||||||
|
]
|
||||||
|
.fmt(f)
|
||||||
|
} else {
|
||||||
|
best_fitting![format_flat, format_parenthesize_value, format_split_left].fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,6 +584,35 @@ struct OptionalParenthesesInlinedComments<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptionalParenthesesInlinedComments<'a> {
|
impl<'a> OptionalParenthesesInlinedComments<'a> {
|
||||||
|
fn new(
|
||||||
|
expression_comments: &LeadingDanglingTrailingComments<'a>,
|
||||||
|
statement: AnyNodeRef<'a>,
|
||||||
|
comments: &'a Comments<'a>,
|
||||||
|
) -> Option<Self> {
|
||||||
|
if expression_comments.has_leading() || expression_comments.has_trailing_own_line() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let statement_trailing_comments = comments.trailing(statement);
|
||||||
|
let after_end_of_line = statement_trailing_comments
|
||||||
|
.partition_point(|comment| comment.line_position().is_end_of_line());
|
||||||
|
let (stmt_inline_comments, _) = statement_trailing_comments.split_at(after_end_of_line);
|
||||||
|
|
||||||
|
let after_end_of_line = expression_comments
|
||||||
|
.trailing
|
||||||
|
.partition_point(|comment| comment.line_position().is_end_of_line());
|
||||||
|
|
||||||
|
let (expression_inline_comments, trailing_own_line_comments) =
|
||||||
|
expression_comments.trailing.split_at(after_end_of_line);
|
||||||
|
|
||||||
|
debug_assert!(trailing_own_line_comments.is_empty(), "The method should have returned early if the expression has trailing own line comments");
|
||||||
|
|
||||||
|
Some(OptionalParenthesesInlinedComments {
|
||||||
|
expression: expression_inline_comments,
|
||||||
|
statement: stmt_inline_comments,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.expression.is_empty() && self.statement.is_empty()
|
self.expression.is_empty() && self.statement.is_empty()
|
||||||
}
|
}
|
||||||
|
@ -313,3 +643,97 @@ impl Format<PyFormatContext<'_>> for OptionalParenthesesInlinedComments<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub(super) enum AnyAssignmentOperator {
|
||||||
|
Assign,
|
||||||
|
AugAssign(Operator),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format<PyFormatContext<'_>> for AnyAssignmentOperator {
|
||||||
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
|
match self {
|
||||||
|
AnyAssignmentOperator::Assign => token("=").fmt(f),
|
||||||
|
AnyAssignmentOperator::AugAssign(operator) => {
|
||||||
|
write!(f, [operator.format(), token("=")])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub(super) enum AnyBeforeOperator<'a> {
|
||||||
|
Expression(&'a Expr),
|
||||||
|
TypeParams(&'a TypeParams),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format<PyFormatContext<'_>> for AnyBeforeOperator<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
|
match self {
|
||||||
|
AnyBeforeOperator::Expression(expression) => {
|
||||||
|
// Preserve parentheses around targets with comments.
|
||||||
|
if f.context().comments().has_leading(*expression)
|
||||||
|
|| f.context().comments().has_trailing(*expression)
|
||||||
|
{
|
||||||
|
expression
|
||||||
|
.format()
|
||||||
|
.with_options(Parentheses::Preserve)
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
// Never parenthesize targets that come with their own parentheses, e.g. don't parenthesize lists or dictionary literals.
|
||||||
|
else if has_target_own_parentheses(expression, f.context()) {
|
||||||
|
expression.format().with_options(Parentheses::Never).fmt(f)
|
||||||
|
} else {
|
||||||
|
parenthesize_if_expands(&expression.format().with_options(Parentheses::Never))
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Never parenthesize type params
|
||||||
|
AnyBeforeOperator::TypeParams(type_params) => type_params.format().fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` for unsplittable expressions for which comments should be inlined.
|
||||||
|
fn should_inline_comments(
|
||||||
|
expression: &Expr,
|
||||||
|
parent: AnyNodeRef,
|
||||||
|
context: &PyFormatContext,
|
||||||
|
) -> bool {
|
||||||
|
match expression {
|
||||||
|
Expr::Name(_) | Expr::NoneLiteral(_) | Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Expr::StringLiteral(string) => {
|
||||||
|
string.needs_parentheses(parent, context) == OptionalParentheses::BestFit
|
||||||
|
}
|
||||||
|
Expr::BytesLiteral(bytes) => {
|
||||||
|
bytes.needs_parentheses(parent, context) == OptionalParentheses::BestFit
|
||||||
|
}
|
||||||
|
Expr::FString(fstring) => {
|
||||||
|
fstring.needs_parentheses(parent, context) == OptionalParentheses::BestFit
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests whether an expression that for which comments shouldn't be inlined should use the best fit layout
|
||||||
|
fn should_non_inlineable_use_best_fit(
|
||||||
|
expr: &Expr,
|
||||||
|
parent: AnyNodeRef,
|
||||||
|
context: &PyFormatContext,
|
||||||
|
) -> bool {
|
||||||
|
match expr {
|
||||||
|
Expr::Attribute(attribute) => {
|
||||||
|
attribute.needs_parentheses(parent, context) == OptionalParentheses::BestFit
|
||||||
|
}
|
||||||
|
Expr::Call(call) => call.needs_parentheses(parent, context) == OptionalParentheses::BestFit,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` for targets that should not be parenthesized if they split because their expanded
|
||||||
|
/// layout comes with their own set of parentheses.
|
||||||
|
pub(super) fn has_target_own_parentheses(target: &Expr, context: &PyFormatContext) -> bool {
|
||||||
|
matches!(target, Expr::Tuple(_)) || has_own_parentheses(target, context).is_some()
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,13 @@ use ruff_formatter::write;
|
||||||
use ruff_python_ast::StmtAugAssign;
|
use ruff_python_ast::StmtAugAssign;
|
||||||
|
|
||||||
use crate::comments::{SourceComment, SuppressionKind};
|
use crate::comments::{SourceComment, SuppressionKind};
|
||||||
|
use crate::expression::parentheses::is_expression_parenthesized;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::stmt_assign::FormatStatementsLastExpression;
|
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
|
||||||
|
use crate::statement::stmt_assign::{
|
||||||
|
has_target_own_parentheses, AnyAssignmentOperator, AnyBeforeOperator,
|
||||||
|
FormatStatementsLastExpression,
|
||||||
|
};
|
||||||
use crate::statement::trailing_semicolon;
|
use crate::statement::trailing_semicolon;
|
||||||
use crate::{AsFormat, FormatNodeRule};
|
use crate::{AsFormat, FormatNodeRule};
|
||||||
|
|
||||||
|
@ -18,17 +23,35 @@ impl FormatNodeRule<StmtAugAssign> for FormatStmtAugAssign {
|
||||||
value,
|
value,
|
||||||
range: _,
|
range: _,
|
||||||
} = item;
|
} = item;
|
||||||
write!(
|
|
||||||
f,
|
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context())
|
||||||
[
|
&& has_target_own_parentheses(target, f.context())
|
||||||
target.format(),
|
&& !is_expression_parenthesized(
|
||||||
space(),
|
target.into(),
|
||||||
op.format(),
|
f.context().comments().ranges(),
|
||||||
token("="),
|
f.context().source(),
|
||||||
space(),
|
)
|
||||||
FormatStatementsLastExpression::new(value, item)
|
{
|
||||||
]
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
)?;
|
before_operator: AnyBeforeOperator::Expression(target),
|
||||||
|
operator: AnyAssignmentOperator::AugAssign(*op),
|
||||||
|
value,
|
||||||
|
statement: item.into(),
|
||||||
|
}
|
||||||
|
.fmt(f)?;
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
target.format(),
|
||||||
|
space(),
|
||||||
|
op.format(),
|
||||||
|
token("="),
|
||||||
|
space(),
|
||||||
|
FormatStatementsLastExpression::left_to_right(value, item)
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
if f.options().source_type().is_ipynb()
|
if f.options().source_type().is_ipynb()
|
||||||
&& f.context().node_level().is_last_top_level_statement()
|
&& f.context().node_level().is_last_top_level_statement()
|
||||||
|
|
|
@ -30,7 +30,10 @@ impl FormatNodeRule<StmtReturn> for FormatStmtReturn {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[space(), FormatStatementsLastExpression::new(value, item)]
|
[
|
||||||
|
space(),
|
||||||
|
FormatStatementsLastExpression::left_to_right(value, item)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
|
|
|
@ -3,7 +3,10 @@ use ruff_python_ast::StmtTypeAlias;
|
||||||
|
|
||||||
use crate::comments::{SourceComment, SuppressionKind};
|
use crate::comments::{SourceComment, SuppressionKind};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::stmt_assign::FormatStatementsLastExpression;
|
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
|
||||||
|
use crate::statement::stmt_assign::{
|
||||||
|
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatStmtTypeAlias;
|
pub struct FormatStmtTypeAlias;
|
||||||
|
@ -20,6 +23,16 @@ impl FormatNodeRule<StmtTypeAlias> for FormatStmtTypeAlias {
|
||||||
write!(f, [token("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 {
|
||||||
|
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context()) {
|
||||||
|
return FormatStatementsLastExpression::RightToLeft {
|
||||||
|
before_operator: AnyBeforeOperator::TypeParams(type_params),
|
||||||
|
operator: AnyAssignmentOperator::Assign,
|
||||||
|
value,
|
||||||
|
statement: item.into(),
|
||||||
|
}
|
||||||
|
.fmt(f);
|
||||||
|
};
|
||||||
|
|
||||||
write!(f, [type_params.format()])?;
|
write!(f, [type_params.format()])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +42,7 @@ impl FormatNodeRule<StmtTypeAlias> for FormatStmtTypeAlias {
|
||||||
space(),
|
space(),
|
||||||
token("="),
|
token("="),
|
||||||
space(),
|
space(),
|
||||||
FormatStatementsLastExpression::new(value, item)
|
FormatStatementsLastExpression::left_to_right(value, item)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ def f(
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -7,26 +7,16 @@
|
@@ -7,23 +7,13 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
# "AnnAssign"s now also work
|
# "AnnAssign"s now also work
|
||||||
|
@ -120,16 +120,10 @@ def f(
|
||||||
- | Loooooooooooooooooooooooong
|
- | Loooooooooooooooooooooooong
|
||||||
- | Loooooooooooooooooooooooong
|
- | Loooooooooooooooooooooooong
|
||||||
-) = 7
|
-) = 7
|
||||||
-z: Short | Short2 | Short3 | Short4 = 8
|
|
||||||
-z: int = 2.3
|
|
||||||
-z: int = foo()
|
|
||||||
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
||||||
+z: (Short | Short2 | Short3 | Short4) = 8
|
z: Short | Short2 | Short3 | Short4 = 8
|
||||||
+z: (int) = 2.3
|
z: int = 2.3
|
||||||
+z: (int) = foo()
|
z: int = foo()
|
||||||
|
|
||||||
# In case I go for not enforcing parantheses, this might get improved at the same time
|
|
||||||
x = (
|
|
||||||
@@ -63,7 +53,7 @@
|
@@ -63,7 +53,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,9 +180,9 @@ z: (int)
|
||||||
|
|
||||||
|
|
||||||
z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
||||||
z: (Short | Short2 | Short3 | Short4) = 8
|
z: Short | Short2 | Short3 | Short4 = 8
|
||||||
z: (int) = 2.3
|
z: int = 2.3
|
||||||
z: (int) = foo()
|
z: int = foo()
|
||||||
|
|
||||||
# In case I go for not enforcing parantheses, this might get improved at the same time
|
# In case I go for not enforcing parantheses, this might get improved at the same time
|
||||||
x = (
|
x = (
|
||||||
|
|
|
@ -789,14 +789,12 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
||||||
- "This is a large string that has a type annotation attached to it. A type"
|
- "This is a large string that has a type annotation attached to it. A type"
|
||||||
- " annotation should NOT stop a long string from being wrapped."
|
- " annotation should NOT stop a long string from being wrapped."
|
||||||
-)
|
-)
|
||||||
-annotated_variable: Literal["fakse_literal"] = (
|
+annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
||||||
|
annotated_variable: Literal["fakse_literal"] = (
|
||||||
- "This is a large string that has a type annotation attached to it. A type"
|
- "This is a large string that has a type annotation attached to it. A type"
|
||||||
- " annotation should NOT stop a long string from being wrapped."
|
- " annotation should NOT stop a long string from being wrapped."
|
||||||
-)
|
+ "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
||||||
+annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
)
|
||||||
+annotated_variable: Literal[
|
|
||||||
+ "fakse_literal"
|
|
||||||
+] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
|
||||||
|
|
||||||
-backslashes = (
|
-backslashes = (
|
||||||
- "This is a really long string with \"embedded\" double quotes and 'single' quotes"
|
- "This is a really long string with \"embedded\" double quotes and 'single' quotes"
|
||||||
|
@ -1308,9 +1306,9 @@ annotated_variable: Final = (
|
||||||
+ "using the '+' operator."
|
+ "using the '+' operator."
|
||||||
)
|
)
|
||||||
annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
||||||
annotated_variable: Literal[
|
annotated_variable: Literal["fakse_literal"] = (
|
||||||
"fakse_literal"
|
"This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
||||||
] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
|
)
|
||||||
|
|
||||||
backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\"
|
backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\"
|
||||||
backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\"
|
backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\"
|
||||||
|
|
|
@ -832,7 +832,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
||||||
|
|
||||||
some_commented_string = ( # This comment stays at the top.
|
some_commented_string = ( # This comment stays at the top.
|
||||||
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
|
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
|
||||||
@@ -279,38 +280,27 @@
|
@@ -279,36 +280,25 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
lpar_and_rpar_have_comments = func_call( # LPAR Comment
|
lpar_and_rpar_have_comments = func_call( # LPAR Comment
|
||||||
|
@ -852,33 +852,31 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
||||||
- f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
- f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||||
-)
|
-)
|
||||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||||
+
|
|
||||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
|
||||||
|
|
||||||
-cmd_fstring = (
|
-cmd_fstring = (
|
||||||
- "sudo -E deluge-console info --detailed --sort-reverse=time_added"
|
- "sudo -E deluge-console info --detailed --sort-reverse=time_added"
|
||||||
- f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
- f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||||
-)
|
-)
|
||||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'"
|
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||||
|
|
||||||
-cmd_fstring = (
|
-cmd_fstring = (
|
||||||
- "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is"
|
- "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is"
|
||||||
- f" None else ID}} | perl -nE 'print if /^{field}:/'"
|
- f" None else ID}} | perl -nE 'print if /^{field}:/'"
|
||||||
-)
|
-)
|
||||||
+fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}."
|
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'"
|
||||||
|
|
||||||
|
+fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}."
|
||||||
|
+
|
||||||
fstring = (
|
fstring = (
|
||||||
- "This string really doesn't need to be an {{fstring}}, but this one most"
|
- "This string really doesn't need to be an {{fstring}}, but this one most"
|
||||||
- f" certainly, absolutely {does}."
|
- f" certainly, absolutely {does}."
|
||||||
+ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
+ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
||||||
)
|
)
|
||||||
|
-
|
||||||
-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||||
|
|
||||||
-
|
|
||||||
class A:
|
class A:
|
||||||
class B:
|
|
||||||
def foo():
|
|
||||||
@@ -364,10 +354,7 @@
|
@@ -364,10 +354,7 @@
|
||||||
def foo():
|
def foo():
|
||||||
if not hasattr(module, name):
|
if not hasattr(module, name):
|
||||||
|
@ -933,7 +931,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -432,14 +415,12 @@
|
@@ -432,9 +415,7 @@
|
||||||
assert xxxxxxx_xxxx in [
|
assert xxxxxxx_xxxx in [
|
||||||
x.xxxxx.xxxxxx.xxxxx.xxxxxx,
|
x.xxxxx.xxxxxx.xxxxx.xxxxxx,
|
||||||
x.xxxxx.xxxxxx.xxxxx.xxxx,
|
x.xxxxx.xxxxxx.xxxxx.xxxx,
|
||||||
|
@ -943,15 +941,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
||||||
+ ], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
|
+ ], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
|
||||||
|
|
||||||
|
|
||||||
-value.__dict__[key] = (
|
value.__dict__[key] = (
|
||||||
- "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
|
||||||
-)
|
|
||||||
+value.__dict__[
|
|
||||||
+ key
|
|
||||||
+] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
|
||||||
|
|
||||||
RE_ONE_BACKSLASH = {
|
|
||||||
"asdf_hjkl_jkl": re.compile(
|
|
||||||
@@ -449,8 +430,7 @@
|
@@ -449,8 +430,7 @@
|
||||||
|
|
||||||
RE_TWO_BACKSLASHES = {
|
RE_TWO_BACKSLASHES = {
|
||||||
|
@ -1627,9 +1617,9 @@ class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx):
|
||||||
], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
|
], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
|
||||||
|
|
||||||
|
|
||||||
value.__dict__[
|
value.__dict__[key] = (
|
||||||
key
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
)
|
||||||
|
|
||||||
RE_ONE_BACKSLASH = {
|
RE_ONE_BACKSLASH = {
|
||||||
"asdf_hjkl_jkl": re.compile(
|
"asdf_hjkl_jkl": re.compile(
|
||||||
|
|
|
@ -118,57 +118,7 @@ a = (
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -1,29 +1,31 @@
|
@@ -60,9 +60,7 @@
|
||||||
-first_item, second_item = (
|
|
||||||
- some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
- first_argument, second_argument, third_argument
|
|
||||||
- )
|
|
||||||
+(
|
|
||||||
+ first_item,
|
|
||||||
+ second_item,
|
|
||||||
+) = some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
+ first_argument, second_argument, third_argument
|
|
||||||
)
|
|
||||||
|
|
||||||
-some_dict["with_a_long_key"] = (
|
|
||||||
- some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
- first_argument, second_argument, third_argument
|
|
||||||
- )
|
|
||||||
+some_dict[
|
|
||||||
+ "with_a_long_key"
|
|
||||||
+] = some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
+ first_argument, second_argument, third_argument
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure it works when the RHS only has one pair of (optional) parens.
|
|
||||||
-first_item, second_item = (
|
|
||||||
- some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
|
||||||
-)
|
|
||||||
+(
|
|
||||||
+ first_item,
|
|
||||||
+ second_item,
|
|
||||||
+) = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
|
||||||
|
|
||||||
-some_dict["with_a_long_key"] = (
|
|
||||||
- some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
|
||||||
-)
|
|
||||||
+some_dict[
|
|
||||||
+ "with_a_long_key"
|
|
||||||
+] = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
|
||||||
|
|
||||||
# Make sure chaining assignments work.
|
|
||||||
-first_item, second_item, third_item, forth_item = m["everything"] = (
|
|
||||||
- some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
- first_argument, second_argument, third_argument
|
|
||||||
- )
|
|
||||||
+first_item, second_item, third_item, forth_item = m[
|
|
||||||
+ "everything"
|
|
||||||
+] = some_looooooooong_module.some_looooooooooooooong_function_name(
|
|
||||||
+ first_argument, second_argument, third_argument
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure when the RHS's first split at the non-optional paren fits,
|
|
||||||
@@ -60,9 +62,7 @@
|
|
||||||
some_arg
|
some_arg
|
||||||
).intersection(pk_cols)
|
).intersection(pk_cols)
|
||||||
|
|
||||||
|
@ -179,76 +129,37 @@ a = (
|
||||||
|
|
||||||
some_kind_of_table[
|
some_kind_of_table[
|
||||||
some_key # type: ignore # noqa: E501
|
some_key # type: ignore # noqa: E501
|
||||||
@@ -85,15 +85,29 @@
|
|
||||||
)
|
|
||||||
|
|
||||||
# Multiple targets
|
|
||||||
-a = b = (
|
|
||||||
- ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
|
||||||
-)
|
|
||||||
+a = (
|
|
||||||
+ b
|
|
||||||
+) = ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
|
||||||
|
|
||||||
-a = b = c = d = e = f = g = (
|
|
||||||
+a = (
|
|
||||||
+ b
|
|
||||||
+) = (
|
|
||||||
+ c
|
|
||||||
+) = (
|
|
||||||
+ d
|
|
||||||
+) = (
|
|
||||||
+ e
|
|
||||||
+) = (
|
|
||||||
+ f
|
|
||||||
+) = (
|
|
||||||
+ g
|
|
||||||
+) = (
|
|
||||||
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
|
|
||||||
-) = i = j = (
|
|
||||||
- kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
|
|
||||||
-)
|
|
||||||
+) = (
|
|
||||||
+ i
|
|
||||||
+) = (
|
|
||||||
+ j
|
|
||||||
+) = kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
|
|
||||||
|
|
||||||
a = (
|
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
|
||||||
```python
|
```python
|
||||||
(
|
first_item, second_item = (
|
||||||
first_item,
|
some_looooooooong_module.some_looooooooooooooong_function_name(
|
||||||
second_item,
|
first_argument, second_argument, third_argument
|
||||||
) = some_looooooooong_module.some_looooooooooooooong_function_name(
|
)
|
||||||
first_argument, second_argument, third_argument
|
|
||||||
)
|
)
|
||||||
|
|
||||||
some_dict[
|
some_dict["with_a_long_key"] = (
|
||||||
"with_a_long_key"
|
some_looooooooong_module.some_looooooooooooooong_function_name(
|
||||||
] = some_looooooooong_module.some_looooooooooooooong_function_name(
|
first_argument, second_argument, third_argument
|
||||||
first_argument, second_argument, third_argument
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Make sure it works when the RHS only has one pair of (optional) parens.
|
# Make sure it works when the RHS only has one pair of (optional) parens.
|
||||||
(
|
first_item, second_item = (
|
||||||
first_item,
|
some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
||||||
second_item,
|
)
|
||||||
) = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
|
||||||
|
|
||||||
some_dict[
|
some_dict["with_a_long_key"] = (
|
||||||
"with_a_long_key"
|
some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
||||||
] = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name
|
)
|
||||||
|
|
||||||
# Make sure chaining assignments work.
|
# Make sure chaining assignments work.
|
||||||
first_item, second_item, third_item, forth_item = m[
|
first_item, second_item, third_item, forth_item = m["everything"] = (
|
||||||
"everything"
|
some_looooooooong_module.some_looooooooooooooong_function_name(
|
||||||
] = some_looooooooong_module.some_looooooooooooooong_function_name(
|
first_argument, second_argument, third_argument
|
||||||
first_argument, second_argument, third_argument
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Make sure when the RHS's first split at the non-optional paren fits,
|
# Make sure when the RHS's first split at the non-optional paren fits,
|
||||||
|
@ -308,29 +219,15 @@ some_kind_of_instance.some_kind_of_map[a_key] = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# Multiple targets
|
# Multiple targets
|
||||||
a = (
|
a = b = (
|
||||||
b
|
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
||||||
) = ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
|
)
|
||||||
|
|
||||||
a = (
|
a = b = c = d = e = f = g = (
|
||||||
b
|
|
||||||
) = (
|
|
||||||
c
|
|
||||||
) = (
|
|
||||||
d
|
|
||||||
) = (
|
|
||||||
e
|
|
||||||
) = (
|
|
||||||
f
|
|
||||||
) = (
|
|
||||||
g
|
|
||||||
) = (
|
|
||||||
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
|
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
|
||||||
) = (
|
) = i = j = (
|
||||||
i
|
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
|
||||||
) = (
|
)
|
||||||
j
|
|
||||||
) = kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
|
|
||||||
|
|
||||||
a = (
|
a = (
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
|
|
@ -421,4 +421,44 @@ def test6():
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -72,13 +72,13 @@
|
||||||
|
## Breaking left
|
||||||
|
|
||||||
|
# Should break `[a]` first
|
||||||
|
-____[
|
||||||
|
- a
|
||||||
|
-] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv # c
|
||||||
|
+____[a] = (
|
||||||
|
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv # c
|
||||||
|
+)
|
||||||
|
|
||||||
|
-____[
|
||||||
|
- a
|
||||||
|
-] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv # cc
|
||||||
|
+____[a] = (
|
||||||
|
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv # cc
|
||||||
|
+)
|
||||||
|
|
||||||
|
(
|
||||||
|
# some weird comments
|
||||||
|
@@ -136,9 +136,9 @@
|
||||||
|
# 89 characters parenthesized (collapse)
|
||||||
|
____a: a = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvv # c
|
||||||
|
|
||||||
|
-_a: a[
|
||||||
|
- b
|
||||||
|
-] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvv # c
|
||||||
|
+_a: a[b] = (
|
||||||
|
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvv # c
|
||||||
|
+)
|
||||||
|
|
||||||
|
## Augmented Assign
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -204,17 +204,17 @@ class RemoveNewlineBeforeClassDocstring:
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
"""Black's `Preview.prefer_splitting_right_hand_side_of_assignments`"""
|
"""Black's `Preview.prefer_splitting_right_hand_side_of_assignments`"""
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] = (
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
cccccccc.ccccccccccccc.cccccccc
|
||||||
] = cccccccc.ccccccccccccc.cccccccc
|
)
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] = (
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
cccccccc.ccccccccccccc().cccccccc
|
||||||
] = cccccccc.ccccccccccccc().cccccccc
|
)
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] = (
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
cccccccc.ccccccccccccc(d).cccccccc
|
||||||
] = cccccccc.ccccccccccccc(d).cccccccc
|
)
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] = (
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] = (
|
||||||
cccccccc.ccccccccccccc(d).cccccccc + e
|
cccccccc.ccccccccccccc(d).cccccccc + e
|
||||||
|
@ -228,12 +228,12 @@ def f():
|
||||||
+ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
+ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||||
)
|
)
|
||||||
|
|
||||||
self._cache: dict[
|
self._cache: dict[DependencyCacheKey, list[list[DependencyPackage]]] = (
|
||||||
DependencyCacheKey, list[list[DependencyPackage]]
|
collections.defaultdict(list)
|
||||||
] = collections.defaultdict(list)
|
)
|
||||||
self._cached_dependencies_by_level: dict[
|
self._cached_dependencies_by_level: dict[int, list[DependencyCacheKey]] = (
|
||||||
int, list[DependencyCacheKey]
|
collections.defaultdict(list)
|
||||||
] = collections.defaultdict(list)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,4 +67,24 @@ class DefaultRunner:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -7,9 +7,9 @@
|
||||||
|
Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb()
|
||||||
|
)
|
||||||
|
|
||||||
|
-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: (
|
||||||
|
- Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
-) = Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb()
|
||||||
|
+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||||
|
+ Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb()
|
||||||
|
+)
|
||||||
|
|
||||||
|
JSONSerializable: TypeAlias = (
|
||||||
|
"str | int | float | bool | None | list | tuple | JSONMapping"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -169,4 +169,40 @@ c = b[dddddd, aaaaaa] = (
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -1,7 +1,5 @@
|
||||||
|
# break left hand side
|
||||||
|
-a1akjdshflkjahdslkfjlasfdahjlfds = (
|
||||||
|
- bakjdshflkjahdslkfjlasfdahjlfds
|
||||||
|
-) = (
|
||||||
|
+a1akjdshflkjahdslkfjlasfdahjlfds = bakjdshflkjahdslkfjlasfdahjlfds = (
|
||||||
|
cakjdshflkjahdslkfjlasfdahjlfds
|
||||||
|
) = kjaödkjaföjfahlfdalfhaöfaöfhaöfha = fkjaödkjaföjfahlfdalfhaöfaöfhaöfha = g = 3
|
||||||
|
|
||||||
|
@@ -9,15 +7,13 @@
|
||||||
|
a2 = b2 = 2
|
||||||
|
|
||||||
|
# Break the last element
|
||||||
|
-a = (
|
||||||
|
- asdf
|
||||||
|
-) = (
|
||||||
|
+a = asdf = (
|
||||||
|
fjhalsdljfalflaflapamsakjsdhflakjdslfjhalsdljfalflaflapamsakjsdhflakjdslfjhalsdljfal
|
||||||
|
) = 1
|
||||||
|
|
||||||
|
-aa = [
|
||||||
|
- bakjdshflkjahdslkfjlasfdahjlfds
|
||||||
|
-] = dddd = ddd = fkjaödkjaföjfahlfdalfhaöfaöfhaöfha = g = [3]
|
||||||
|
+aa = [bakjdshflkjahdslkfjlasfdahjlfds] = dddd = ddd = (
|
||||||
|
+ fkjaödkjaföjfahlfdalfhaöfaöfhaöfha
|
||||||
|
+) = g = [3]
|
||||||
|
|
||||||
|
aa = [] = dddd = ddd = fkjaödkjaföjfahlfdalfhaöfaöfhaöfha = g = [3]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,457 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```python
|
||||||
|
#######
|
||||||
|
# Unsplittable target and value
|
||||||
|
|
||||||
|
# Only parenthesize the value if it makes it fit, otherwise avoid parentheses.
|
||||||
|
b = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
bbbbbbbbbbbbbbbb = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvv
|
||||||
|
|
||||||
|
# Avoid parenthesizing the value even if the target exceeds the configured width
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = bbb
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Splittable targets
|
||||||
|
|
||||||
|
# Does not double-parenthesize tuples
|
||||||
|
(
|
||||||
|
first_item,
|
||||||
|
second_item,
|
||||||
|
) = some_looooooooong_module.some_loooooog_function_name(
|
||||||
|
first_argument, second_argument, third_argument
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Preserve parentheses around the first target
|
||||||
|
(
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
]["destinations"]["destination"][0]["ip_address"]
|
||||||
|
) = dst
|
||||||
|
|
||||||
|
# Augmented assignment
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
] += dst
|
||||||
|
|
||||||
|
# Always parenthesize the value if it avoids splitting the target, regardless of the value's width.
|
||||||
|
_a: a[aaaa] = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
)
|
||||||
|
|
||||||
|
#####
|
||||||
|
# Avoid parenthesizing the value if the expression right before the `=` splits to avoid an unnecessary pair of parentheses
|
||||||
|
|
||||||
|
# The type annotation is guaranteed to split because it is too long.
|
||||||
|
_a: a[
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target is too long
|
||||||
|
(
|
||||||
|
aaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target splits because of a magic trailing comma
|
||||||
|
(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The targets split because of a comment
|
||||||
|
(
|
||||||
|
# leading
|
||||||
|
a
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a
|
||||||
|
# trailing
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a, # nested
|
||||||
|
b
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Multi targets
|
||||||
|
|
||||||
|
# Black always parenthesizes the right if using multiple targets regardless if the parenthesized value exceeds the
|
||||||
|
# the configured line width or not
|
||||||
|
aaaa = bbbbbbbbbbbbbbbb = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
)
|
||||||
|
|
||||||
|
# Black does parenthesize the target if the target itself exceeds the line width and only parenthesizes
|
||||||
|
# the values if it makes it fit.
|
||||||
|
# The second target is too long to ever fit into the configured line width.
|
||||||
|
aaaa = (
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdddd
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
# Does also apply for other multi target assignments, as soon as a single target exceeds the configured
|
||||||
|
# width
|
||||||
|
aaaaaa = a["aaa"] = bbbbb[aa, bbb, cccc] = dddddddddd = eeeeee = (
|
||||||
|
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Call expressions:
|
||||||
|
# For unsplittable targets: Parenthesize the call expression if it makes it fit.
|
||||||
|
#
|
||||||
|
# For splittable targets:
|
||||||
|
# Only parenthesize a call expression if the parens of the call don't fit on the same line
|
||||||
|
# as the target. Don't parenthesize the call expression if the target (or annotation) right before
|
||||||
|
# splits.
|
||||||
|
|
||||||
|
# Don't parenthesize the function call if the left is unsplittable.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = a.b.function(
|
||||||
|
arg1, arg2, arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = (
|
||||||
|
function()
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = (
|
||||||
|
a.b.function(arg1, arg2, arg3)
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function()
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
####### Fluent call expressions
|
||||||
|
# Uses the regular `Multiline` layout where the entire `value` gets parenthesized
|
||||||
|
# if it doesn't fit on the line.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use = (
|
||||||
|
function().b().c([1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Test comment inlining
|
||||||
|
value.__dict__[key] = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Don't parenthesize the value because the target's trailing comma forces it to split.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
|
||||||
|
# Parenthesize the value, but don't duplicate the comment.
|
||||||
|
a[aaaaaaa, b] = (
|
||||||
|
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format both as flat, but don't loos the comment.
|
||||||
|
a[aaaaaaa, b] = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # comment
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# Test the case where a parenthesized value now fits:
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b
|
||||||
|
] = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Splits the target but not the value because of the magic trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Splits the second target because of the comment and the first target because of the trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = (
|
||||||
|
# leading comment
|
||||||
|
b
|
||||||
|
) = (
|
||||||
|
cccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
########
|
||||||
|
# Type Alias Statement
|
||||||
|
type A[str, int, number] = VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtin
|
||||||
|
|
||||||
|
type A[VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtinthatExceedsTheWidth] = str
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
### Output 1
|
||||||
|
```
|
||||||
|
indent-style = space
|
||||||
|
line-width = 88
|
||||||
|
indent-width = 4
|
||||||
|
quote-style = Double
|
||||||
|
line-ending = LineFeed
|
||||||
|
magic-trailing-comma = Respect
|
||||||
|
docstring-code = Disabled
|
||||||
|
docstring-code-line-width = 88
|
||||||
|
preview = Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
#######
|
||||||
|
# Unsplittable target and value
|
||||||
|
|
||||||
|
# Only parenthesize the value if it makes it fit, otherwise avoid parentheses.
|
||||||
|
b = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
bbbbbbbbbbbbbbbb = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvv
|
||||||
|
)
|
||||||
|
|
||||||
|
# Avoid parenthesizing the value even if the target exceeds the configured width
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = bbb
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Splittable targets
|
||||||
|
|
||||||
|
# Does not double-parenthesize tuples
|
||||||
|
(
|
||||||
|
first_item,
|
||||||
|
second_item,
|
||||||
|
) = some_looooooooong_module.some_loooooog_function_name(
|
||||||
|
first_argument, second_argument, third_argument
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Preserve parentheses around the first target
|
||||||
|
(
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
]["destinations"]["destination"][0]["ip_address"]
|
||||||
|
) = dst
|
||||||
|
|
||||||
|
# Augmented assignment
|
||||||
|
req["ticket"]["steps"]["step"][0]["tasks"]["task"]["fields"]["field"][
|
||||||
|
"access_request"
|
||||||
|
] += dst
|
||||||
|
|
||||||
|
# Always parenthesize the value if it avoids splitting the target, regardless of the value's width.
|
||||||
|
_a: a[aaaa] = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
)
|
||||||
|
|
||||||
|
#####
|
||||||
|
# Avoid parenthesizing the value if the expression right before the `=` splits to avoid an unnecessary pair of parentheses
|
||||||
|
|
||||||
|
# The type annotation is guaranteed to split because it is too long.
|
||||||
|
_a: a[
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target is too long
|
||||||
|
(
|
||||||
|
aaaaaaaaaaa,
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The target splits because of a magic trailing comma
|
||||||
|
(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
# The targets split because of a comment
|
||||||
|
(
|
||||||
|
# leading
|
||||||
|
a
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a
|
||||||
|
# trailing
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
(
|
||||||
|
a, # nested
|
||||||
|
b,
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvvvv
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Multi targets
|
||||||
|
|
||||||
|
# Black always parenthesizes the right if using multiple targets regardless if the parenthesized value exceeds the
|
||||||
|
# the configured line width or not
|
||||||
|
aaaa = bbbbbbbbbbbbbbbb = (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
)
|
||||||
|
|
||||||
|
# Black does parenthesize the target if the target itself exceeds the line width and only parenthesizes
|
||||||
|
# the values if it makes it fit.
|
||||||
|
# The second target is too long to ever fit into the configured line width.
|
||||||
|
aaaa = (
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdddd
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvvvvee
|
||||||
|
|
||||||
|
# Does also apply for other multi target assignments, as soon as a single target exceeds the configured
|
||||||
|
# width
|
||||||
|
aaaaaa = a["aaa"] = bbbbb[aa, bbb, cccc] = dddddddddd = eeeeee = (
|
||||||
|
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Call expressions:
|
||||||
|
# For unsplittable targets: Parenthesize the call expression if it makes it fit.
|
||||||
|
#
|
||||||
|
# For splittable targets:
|
||||||
|
# Only parenthesize a call expression if the parens of the call don't fit on the same line
|
||||||
|
# as the target. Don't parenthesize the call expression if the target (or annotation) right before
|
||||||
|
# splits.
|
||||||
|
|
||||||
|
# Don't parenthesize the function call if the left is unsplittable.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = a.b.function(
|
||||||
|
arg1, arg2, arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function()
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = (
|
||||||
|
a.b.function(arg1, arg2, arg3)
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function()
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||||
|
)
|
||||||
|
this_is_a_ridiculously_long_name_and_nobodyddddddddddddddddddddddddddddddd = function(
|
||||||
|
[1, 2, 3],
|
||||||
|
arg1,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg2,
|
||||||
|
[1, 2, 3],
|
||||||
|
arg3,
|
||||||
|
dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd,
|
||||||
|
eeeeeeeeeeeeee,
|
||||||
|
)
|
||||||
|
|
||||||
|
####### Fluent call expressions
|
||||||
|
# Uses the regular `Multiline` layout where the entire `value` gets parenthesized
|
||||||
|
# if it doesn't fit on the line.
|
||||||
|
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use = (
|
||||||
|
function().b().c([1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#######
|
||||||
|
# Test comment inlining
|
||||||
|
value.__dict__[key] = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
value.__dict__.keye = (
|
||||||
|
"test" # set some Thrift field to non-None in the struct aa bb cc dd ee
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Don't parenthesize the value because the target's trailing comma forces it to split.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
|
||||||
|
# Parenthesize the value, but don't duplicate the comment.
|
||||||
|
a[aaaaaaa, b] = (
|
||||||
|
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format both as flat, but don't loos the comment.
|
||||||
|
a[aaaaaaa, b] = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # comment
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# Test the case where a parenthesized value now fits:
|
||||||
|
a[aaaaaaa, b] = cccccccc # comment
|
||||||
|
|
||||||
|
# Splits the target but not the value because of the magic trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = cccccccc # comment
|
||||||
|
|
||||||
|
# Splits the second target because of the comment and the first target because of the trailing comma.
|
||||||
|
a[
|
||||||
|
aaaaaaa,
|
||||||
|
b,
|
||||||
|
] = (
|
||||||
|
# leading comment
|
||||||
|
b
|
||||||
|
) = cccccccc # comment
|
||||||
|
|
||||||
|
|
||||||
|
########
|
||||||
|
# Type Alias Statement
|
||||||
|
type A[str, int, number] = (
|
||||||
|
VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtin
|
||||||
|
)
|
||||||
|
|
||||||
|
type A[
|
||||||
|
VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtinthatExceedsTheWidth
|
||||||
|
] = str
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue