Improve comprehension line break beheavior

<!--
Thank you for contributing to Ruff! To help us out with reviewing, please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

This PR improves the Black compatibility when it comes to breaking comprehensions. 

We want to avoid line breaks before the target and `in` whenever possible. Furthermore, `if X is not None` should be grouped together, similar to other binary like expressions

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

`cargo test`

<!-- How was it tested? -->
This commit is contained in:
Micha Reiser 2023-07-11 16:51:24 +02:00 committed by GitHub
parent 62a24e1028
commit 8b9193ab1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 121 additions and 316 deletions

View file

@ -33,36 +33,40 @@ impl FormatNodeRule<ExprCompare> for FormatExprCompare {
let comments = f.context().comments().clone();
write!(f, [in_parentheses_only_group(&left.format())])?;
let inner = format_with(|f| {
write!(f, [in_parentheses_only_group(&left.format())])?;
assert_eq!(comparators.len(), ops.len());
assert_eq!(comparators.len(), ops.len());
for (operator, comparator) in ops.iter().zip(comparators) {
let leading_comparator_comments = comments.leading_comments(comparator);
if leading_comparator_comments.is_empty() {
write!(f, [soft_line_break_or_space()])?;
} else {
// Format the expressions leading comments **before** the operator
write!(
f,
[
hard_line_break(),
leading_comments(leading_comparator_comments)
]
)?;
}
for (operator, comparator) in ops.iter().zip(comparators) {
let leading_comparator_comments = comments.leading_comments(comparator);
if leading_comparator_comments.is_empty() {
write!(f, [soft_line_break_or_space()])?;
} else {
// Format the expressions leading comments **before** the operator
write!(
f,
[
hard_line_break(),
leading_comments(leading_comparator_comments)
operator.format(),
space(),
in_parentheses_only_group(&comparator.format())
]
)?;
}
write!(
f,
[
operator.format(),
space(),
in_parentheses_only_group(&comparator.format())
]
)?;
}
Ok(())
});
Ok(())
in_parentheses_only_group(&inner).fmt(f)
}
}

View file

@ -3,13 +3,25 @@ use crate::prelude::*;
use crate::AsFormat;
use crate::{FormatNodeRule, PyFormatter};
use ruff_formatter::{format_args, write, Buffer, FormatResult};
use rustpython_parser::ast::{Comprehension, Ranged};
use rustpython_parser::ast::{Comprehension, Expr, Ranged};
#[derive(Default)]
pub struct FormatComprehension;
impl FormatNodeRule<Comprehension> for FormatComprehension {
fn fmt_fields(&self, item: &Comprehension, f: &mut PyFormatter) -> FormatResult<()> {
struct Spacer<'a>(&'a Expr);
impl Format<PyFormatContext<'_>> for Spacer<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
if f.context().comments().has_leading_comments(self.0) {
soft_line_break_or_space().fmt(f)
} else {
space().fmt(f)
}
}
}
let Comprehension {
range: _,
target,
@ -18,33 +30,40 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
is_async,
} = item;
let comments = f.context().comments().clone();
if *is_async {
write!(f, [text("async"), space()])?;
}
let comments = f.context().comments().clone();
let dangling_item_comments = comments.dangling_comments(item);
let (before_target_comments, before_in_comments) = dangling_item_comments.split_at(
dangling_item_comments
.partition_point(|comment| comment.slice().end() < target.range().start()),
);
let trailing_in_comments = comments.dangling_comments(iter);
let in_spacer = format_with(|f| {
if before_in_comments.is_empty() {
space().fmt(f)
} else {
soft_line_break_or_space().fmt(f)
}
});
write!(
f,
[
text("for"),
trailing_comments(before_target_comments),
group(&format_args!(
soft_line_break_or_space(),
Spacer(target),
target.format(),
soft_line_break_or_space(),
in_spacer,
leading_comments(before_in_comments),
text("in"),
trailing_comments(trailing_in_comments),
soft_line_break_or_space(),
Spacer(iter),
iter.format(),
)),
]
@ -64,7 +83,7 @@ impl FormatNodeRule<Comprehension> for FormatComprehension {
leading_comments(own_line_if_comments),
text("if"),
trailing_comments(end_of_line_if_comments),
soft_line_break_or_space(),
Spacer(if_case),
if_case.format(),
)));
}