mirror of https://github.com/astral-sh/ruff
consolidate layout and preview checks
This commit is contained in:
parent
93a958a734
commit
17a1065fad
|
|
@ -137,12 +137,16 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
||||||
write!(f, [dangling_comments(dangling)])?;
|
write!(f, [dangling_comments(dangling)])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatBody {
|
if !preview {
|
||||||
body,
|
return body.format().fmt(f);
|
||||||
dangling,
|
}
|
||||||
layout: self.layout,
|
|
||||||
|
let fmt_body = FormatBody { body, dangling };
|
||||||
|
|
||||||
|
match self.layout {
|
||||||
|
ExprLambdaLayout::Assignment => fits_expanded(&fmt_body).fmt(f),
|
||||||
|
ExprLambdaLayout::Default => fmt_body.fmt(f),
|
||||||
}
|
}
|
||||||
.fmt(f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,219 +207,203 @@ impl NeedsParentheses for ExprLambda {
|
||||||
struct FormatBody<'a> {
|
struct FormatBody<'a> {
|
||||||
body: &'a Expr,
|
body: &'a Expr,
|
||||||
dangling: &'a [SourceComment],
|
dangling: &'a [SourceComment],
|
||||||
layout: ExprLambdaLayout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for FormatBody<'_> {
|
impl Format<PyFormatContext<'_>> for FormatBody<'_> {
|
||||||
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let FormatBody {
|
let FormatBody { dangling, body } = self;
|
||||||
dangling,
|
|
||||||
body,
|
|
||||||
layout,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
if !is_parenthesize_lambda_bodies_enabled(f.context()) {
|
|
||||||
return body.format().fmt(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = *body;
|
let body = *body;
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
let body_comments = comments.leading_dangling_trailing(body);
|
let body_comments = comments.leading_dangling_trailing(body);
|
||||||
|
|
||||||
let fmt_body = format_with(|f: &mut PyFormatter| {
|
if !dangling.is_empty() {
|
||||||
if !dangling.is_empty() {
|
// Can't use partition_point because there can be additional end of line comments
|
||||||
// Can't use partition_point because there can be additional end of line comments
|
// after the initial set. All of these comments are dangling, for example:
|
||||||
// after the initial set. All of these comments are dangling, for example:
|
//
|
||||||
//
|
// ```python
|
||||||
// ```python
|
// (
|
||||||
// (
|
// lambda # 1
|
||||||
// lambda # 1
|
// # 2
|
||||||
// # 2
|
// : # 3
|
||||||
// : # 3
|
// # 4
|
||||||
// # 4
|
// y
|
||||||
// y
|
// )
|
||||||
// )
|
// ```
|
||||||
// ```
|
//
|
||||||
//
|
// and alternate between own line and end of line.
|
||||||
// and alternate between own line and end of line.
|
let (after_parameters_end_of_line, leading_body_comments) = dangling.split_at(
|
||||||
let (after_parameters_end_of_line, leading_body_comments) = dangling.split_at(
|
dangling
|
||||||
dangling
|
.iter()
|
||||||
.iter()
|
.position(|comment| comment.line_position().is_own_line())
|
||||||
.position(|comment| comment.line_position().is_own_line())
|
.unwrap_or(dangling.len()),
|
||||||
.unwrap_or(dangling.len()),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// If the body is parenthesized and has its own leading comments, preserve the
|
// If the body is parenthesized and has its own leading comments, preserve the
|
||||||
// separation between the dangling lambda comments and the body comments. For
|
// separation between the dangling lambda comments and the body comments. For
|
||||||
// example, preserve this comment positioning:
|
// example, preserve this comment positioning:
|
||||||
//
|
//
|
||||||
// ```python
|
// ```python
|
||||||
// (
|
// (
|
||||||
// lambda: # 1
|
// lambda: # 1
|
||||||
// # 2
|
// # 2
|
||||||
// ( # 3
|
// ( # 3
|
||||||
// x
|
// x
|
||||||
// )
|
// )
|
||||||
// )
|
// )
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// 1 and 2 are dangling on the lambda and emitted first, followed by a hard line
|
// 1 and 2 are dangling on the lambda and emitted first, followed by a hard line
|
||||||
// break and the parenthesized body with its leading comments.
|
// break and the parenthesized body with its leading comments.
|
||||||
//
|
//
|
||||||
// However, when removing 2, 1 and 3 can instead be formatted on the same line:
|
// However, when removing 2, 1 and 3 can instead be formatted on the same line:
|
||||||
//
|
//
|
||||||
// ```python
|
// ```python
|
||||||
// (
|
// (
|
||||||
// lambda: ( # 1 # 3
|
// lambda: ( # 1 # 3
|
||||||
// x
|
// x
|
||||||
// )
|
// )
|
||||||
// )
|
// )
|
||||||
// ```
|
// ```
|
||||||
let comments = f.context().comments();
|
let comments = f.context().comments();
|
||||||
if is_expression_parenthesized(body.into(), comments.ranges(), f.context().source())
|
if is_expression_parenthesized(body.into(), comments.ranges(), f.context().source())
|
||||||
&& comments.has_leading(body)
|
&& comments.has_leading(body)
|
||||||
{
|
{
|
||||||
trailing_comments(dangling).fmt(f)?;
|
trailing_comments(dangling).fmt(f)?;
|
||||||
|
|
||||||
if leading_body_comments.is_empty() {
|
if leading_body_comments.is_empty() {
|
||||||
space().fmt(f)?;
|
space().fmt(f)?;
|
||||||
} else {
|
|
||||||
hard_line_break().fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.format().with_options(Parentheses::Always).fmt(f)
|
|
||||||
} else {
|
} else {
|
||||||
write!(
|
hard_line_break().fmt(f)?;
|
||||||
f,
|
|
||||||
[
|
|
||||||
space(),
|
|
||||||
token("("),
|
|
||||||
trailing_comments(after_parameters_end_of_line),
|
|
||||||
block_indent(&format_args!(
|
|
||||||
leading_comments(leading_body_comments),
|
|
||||||
body.format().with_options(Parentheses::Never)
|
|
||||||
)),
|
|
||||||
token(")")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// If the body has comments, we always want to preserve the parentheses. This also
|
|
||||||
// ensures that we correctly handle parenthesized comments, and don't need to worry
|
|
||||||
// about them in the implementation below.
|
|
||||||
else if body_comments.has_leading() || body_comments.has_trailing_own_line() {
|
|
||||||
body.format().with_options(Parentheses::Always).fmt(f)
|
body.format().with_options(Parentheses::Always).fmt(f)
|
||||||
}
|
} else {
|
||||||
// Calls and subscripts require special formatting because they have their own
|
write!(
|
||||||
// parentheses, but they can also have an arbitrary amount of text before the
|
f,
|
||||||
// opening parenthesis. We want to avoid cases where we keep a long callable on the
|
[
|
||||||
// same line as the lambda parameters. For example, `db_evmtx...` in:
|
space(),
|
||||||
//
|
token("("),
|
||||||
// ```py
|
trailing_comments(after_parameters_end_of_line),
|
||||||
// transaction_count = self._query_txs_for_range(
|
block_indent(&format_args!(
|
||||||
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range(
|
leading_comments(leading_body_comments),
|
||||||
// chain_id=_chain_id,
|
body.format().with_options(Parentheses::Never)
|
||||||
// from_ts=from_ts,
|
)),
|
||||||
// to_ts=to_ts,
|
token(")")
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// should cause the whole lambda body to be parenthesized instead:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// transaction_count = self._query_txs_for_range(
|
|
||||||
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: (
|
|
||||||
// db_evmtx.count_transactions_in_range(
|
|
||||||
// chain_id=_chain_id,
|
|
||||||
// from_ts=from_ts,
|
|
||||||
// to_ts=to_ts,
|
|
||||||
// )
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
else if matches!(body, Expr::Call(_) | Expr::Subscript(_)) {
|
|
||||||
let unparenthesized = body.format().with_options(Parentheses::Never);
|
|
||||||
if CallChainLayout::from_expression(
|
|
||||||
body.into(),
|
|
||||||
comments.ranges(),
|
|
||||||
f.context().source(),
|
|
||||||
) == CallChainLayout::Fluent
|
|
||||||
{
|
|
||||||
parenthesize_if_expands(&unparenthesized).fmt(f)
|
|
||||||
} else {
|
|
||||||
let unparenthesized = unparenthesized.memoized();
|
|
||||||
if unparenthesized.inspect(f)?.will_break() {
|
|
||||||
expand_parent().fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
best_fitting![
|
|
||||||
// body all flat
|
|
||||||
unparenthesized,
|
|
||||||
// body expanded
|
|
||||||
group(&unparenthesized).should_expand(true),
|
|
||||||
// parenthesized
|
|
||||||
format_args![token("("), block_indent(&unparenthesized), token(")")]
|
|
||||||
]
|
]
|
||||||
.fmt(f)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the body has comments, we always want to preserve the parentheses. This also
|
||||||
|
// ensures that we correctly handle parenthesized comments, and don't need to worry
|
||||||
|
// about them in the implementation below.
|
||||||
|
else if body_comments.has_leading() || body_comments.has_trailing_own_line() {
|
||||||
|
body.format().with_options(Parentheses::Always).fmt(f)
|
||||||
|
}
|
||||||
|
// Calls and subscripts require special formatting because they have their own
|
||||||
|
// parentheses, but they can also have an arbitrary amount of text before the
|
||||||
|
// opening parenthesis. We want to avoid cases where we keep a long callable on the
|
||||||
|
// same line as the lambda parameters. For example, `db_evmtx...` in:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// transaction_count = self._query_txs_for_range(
|
||||||
|
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range(
|
||||||
|
// chain_id=_chain_id,
|
||||||
|
// from_ts=from_ts,
|
||||||
|
// to_ts=to_ts,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// should cause the whole lambda body to be parenthesized instead:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// transaction_count = self._query_txs_for_range(
|
||||||
|
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: (
|
||||||
|
// db_evmtx.count_transactions_in_range(
|
||||||
|
// chain_id=_chain_id,
|
||||||
|
// from_ts=from_ts,
|
||||||
|
// to_ts=to_ts,
|
||||||
|
// )
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
else if matches!(body, Expr::Call(_) | Expr::Subscript(_)) {
|
||||||
|
let unparenthesized = body.format().with_options(Parentheses::Never);
|
||||||
|
if CallChainLayout::from_expression(
|
||||||
|
body.into(),
|
||||||
|
comments.ranges(),
|
||||||
|
f.context().source(),
|
||||||
|
) == CallChainLayout::Fluent
|
||||||
|
{
|
||||||
|
parenthesize_if_expands(&unparenthesized).fmt(f)
|
||||||
|
} else {
|
||||||
|
let unparenthesized = unparenthesized.memoized();
|
||||||
|
if unparenthesized.inspect(f)?.will_break() {
|
||||||
|
expand_parent().fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// For other cases with their own parentheses, such as lists, sets, dicts, tuples,
|
|
||||||
// etc., we can just format the body directly. Their own formatting results in the
|
|
||||||
// lambda being formatted well too. For example:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz]
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// gets formatted as:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [
|
|
||||||
// xxxxxxxxxxxxxxxxxxxx,
|
|
||||||
// yyyyyyyyyyyyyyyyyyyy,
|
|
||||||
// zzzzzzzzzzzzzzzzzzzz
|
|
||||||
// ]
|
|
||||||
// ```
|
|
||||||
else if has_own_parentheses(body, f.context()).is_some() {
|
|
||||||
body.format().fmt(f)
|
|
||||||
}
|
|
||||||
// Finally, for expressions without their own parentheses, use
|
|
||||||
// `parenthesize_if_expands` to add parentheses around the body, only if it expands
|
|
||||||
// across multiple lines. The `Parentheses::Never` here also removes unnecessary
|
|
||||||
// parentheses around lambda bodies that fit on one line. For example:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// is formatted as:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: (
|
|
||||||
// xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
|
||||||
// )
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// while
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx: (xxxxxxxxxxxxxxxxxxxx + 1)
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// is formatted as:
|
|
||||||
//
|
|
||||||
// ```py
|
|
||||||
// lambda xxxxxxxxxxxxxxxxxxxx: xxxxxxxxxxxxxxxxxxxx + 1
|
|
||||||
// ```
|
|
||||||
else {
|
|
||||||
parenthesize_if_expands(&body.format().with_options(Parentheses::Never)).fmt(f)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match layout {
|
best_fitting![
|
||||||
ExprLambdaLayout::Assignment => fits_expanded(&fmt_body).fmt(f),
|
// body all flat
|
||||||
ExprLambdaLayout::Default => fmt_body.fmt(f),
|
unparenthesized,
|
||||||
|
// body expanded
|
||||||
|
group(&unparenthesized).should_expand(true),
|
||||||
|
// parenthesized
|
||||||
|
format_args![token("("), block_indent(&unparenthesized), token(")")]
|
||||||
|
]
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For other cases with their own parentheses, such as lists, sets, dicts, tuples,
|
||||||
|
// etc., we can just format the body directly. Their own formatting results in the
|
||||||
|
// lambda being formatted well too. For example:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz]
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// gets formatted as:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [
|
||||||
|
// xxxxxxxxxxxxxxxxxxxx,
|
||||||
|
// yyyyyyyyyyyyyyyyyyyy,
|
||||||
|
// zzzzzzzzzzzzzzzzzzzz
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
else if has_own_parentheses(body, f.context()).is_some() {
|
||||||
|
body.format().fmt(f)
|
||||||
|
}
|
||||||
|
// Finally, for expressions without their own parentheses, use
|
||||||
|
// `parenthesize_if_expands` to add parentheses around the body, only if it expands
|
||||||
|
// across multiple lines. The `Parentheses::Never` here also removes unnecessary
|
||||||
|
// parentheses around lambda bodies that fit on one line. For example:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// is formatted as:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: (
|
||||||
|
// xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// while
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx: (xxxxxxxxxxxxxxxxxxxx + 1)
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// is formatted as:
|
||||||
|
//
|
||||||
|
// ```py
|
||||||
|
// lambda xxxxxxxxxxxxxxxxxxxx: xxxxxxxxxxxxxxxxxxxx + 1
|
||||||
|
// ```
|
||||||
|
else {
|
||||||
|
parenthesize_if_expands(&body.format().with_options(Parentheses::Never)).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue