mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:09:22 +00:00
Parenthesize with statements (#5758)
<!-- 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 parentheses handling for with items to get closer to black's formatting. ### Case 1: ```python # Black / Input with ( [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccccc", dddddddddddddddddddddddddddddddd, ] as example1, aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccccccc + ddddddddddddddddd as example2, CtxManager2() as example2, CtxManager2() as example2, CtxManager2() as example2, ): ... # Before with ( [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccccc", dddddddddddddddddddddddddddddddd, ] as example1, ( aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccccccc + ddddddddddddddddd ) as example2, CtxManager2() as example2, CtxManager2() as example2, CtxManager2() as example2, ): ... ``` Notice how Ruff wraps the binary expression in an extra set of parentheses ### Case 2: Black does not expand the with-items if the with has no parentheses: ```python # Black / Input with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c: ... # Before with ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c ): ... ``` Or ```python # Black / Input with [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccccc", dddddddddddddddddddddddddddddddd, ] as example1, aaaaaaaaaaaaaaaaaaaaaaaaaa * bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * cccccccccccccccccccccccccccc + ddddddddddddddddd as example2, CtxManager222222222222222() as example2: ... # Before (Same as Case 1) with ( [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccccc", dddddddddddddddddddddddddddddddd, ] as example1, ( aaaaaaaaaaaaaaaaaaaaaaaaaa * bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * cccccccccccccccccccccccccccc + ddddddddddddddddd ) as example2, CtxManager222222222222222() as example2, ): ... ``` ## Test Plan I added new snapshot tests Improves the django similarity index from 0.973 to 0.977
This commit is contained in:
parent
e1c119fde3
commit
3cda89ecaf
13 changed files with 443 additions and 141 deletions
|
@ -1,11 +1,15 @@
|
|||
use ruff_formatter::{write, Buffer, FormatResult};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_text_size::TextRange;
|
||||
use rustpython_parser::ast::{Ranged, StmtAsyncWith, StmtWith, Suite, WithItem};
|
||||
|
||||
use crate::builders::parenthesize_if_expands;
|
||||
use ruff_formatter::{format_args, write, FormatError};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
|
||||
use crate::comments::trailing_comments;
|
||||
use crate::expression::parentheses::{
|
||||
in_parentheses_only_soft_line_break_or_space, optional_parentheses,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::trivia::{SimpleTokenizer, TokenKind};
|
||||
use crate::FormatNodeRule;
|
||||
|
||||
pub(super) enum AnyStatementWith<'a> {
|
||||
|
@ -68,22 +72,39 @@ impl Format<PyFormatContext<'_>> for AnyStatementWith<'_> {
|
|||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling_comments(self);
|
||||
|
||||
let joined_items = format_with(|f| {
|
||||
f.join_comma_separated(self.body().first().unwrap().start())
|
||||
.nodes(self.items().iter())
|
||||
.finish()
|
||||
});
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
self.is_async()
|
||||
.then_some(format_args![text("async"), space()]),
|
||||
text("with"),
|
||||
space()
|
||||
]
|
||||
)?;
|
||||
|
||||
if self.is_async() {
|
||||
write!(f, [text("async"), space()])?;
|
||||
if are_with_items_parenthesized(self, f.context())? {
|
||||
optional_parentheses(&format_with(|f| {
|
||||
let mut joiner = f.join_comma_separated(self.body().first().unwrap().start());
|
||||
|
||||
for item in self.items() {
|
||||
joiner.entry_with_line_separator(
|
||||
item,
|
||||
&item.format(),
|
||||
in_parentheses_only_soft_line_break_or_space(),
|
||||
);
|
||||
}
|
||||
joiner.finish()
|
||||
}))
|
||||
.fmt(f)?;
|
||||
} else {
|
||||
f.join_with(format_args![text(","), space()])
|
||||
.entries(self.items().iter().formatted())
|
||||
.finish()?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
text("with"),
|
||||
space(),
|
||||
group(&parenthesize_if_expands(&joined_items)),
|
||||
text(":"),
|
||||
trailing_comments(dangling_comments),
|
||||
block_indent(&self.body().format())
|
||||
|
@ -92,6 +113,34 @@ impl Format<PyFormatContext<'_>> for AnyStatementWith<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn are_with_items_parenthesized(
|
||||
with: &AnyStatementWith,
|
||||
context: &PyFormatContext,
|
||||
) -> FormatResult<bool> {
|
||||
let first_with_item = with.items().first().ok_or(FormatError::SyntaxError)?;
|
||||
let before_first_with_item = TextRange::new(with.start(), first_with_item.start());
|
||||
|
||||
let mut tokenizer = SimpleTokenizer::new(context.source(), before_first_with_item)
|
||||
.skip_trivia()
|
||||
.skip_while(|t| t.kind() == TokenKind::Async);
|
||||
|
||||
let with_keyword = tokenizer.next().ok_or(FormatError::SyntaxError)?;
|
||||
|
||||
debug_assert_eq!(
|
||||
with_keyword.kind(),
|
||||
TokenKind::With,
|
||||
"Expected with keyword but at {with_keyword:?}"
|
||||
);
|
||||
|
||||
match tokenizer.next() {
|
||||
Some(left_paren) => {
|
||||
debug_assert_eq!(left_paren.kind(), TokenKind::LParen);
|
||||
Ok(true)
|
||||
}
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatStmtWith;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue