Add dangling comment handling for lambda expressions (#7493)

## Summary

This PR adds dangling comment handling for `lambda` expressions. In
short, comments around the `lambda` and the `:` are all considered
dangling. Comments that come between the `lambda` and the `:` may be
moved after the colon for simplicity (this is an odd position for a
comment anyway), unless they also precede the lambda parameters, in
which case they're formatted before the parameters.

Closes https://github.com/astral-sh/ruff/issues/7470.

## Test Plan

`cargo test`

No change in similarity.

Before:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |

After:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99982 | 2760 | 37 |
| transformers | 0.99957 | 2587 | 398 |
| twine | 1.00000 | 33 | 0 |
| typeshed | 0.99983 | 3496 | 18 |
| warehouse | 0.99929 | 648 | 16 |
| zulip | 0.99962 | 1437 | 22 |
This commit is contained in:
Charlie Marsh 2023-09-19 15:23:51 -04:00 committed by GitHub
parent e07670ad97
commit 4c4eceee36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 310 additions and 33 deletions

View file

@ -1,6 +1,7 @@
use ruff_formatter::write;
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::ExprLambda;
use ruff_text_size::Ranged;
use crate::comments::{dangling_comments, SourceComment};
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
@ -24,28 +25,40 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
write!(f, [token("lambda")])?;
if let Some(parameters) = parameters {
// In this context, a dangling comment can either be a comment between the `lambda` the
// parameters, or a comment between the parameters and the body.
let (dangling_before_parameters, dangling_after_parameters) = dangling
.split_at(dangling.partition_point(|comment| comment.end() < parameters.start()));
if dangling_before_parameters.is_empty() {
write!(f, [space()])?;
} else {
write!(f, [dangling_comments(dangling_before_parameters)])?;
}
write!(
f,
[
space(),
parameters
.format()
.with_options(ParametersParentheses::Never),
]
[parameters
.format()
.with_options(ParametersParentheses::Never)]
)?;
}
write!(f, [token(":")])?;
write!(f, [token(":")])?;
if dangling.is_empty() {
write!(f, [space()])?;
if dangling_after_parameters.is_empty() {
write!(f, [space()])?;
} else {
write!(f, [dangling_comments(dangling_after_parameters)])?;
}
} else {
write!(f, [dangling_comments(dangling)])?;
}
write!(f, [token(":")])?;
// Insert hard line break if body has leading comment to ensure consistent formatting
if comments.has_leading(body.as_ref()) {
write!(f, [hard_line_break()])?;
// In this context, a dangling comment is a comment between the `lambda` and the body.
if dangling.is_empty() {
write!(f, [space()])?;
} else {
write!(f, [dangling_comments(dangling)])?;
}
}
write!(f, [body.format()])

View file

@ -17,7 +17,7 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
range: _,
} = item;
// This context, a dangling comment is an end-of-line comment on the same line as the `:=`.
// This context, a dangling comment is a comment between the `:=` and the value.
let comments = f.context().comments().clone();
let dangling = comments.dangling(item);