mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 05:25:17 +00:00
Format binary expressions (#4862)
* Format Binary Expressions * Extract NeedsParentheses trait
This commit is contained in:
parent
775326790e
commit
3f032cf09d
47 changed files with 1122 additions and 217 deletions
43
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary_expression.py
vendored
Normal file
43
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary_expression.py
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(aaaaaaaa
|
||||||
|
+ # trailing operator comment
|
||||||
|
b # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
aaaaaaaaaaaaaa + caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
aaaaaaaaaaaaaa + [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee]
|
||||||
|
aaaaaaaaaaaaaa + (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
aaaaaaaaaaaaaa + { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
aaaaaaaaaaaaaa + (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eee
|
||||||
|
] # comment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
[a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c ]
|
||||||
|
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content + b
|
||||||
|
)
|
|
@ -12,7 +12,7 @@ pub(crate) trait PyFormatterExtensions<'ast, 'buf> {
|
||||||
///
|
///
|
||||||
/// * [`NodeLevel::Module`]: Up to two empty lines
|
/// * [`NodeLevel::Module`]: Up to two empty lines
|
||||||
/// * [`NodeLevel::CompoundStatement`]: Up to one empty line
|
/// * [`NodeLevel::CompoundStatement`]: Up to one empty line
|
||||||
/// * [`NodeLevel::Parenthesized`]: No empty lines
|
/// * [`NodeLevel::Expression`]: No empty lines
|
||||||
fn join_nodes<'fmt>(&'fmt mut self, level: NodeLevel) -> JoinNodesBuilder<'fmt, 'ast, 'buf>;
|
fn join_nodes<'fmt>(&'fmt mut self, level: NodeLevel) -> JoinNodesBuilder<'fmt, 'ast, 'buf>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,18 +48,18 @@ impl<'fmt, 'ast, 'buf> JoinNodesBuilder<'fmt, 'ast, 'buf> {
|
||||||
{
|
{
|
||||||
let node_level = self.node_level;
|
let node_level = self.node_level;
|
||||||
let separator = format_with(|f: &mut PyFormatter| match node_level {
|
let separator = format_with(|f: &mut PyFormatter| match node_level {
|
||||||
NodeLevel::TopLevel => match lines_before(f.context().contents(), node.start()) {
|
NodeLevel::TopLevel => match lines_before(node.start(), f.context().contents()) {
|
||||||
0 | 1 => hard_line_break().fmt(f),
|
0 | 1 => hard_line_break().fmt(f),
|
||||||
2 => empty_line().fmt(f),
|
2 => empty_line().fmt(f),
|
||||||
_ => write!(f, [empty_line(), empty_line()]),
|
_ => write!(f, [empty_line(), empty_line()]),
|
||||||
},
|
},
|
||||||
NodeLevel::CompoundStatement => {
|
NodeLevel::CompoundStatement => {
|
||||||
match lines_before(f.context().contents(), node.start()) {
|
match lines_before(node.start(), f.context().contents()) {
|
||||||
0 | 1 => hard_line_break().fmt(f),
|
0 | 1 => hard_line_break().fmt(f),
|
||||||
_ => empty_line().fmt(f),
|
_ => empty_line().fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeLevel::Parenthesized => hard_line_break().fmt(f),
|
NodeLevel::Expression => hard_line_break().fmt(f),
|
||||||
});
|
});
|
||||||
|
|
||||||
self.entry_with_separator(&separator, content);
|
self.entry_with_separator(&separator, content);
|
||||||
|
@ -200,7 +200,7 @@ no_leading_newline = 30"#
|
||||||
// Removes all empty lines
|
// Removes all empty lines
|
||||||
#[test]
|
#[test]
|
||||||
fn ranged_builder_parenthesized_level() {
|
fn ranged_builder_parenthesized_level() {
|
||||||
let printed = format_ranged(NodeLevel::Parenthesized);
|
let printed = format_ranged(NodeLevel::Expression);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&printed,
|
&printed,
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingComments<'_> {
|
||||||
for comment in leading_comments {
|
for comment in leading_comments {
|
||||||
let slice = comment.slice();
|
let slice = comment.slice();
|
||||||
|
|
||||||
let lines_after_comment = lines_after(f.context().contents(), slice.end());
|
let lines_after_comment = lines_after(slice.end(), f.context().contents());
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[format_comment(comment), empty_lines(lines_after_comment)]
|
[format_comment(comment), empty_lines(lines_after_comment)]
|
||||||
|
@ -80,7 +80,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {
|
||||||
if let Some(first_leading) = self.comments.first() {
|
if let Some(first_leading) = self.comments.first() {
|
||||||
// Leading comments only preserves the lines after the comment but not before.
|
// Leading comments only preserves the lines after the comment but not before.
|
||||||
// Insert the necessary lines.
|
// Insert the necessary lines.
|
||||||
if lines_before(f.context().contents(), first_leading.slice().start()) > 1 {
|
if lines_before(first_leading.slice().start(), f.context().contents()) > 1 {
|
||||||
write!(f, [empty_line()])?;
|
write!(f, [empty_line()])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {
|
||||||
} else if let Some(last_preceding) = self.last_node {
|
} else if let Some(last_preceding) = self.last_node {
|
||||||
// The leading comments formatting ensures that it preserves the right amount of lines after
|
// The leading comments formatting ensures that it preserves the right amount of lines after
|
||||||
// We need to take care of this ourselves, if there's no leading `else` comment.
|
// We need to take care of this ourselves, if there's no leading `else` comment.
|
||||||
if lines_after(f.context().contents(), last_preceding.end()) > 1 {
|
if lines_after(last_preceding.end(), f.context().contents()) > 1 {
|
||||||
write!(f, [empty_line()])?;
|
write!(f, [empty_line()])?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
||||||
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
||||||
|
|
||||||
if has_trailing_own_line_comment {
|
if has_trailing_own_line_comment {
|
||||||
let lines_before_comment = lines_before(f.context().contents(), slice.start());
|
let lines_before_comment = lines_before(slice.start(), f.context().contents());
|
||||||
|
|
||||||
// A trailing comment at the end of a body or list
|
// A trailing comment at the end of a body or list
|
||||||
// ```python
|
// ```python
|
||||||
|
@ -175,20 +175,26 @@ pub(crate) fn dangling_node_comments<T>(node: &T) -> FormatDanglingComments
|
||||||
where
|
where
|
||||||
T: AstNode,
|
T: AstNode,
|
||||||
{
|
{
|
||||||
FormatDanglingComments {
|
FormatDanglingComments::Node(node.as_any_node_ref())
|
||||||
node: node.as_any_node_ref(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FormatDanglingComments<'a> {
|
pub(crate) fn dangling_comments(comments: &[SourceComment]) -> FormatDanglingComments {
|
||||||
node: AnyNodeRef<'a>,
|
FormatDanglingComments::Comments(comments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum FormatDanglingComments<'a> {
|
||||||
|
Node(AnyNodeRef<'a>),
|
||||||
|
Comments(&'a [SourceComment]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
|
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
|
|
||||||
let dangling_comments = comments.dangling_comments(self.node);
|
let dangling_comments = match self {
|
||||||
|
Self::Comments(comments) => comments,
|
||||||
|
Self::Node(node) => comments.dangling_comments(*node),
|
||||||
|
};
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for comment in dangling_comments {
|
for comment in dangling_comments {
|
||||||
|
@ -200,7 +206,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
format_comment(comment),
|
format_comment(comment),
|
||||||
empty_lines(lines_after(f.context().contents(), comment.slice().end()))
|
empty_lines(lines_after(comment.slice().end(), f.context().contents()))
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -301,7 +307,7 @@ impl Format<PyFormatContext<'_>> for FormatEmptyLines {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove all whitespace in parenthesized expressions
|
// Remove all whitespace in parenthesized expressions
|
||||||
NodeLevel::Parenthesized => write!(f, [hard_line_break()]),
|
NodeLevel::Expression => write!(f, [hard_line_break()]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,8 +103,8 @@ use crate::comments::map::MultiMap;
|
||||||
use crate::comments::node_key::NodeRefEqualityKey;
|
use crate::comments::node_key::NodeRefEqualityKey;
|
||||||
use crate::comments::visitor::CommentsVisitor;
|
use crate::comments::visitor::CommentsVisitor;
|
||||||
pub(crate) use format::{
|
pub(crate) use format::{
|
||||||
dangling_node_comments, leading_alternate_branch_comments, leading_node_comments,
|
dangling_comments, dangling_node_comments, leading_alternate_branch_comments,
|
||||||
trailing_comments, trailing_node_comments,
|
leading_node_comments, trailing_comments, trailing_node_comments,
|
||||||
};
|
};
|
||||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
|
|
@ -520,8 +520,8 @@ fn handle_trailing_end_of_line_condition_comment<'a>(
|
||||||
if preceding.ptr_eq(last_before_colon) {
|
if preceding.ptr_eq(last_before_colon) {
|
||||||
let mut start = preceding.end();
|
let mut start = preceding.end();
|
||||||
while let Some((offset, c)) = find_first_non_trivia_character_in_range(
|
while let Some((offset, c)) = find_first_non_trivia_character_in_range(
|
||||||
locator.contents(),
|
|
||||||
TextRange::new(start, following.start()),
|
TextRange::new(start, following.start()),
|
||||||
|
locator.contents(),
|
||||||
) {
|
) {
|
||||||
match c {
|
match c {
|
||||||
':' => {
|
':' => {
|
||||||
|
@ -655,7 +655,7 @@ fn handle_trailing_binary_expression_left_or_operator_comment<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let operator_offset = loop {
|
let operator_offset = loop {
|
||||||
match find_first_non_trivia_character_in_range(locator.contents(), between_operands_range) {
|
match find_first_non_trivia_character_in_range(between_operands_range, locator.contents()) {
|
||||||
// Skip over closing parens
|
// Skip over closing parens
|
||||||
Some((offset, ')')) => {
|
Some((offset, ')')) => {
|
||||||
between_operands_range =
|
between_operands_range =
|
||||||
|
@ -733,17 +733,17 @@ fn find_pos_only_slash_offset(
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
) -> Option<TextSize> {
|
) -> Option<TextSize> {
|
||||||
// First find the comma separating the two arguments
|
// First find the comma separating the two arguments
|
||||||
find_first_non_trivia_character_in_range(locator.contents(), between_arguments_range).and_then(
|
find_first_non_trivia_character_in_range(between_arguments_range, locator.contents()).and_then(
|
||||||
|(comma_offset, comma)| {
|
|(comma_offset, comma)| {
|
||||||
debug_assert_eq!(comma, ',');
|
debug_assert_eq!(comma, ',');
|
||||||
|
|
||||||
// Then find the position of the `/` operator
|
// Then find the position of the `/` operator
|
||||||
find_first_non_trivia_character_in_range(
|
find_first_non_trivia_character_in_range(
|
||||||
locator.contents(),
|
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
comma_offset + TextSize::new(1),
|
comma_offset + TextSize::new(1),
|
||||||
between_arguments_range.end(),
|
between_arguments_range.end(),
|
||||||
),
|
),
|
||||||
|
locator.contents(),
|
||||||
)
|
)
|
||||||
.map(|(offset, c)| {
|
.map(|(offset, c)| {
|
||||||
debug_assert_eq!(c, '/');
|
debug_assert_eq!(c, '/');
|
||||||
|
|
|
@ -82,5 +82,5 @@ pub(crate) enum NodeLevel {
|
||||||
CompoundStatement,
|
CompoundStatement,
|
||||||
|
|
||||||
/// Formatting nodes that are enclosed in a parenthesized expression.
|
/// Formatting nodes that are enclosed in a parenthesized expression.
|
||||||
Parenthesized,
|
Expression,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprAttribute;
|
use rustpython_parser::ast::ExprAttribute;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprAttribute {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprAwait;
|
use rustpython_parser::ast::ExprAwait;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAwait> for FormatExprAwait {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprAwait {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,215 @@
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::comments::trailing_comments;
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use crate::expression::parentheses::{
|
||||||
use rustpython_parser::ast::ExprBinOp;
|
default_expression_needs_parentheses, NeedsParentheses, Parenthesize,
|
||||||
|
};
|
||||||
|
use crate::expression::Parentheses;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::FormatNodeRule;
|
||||||
|
use ruff_formatter::{
|
||||||
|
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions,
|
||||||
|
};
|
||||||
|
use ruff_python_ast::node::AstNode;
|
||||||
|
use rustpython_parser::ast::{
|
||||||
|
Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, Unaryop,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExprBinOp;
|
pub struct FormatExprBinOp {
|
||||||
|
parentheses: Option<Parentheses>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRuleWithOptions<ExprBinOp, PyFormatContext<'_>> for FormatExprBinOp {
|
||||||
|
type Options = Option<Parentheses>;
|
||||||
|
|
||||||
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
|
self.parentheses = options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
||||||
fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
write!(f, [verbatim_text(item.range)])
|
let ExprBinOp {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
op,
|
||||||
|
range: _,
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
let should_break_right = self.parentheses == Some(Parentheses::Custom);
|
||||||
|
|
||||||
|
if should_break_right {
|
||||||
|
let left = left.format().memoized();
|
||||||
|
let right = right.format().memoized();
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[best_fitting![
|
||||||
|
// The whole expression on a single line
|
||||||
|
format_args![left, space(), op.format(), space(), right],
|
||||||
|
// Break the right, but keep the left flat
|
||||||
|
format_args![
|
||||||
|
left,
|
||||||
|
space(),
|
||||||
|
op.format(),
|
||||||
|
space(),
|
||||||
|
group(&right).should_expand(true),
|
||||||
|
],
|
||||||
|
// Break after the operator, try to keep the right flat, otherwise expand it
|
||||||
|
format_args![
|
||||||
|
text("("),
|
||||||
|
block_indent(&format_args![
|
||||||
|
left,
|
||||||
|
hard_line_break(),
|
||||||
|
op.format(),
|
||||||
|
space(),
|
||||||
|
group(&right),
|
||||||
|
]),
|
||||||
|
text(")")
|
||||||
|
],
|
||||||
|
]]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let operator_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||||
|
let needs_space = !is_simple_power_expression(item);
|
||||||
|
|
||||||
|
let before_operator_space = if needs_space {
|
||||||
|
soft_line_break_or_space()
|
||||||
|
} else {
|
||||||
|
soft_line_break()
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
left.format(),
|
||||||
|
before_operator_space,
|
||||||
|
op.format(),
|
||||||
|
trailing_comments(operator_comments),
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Format the operator on its own line if the operator has trailing comments and the right side has leading comments.
|
||||||
|
if !operator_comments.is_empty() && comments.has_leading_comments(right.as_ref().into())
|
||||||
|
{
|
||||||
|
write!(f, [hard_line_break()])?;
|
||||||
|
} else if needs_space {
|
||||||
|
write!(f, [space()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, [group(&right.format())])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dangling_comments(&self, _node: &ExprBinOp, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// Handled inside of `fmt_fields`
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn is_simple_power_expression(expr: &ExprBinOp) -> bool {
|
||||||
|
expr.op.is_pow() && is_simple_power_operand(&expr.left) && is_simple_power_operand(&expr.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if an [`Expr`] adheres to [Black's definition](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-breaks-binary-operators)
|
||||||
|
/// of a non-complex expression, in the context of a power operation.
|
||||||
|
const fn is_simple_power_operand(expr: &Expr) -> bool {
|
||||||
|
match expr {
|
||||||
|
Expr::UnaryOp(ExprUnaryOp {
|
||||||
|
op: Unaryop::Not, ..
|
||||||
|
}) => false,
|
||||||
|
Expr::Constant(ExprConstant {
|
||||||
|
value: Constant::Complex { .. } | Constant::Float(_) | Constant::Int(_),
|
||||||
|
..
|
||||||
|
}) => true,
|
||||||
|
Expr::Name(_) => true,
|
||||||
|
Expr::UnaryOp(ExprUnaryOp { operand, .. }) => is_simple_power_operand(operand),
|
||||||
|
Expr::Attribute(ExprAttribute { value, .. }) => is_simple_power_operand(value),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct FormatOperator;
|
||||||
|
|
||||||
|
impl<'ast> AsFormat<PyFormatContext<'ast>> for Operator {
|
||||||
|
type Format<'a> = FormatRefWithRule<'a, Operator, FormatOperator, PyFormatContext<'ast>>;
|
||||||
|
|
||||||
|
fn format(&self) -> Self::Format<'_> {
|
||||||
|
FormatRefWithRule::new(self, FormatOperator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> IntoFormat<PyFormatContext<'ast>> for Operator {
|
||||||
|
type Format = FormatOwnedWithRule<Operator, FormatOperator, PyFormatContext<'ast>>;
|
||||||
|
fn into_format(self) -> Self::Format {
|
||||||
|
FormatOwnedWithRule::new(self, FormatOperator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRule<Operator, PyFormatContext<'_>> for FormatOperator {
|
||||||
|
fn fmt(&self, item: &Operator, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
|
let operator = match item {
|
||||||
|
Operator::Add => "+",
|
||||||
|
Operator::Sub => "-",
|
||||||
|
Operator::Mult => "*",
|
||||||
|
Operator::MatMult => "@",
|
||||||
|
Operator::Div => "/",
|
||||||
|
Operator::Mod => "%",
|
||||||
|
Operator::Pow => "**",
|
||||||
|
Operator::LShift => "<<",
|
||||||
|
Operator::RShift => ">>",
|
||||||
|
Operator::BitOr => "|",
|
||||||
|
Operator::BitXor => "^",
|
||||||
|
Operator::BitAnd => "&",
|
||||||
|
Operator::FloorDiv => "//",
|
||||||
|
};
|
||||||
|
|
||||||
|
text(operator).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprBinOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => {
|
||||||
|
if should_binary_break_right_side_first(self) {
|
||||||
|
Parentheses::Custom
|
||||||
|
} else {
|
||||||
|
Parentheses::Optional
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn should_binary_break_right_side_first(expr: &ExprBinOp) -> bool {
|
||||||
|
use ruff_python_ast::prelude::*;
|
||||||
|
|
||||||
|
if expr.left.is_bin_op_expr() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match expr.right.as_ref() {
|
||||||
|
Expr::Tuple(ExprTuple {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::List(ExprList {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::Set(ExprSet {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::Dict(ExprDict {
|
||||||
|
values: expressions,
|
||||||
|
..
|
||||||
|
}) => !expressions.is_empty(),
|
||||||
|
Expr::Call(ExprCall { args, keywords, .. }) => !args.is_empty() && !keywords.is_empty(),
|
||||||
|
Expr::ListComp(_) | Expr::SetComp(_) | Expr::DictComp(_) | Expr::GeneratorExp(_) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprBoolOp;
|
use rustpython_parser::ast::ExprBoolOp;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprBoolOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprCall;
|
use rustpython_parser::ast::ExprCall;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprCall {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprCompare;
|
use rustpython_parser::ast::ExprCompare;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprCompare> for FormatExprCompare {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprCompare {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprConstant;
|
use rustpython_parser::ast::ExprConstant;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprConstant> for FormatExprConstant {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprConstant {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprDict;
|
use rustpython_parser::ast::ExprDict;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDict> for FormatExprDict {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprDict {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprDictComp;
|
use rustpython_parser::ast::ExprDictComp;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDictComp> for FormatExprDictComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprDictComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprFormattedValue;
|
use rustpython_parser::ast::ExprFormattedValue;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprFormattedValue> for FormatExprFormattedValue {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprFormattedValue {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprGeneratorExp;
|
use rustpython_parser::ast::ExprGeneratorExp;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprGeneratorExp> for FormatExprGeneratorExp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprGeneratorExp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprIfExp;
|
use rustpython_parser::ast::ExprIfExp;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprIfExp> for FormatExprIfExp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprIfExp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprJoinedStr;
|
use rustpython_parser::ast::ExprJoinedStr;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprJoinedStr> for FormatExprJoinedStr {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprJoinedStr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprLambda;
|
use rustpython_parser::ast::ExprLambda;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprLambda {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::comments::dangling_comments;
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::FormatNodeRule;
|
||||||
|
use ruff_formatter::{format_args, write};
|
||||||
use rustpython_parser::ast::ExprList;
|
use rustpython_parser::ast::ExprList;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -7,6 +12,55 @@ pub struct FormatExprList;
|
||||||
|
|
||||||
impl FormatNodeRule<ExprList> for FormatExprList {
|
impl FormatNodeRule<ExprList> for FormatExprList {
|
||||||
fn fmt_fields(&self, item: &ExprList, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprList, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
write!(f, [verbatim_text(item.range)])
|
let ExprList {
|
||||||
|
range: _,
|
||||||
|
elts,
|
||||||
|
ctx: _,
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
let items = format_with(|f| {
|
||||||
|
let mut iter = elts.iter();
|
||||||
|
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
write!(f, [first.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in iter {
|
||||||
|
write!(f, [text(","), soft_line_break_or_space(), item.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !elts.is_empty() {
|
||||||
|
write!(f, [if_group_breaks(&text(","))])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let dangling = comments.dangling_comments(item.into());
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
text("["),
|
||||||
|
dangling_comments(dangling),
|
||||||
|
soft_block_indent(&items),
|
||||||
|
text("]")
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dangling_comments(&self, _node: &ExprList, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// Handled as part of `fmt_fields`
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprList {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprListComp;
|
use rustpython_parser::ast::ExprListComp;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprListComp> for FormatExprListComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprListComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use ruff_formatter::{write, FormatContext};
|
use ruff_formatter::{write, FormatContext};
|
||||||
|
@ -22,6 +25,12 @@ impl FormatNodeRule<ExprName> for FormatExprName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprName {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprNamedExpr;
|
use rustpython_parser::ast::ExprNamedExpr;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprNamedExpr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSet;
|
use rustpython_parser::ast::ExprSet;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSet> for FormatExprSet {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSet {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSetComp;
|
use rustpython_parser::ast::ExprSetComp;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSetComp> for FormatExprSetComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSetComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSlice;
|
use rustpython_parser::ast::ExprSlice;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSlice> for FormatExprSlice {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSlice {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprStarred;
|
use rustpython_parser::ast::ExprStarred;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprStarred> for FormatExprStarred {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprStarred {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSubscript;
|
use rustpython_parser::ast::ExprSubscript;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSubscript {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprTuple;
|
use rustpython_parser::ast::ExprTuple;
|
||||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprTuple {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprUnaryOp;
|
use rustpython_parser::ast::ExprUnaryOp;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprUnaryOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprYield;
|
use rustpython_parser::ast::ExprYield;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYield> for FormatExprYield {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprYield {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprYieldFrom;
|
use rustpython_parser::ast::ExprYieldFrom;
|
||||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYieldFrom> for FormatExprYieldFrom {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprYieldFrom {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
use crate::context::NodeLevel;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use ruff_formatter::{format_args, write};
|
|
||||||
use rustpython_parser::ast::Expr;
|
|
||||||
|
|
||||||
/// Formats the passed expression. Adds parentheses if the expression doesn't fit on a line.
|
|
||||||
pub(crate) const fn maybe_parenthesize(expression: &Expr) -> MaybeParenthesize {
|
|
||||||
MaybeParenthesize { expression }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct MaybeParenthesize<'a> {
|
|
||||||
expression: &'a Expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for MaybeParenthesize<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
|
||||||
let saved_level = f.context().node_level();
|
|
||||||
f.context_mut().set_node_level(NodeLevel::Parenthesized);
|
|
||||||
|
|
||||||
let result = if needs_parentheses(self.expression) {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[group(&format_args![
|
|
||||||
if_group_breaks(&text("(")),
|
|
||||||
soft_block_indent(&self.expression.format()),
|
|
||||||
if_group_breaks(&text(")"))
|
|
||||||
])]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Don't add parentheses around expressions that have parentheses on their own (e.g. list, dict, tuple, call expression)
|
|
||||||
self.expression.format().fmt(f)
|
|
||||||
};
|
|
||||||
|
|
||||||
f.context_mut().set_node_level(saved_level);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn needs_parentheses(expr: &Expr) -> bool {
|
|
||||||
!matches!(
|
|
||||||
expr,
|
|
||||||
Expr::Tuple(_)
|
|
||||||
| Expr::List(_)
|
|
||||||
| Expr::Set(_)
|
|
||||||
| Expr::Dict(_)
|
|
||||||
| Expr::ListComp(_)
|
|
||||||
| Expr::SetComp(_)
|
|
||||||
| Expr::DictComp(_)
|
|
||||||
| Expr::GeneratorExp(_)
|
|
||||||
| Expr::Call(_)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
use crate::context::NodeLevel;
|
||||||
|
use crate::expression::parentheses::{NeedsParentheses, Parentheses, Parenthesize};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule};
|
use ruff_formatter::{
|
||||||
|
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
||||||
|
};
|
||||||
use rustpython_parser::ast::Expr;
|
use rustpython_parser::ast::Expr;
|
||||||
|
|
||||||
pub(crate) mod expr_attribute;
|
pub(crate) mod expr_attribute;
|
||||||
|
@ -29,17 +33,30 @@ pub(crate) mod expr_tuple;
|
||||||
pub(crate) mod expr_unary_op;
|
pub(crate) mod expr_unary_op;
|
||||||
pub(crate) mod expr_yield;
|
pub(crate) mod expr_yield;
|
||||||
pub(crate) mod expr_yield_from;
|
pub(crate) mod expr_yield_from;
|
||||||
pub(crate) mod maybe_parenthesize;
|
pub(crate) mod parentheses;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExpr;
|
pub struct FormatExpr {
|
||||||
|
parenthesize: Parenthesize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRuleWithOptions<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
|
type Options = Parenthesize;
|
||||||
|
|
||||||
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
|
self.parenthesize = options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
fn fmt(&self, item: &Expr, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt(&self, item: &Expr, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
match item {
|
let parentheses = item.needs_parentheses(self.parenthesize, f.context().contents());
|
||||||
|
|
||||||
|
let format_expr = format_with(|f| match item {
|
||||||
Expr::BoolOp(expr) => expr.format().fmt(f),
|
Expr::BoolOp(expr) => expr.format().fmt(f),
|
||||||
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
||||||
Expr::BinOp(expr) => expr.format().fmt(f),
|
Expr::BinOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f),
|
||||||
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
||||||
Expr::Lambda(expr) => expr.format().fmt(f),
|
Expr::Lambda(expr) => expr.format().fmt(f),
|
||||||
Expr::IfExp(expr) => expr.format().fmt(f),
|
Expr::IfExp(expr) => expr.format().fmt(f),
|
||||||
|
@ -64,6 +81,72 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
Expr::List(expr) => expr.format().fmt(f),
|
Expr::List(expr) => expr.format().fmt(f),
|
||||||
Expr::Tuple(expr) => expr.format().fmt(f),
|
Expr::Tuple(expr) => expr.format().fmt(f),
|
||||||
Expr::Slice(expr) => expr.format().fmt(f),
|
Expr::Slice(expr) => expr.format().fmt(f),
|
||||||
|
});
|
||||||
|
|
||||||
|
let saved_level = f.context().node_level();
|
||||||
|
f.context_mut().set_node_level(NodeLevel::Expression);
|
||||||
|
|
||||||
|
let result = match parentheses {
|
||||||
|
Parentheses::Always => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
text("("),
|
||||||
|
soft_block_indent(&format_expr),
|
||||||
|
text(")")
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Add optional parentheses. Ignore if the item renders parentheses itself.
|
||||||
|
Parentheses::Optional => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
if_group_breaks(&text("(")),
|
||||||
|
soft_block_indent(&format_expr),
|
||||||
|
if_group_breaks(&text(")"))
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Parentheses::Custom | Parentheses::Never => Format::fmt(&format_expr, f),
|
||||||
|
};
|
||||||
|
|
||||||
|
f.context_mut().set_node_level(saved_level);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for Expr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match self {
|
||||||
|
Expr::BoolOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::NamedExpr(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::BinOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::UnaryOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Lambda(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::IfExp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Dict(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Set(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::ListComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::SetComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::DictComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::GeneratorExp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Await(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Yield(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::YieldFrom(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Compare(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Call(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::FormattedValue(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::JoinedStr(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Constant(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Attribute(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Subscript(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Starred(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Name(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::List(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Tuple(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Slice(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
93
crates/ruff_python_formatter/src/expression/parentheses.rs
Normal file
93
crates/ruff_python_formatter/src/expression/parentheses.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use crate::trivia::{
|
||||||
|
find_first_non_trivia_character_after, find_first_non_trivia_character_before,
|
||||||
|
};
|
||||||
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
|
||||||
|
pub(crate) trait NeedsParentheses {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn default_expression_needs_parentheses(
|
||||||
|
node: AnyNodeRef,
|
||||||
|
parenthesize: Parenthesize,
|
||||||
|
source: &str,
|
||||||
|
) -> Parentheses {
|
||||||
|
debug_assert!(
|
||||||
|
node.is_expression(),
|
||||||
|
"Should only be called for expressions"
|
||||||
|
);
|
||||||
|
|
||||||
|
// `Optional` or `Preserve` and expression has parentheses in source code.
|
||||||
|
if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
|
||||||
|
Parentheses::Always
|
||||||
|
}
|
||||||
|
// `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line
|
||||||
|
else if !parenthesize.is_preserve() {
|
||||||
|
Parentheses::Optional
|
||||||
|
} else {
|
||||||
|
//`Preserve` and expression has no parentheses in the source code
|
||||||
|
Parentheses::Never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures if the expression should be parenthesized.
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub enum Parenthesize {
|
||||||
|
/// Parenthesize the expression if it has parenthesis in the source.
|
||||||
|
#[default]
|
||||||
|
Preserve,
|
||||||
|
|
||||||
|
/// Parenthesizes the expression if it doesn't fit on a line OR if the expression is parenthesized in the source code.
|
||||||
|
Optional,
|
||||||
|
|
||||||
|
/// Parenthesizes the expression only if it doesn't fit on a line.
|
||||||
|
IfBreaks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parenthesize {
|
||||||
|
const fn is_if_breaks(self) -> bool {
|
||||||
|
matches!(self, Parenthesize::IfBreaks)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn is_preserve(self) -> bool {
|
||||||
|
matches!(self, Parenthesize::Preserve)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether it is necessary to add parentheses around an expression.
|
||||||
|
/// This is different from [`Parenthesize`] in that it is the resolved representation: It takes into account
|
||||||
|
/// whether there are parentheses in the source code or not.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Parentheses {
|
||||||
|
/// Always create parentheses
|
||||||
|
Always,
|
||||||
|
|
||||||
|
/// Only add parentheses when necessary because the expression breaks over multiple lines.
|
||||||
|
Optional,
|
||||||
|
|
||||||
|
/// Custom handling by the node's formatter implementation
|
||||||
|
Custom,
|
||||||
|
|
||||||
|
/// Never add parentheses
|
||||||
|
Never,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_expression_parenthesized(expr: AnyNodeRef, contents: &str) -> bool {
|
||||||
|
use rustpython_parser::ast::Ranged;
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
expr.is_expression(),
|
||||||
|
"Should only be called for expressions"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Search backwards to avoid ambiguity with `(a, )` and because it's faster
|
||||||
|
matches!(
|
||||||
|
find_first_non_trivia_character_after(expr.end(), contents),
|
||||||
|
Some((_, ')'))
|
||||||
|
)
|
||||||
|
// Search forwards to confirm that this is not a nested expression `(5 + d * 3)`
|
||||||
|
&& matches!(
|
||||||
|
find_first_non_trivia_character_before(expr.start(), contents),
|
||||||
|
Some((_, '('))
|
||||||
|
)
|
||||||
|
}
|
|
@ -51,7 +51,7 @@ where
|
||||||
self.fmt_node(node, f)?;
|
self.fmt_node(node, f)?;
|
||||||
self.fmt_dangling_comments(node, f)?;
|
self.fmt_dangling_comments(node, f)?;
|
||||||
self.fmt_trailing_comments(node, f)?;
|
self.fmt_trailing_comments(node, f)?;
|
||||||
write!(f, [source_position(node.start())])
|
write!(f, [source_position(node.end())])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the node without comments. Ignores any suppression comments.
|
/// Formats the node without comments. Ignores any suppression comments.
|
||||||
|
@ -225,8 +225,11 @@ if True:
|
||||||
|
|
||||||
let formatted_code = printed.as_code();
|
let formatted_code = printed.as_code();
|
||||||
|
|
||||||
let reformatted =
|
let reformatted = format_module(formatted_code).unwrap_or_else(|err| {
|
||||||
format_module(formatted_code).expect("Expected formatted code to be valid syntax");
|
panic!(
|
||||||
|
"Formatted code resulted introduced a syntax error {err:#?}. Code:\n{formatted_code}"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
if reformatted.as_code() != formatted_code {
|
if reformatted.as_code() != formatted_code {
|
||||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||||
|
@ -314,7 +317,7 @@ Formatted twice:
|
||||||
let formatted_code = printed.as_code();
|
let formatted_code = printed.as_code();
|
||||||
|
|
||||||
let reformatted =
|
let reformatted =
|
||||||
format_module(formatted_code).expect("Expected formatted code to be valid syntax");
|
format_module(formatted_code).unwrap_or_else(|err| panic!("Expected formatted code to be valid syntax but it contains syntax errors: {err}\n{formatted_code}"));
|
||||||
|
|
||||||
if reformatted.as_code() != formatted_code {
|
if reformatted.as_code() != formatted_code {
|
||||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||||
|
@ -378,7 +381,9 @@ other
|
||||||
// Uncomment the `dbg` to print the IR.
|
// Uncomment the `dbg` to print the IR.
|
||||||
// Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR
|
// Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR
|
||||||
// inside of a `Format` implementation
|
// inside of a `Format` implementation
|
||||||
// dbg!(formatted.document());
|
// dbg!(formatted
|
||||||
|
// .document()
|
||||||
|
// .display(formatted.context().source_code()));
|
||||||
|
|
||||||
let printed = formatted.print().unwrap();
|
let printed = formatted.print().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ if True:
|
||||||
- 2,
|
- 2,
|
||||||
- 3,
|
- 3,
|
||||||
-]
|
-]
|
||||||
+[1, 2, 3,]
|
+[1, 2, 3]
|
||||||
|
|
||||||
-division_result_tuple = (6 / 2,)
|
-division_result_tuple = (6 / 2,)
|
||||||
+division_result_tuple = (6/2,)
|
+division_result_tuple = (6/2,)
|
||||||
|
@ -250,7 +250,7 @@ for x in (1,):
|
||||||
for (x,) in (1,), (2,), (3,):
|
for (x,) in (1,), (2,), (3,):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
[1, 2, 3,]
|
[1, 2, 3]
|
||||||
|
|
||||||
division_result_tuple = (6/2,)
|
division_result_tuple = (6/2,)
|
||||||
print("foo %r", (foo.bar,))
|
print("foo %r", (foo.bar,))
|
||||||
|
|
|
@ -109,7 +109,21 @@ async def wat():
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -19,14 +19,9 @@
|
@@ -4,10 +4,12 @@
|
||||||
|
#
|
||||||
|
# Has many lines. Many, many lines.
|
||||||
|
# Many, many, many lines.
|
||||||
|
-"""Module docstring.
|
||||||
|
+(
|
||||||
|
+ """Module docstring.
|
||||||
|
|
||||||
|
Possibly also many, many lines.
|
||||||
|
"""
|
||||||
|
+)
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
@@ -19,9 +21,6 @@
|
||||||
import fast
|
import fast
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import slow as fast
|
import slow as fast
|
||||||
|
@ -117,16 +131,9 @@ async def wat():
|
||||||
-
|
-
|
||||||
-# Some comment before a function.
|
-# Some comment before a function.
|
||||||
y = 1
|
y = 1
|
||||||
-(
|
(
|
||||||
- # some strings
|
# some strings
|
||||||
- y # type: ignore
|
@@ -93,4 +92,4 @@
|
||||||
-)
|
|
||||||
+# some strings
|
|
||||||
+y # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
def function(default=None):
|
|
||||||
@@ -93,4 +88,4 @@
|
|
||||||
|
|
||||||
# Some closing comments.
|
# Some closing comments.
|
||||||
# Maybe Vim or Emacs directives for formatting.
|
# Maybe Vim or Emacs directives for formatting.
|
||||||
|
@ -144,10 +151,12 @@ async def wat():
|
||||||
#
|
#
|
||||||
# Has many lines. Many, many lines.
|
# Has many lines. Many, many lines.
|
||||||
# Many, many, many lines.
|
# Many, many, many lines.
|
||||||
"""Module docstring.
|
(
|
||||||
|
"""Module docstring.
|
||||||
|
|
||||||
Possibly also many, many lines.
|
Possibly also many, many lines.
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
@ -160,8 +169,10 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import slow as fast
|
import slow as fast
|
||||||
y = 1
|
y = 1
|
||||||
# some strings
|
(
|
||||||
y # type: ignore
|
# some strings
|
||||||
|
y # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def function(default=None):
|
def function(default=None):
|
||||||
|
|
|
@ -276,17 +276,7 @@ last_call()
|
||||||
Name
|
Name
|
||||||
None
|
None
|
||||||
True
|
True
|
||||||
@@ -22,119 +23,83 @@
|
@@ -30,56 +31,39 @@
|
||||||
v1 << 2
|
|
||||||
1 >> v2
|
|
||||||
1 % finished
|
|
||||||
-1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
|
||||||
-((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
|
||||||
+1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8
|
|
||||||
+((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)
|
|
||||||
not great
|
|
||||||
~great
|
|
||||||
+value
|
|
||||||
-1
|
-1
|
||||||
~int and not v1 ^ 123 + v2 | True
|
~int and not v1 ^ 123 + v2 | True
|
||||||
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
||||||
|
@ -312,14 +302,14 @@ last_call()
|
||||||
(str or None) if True else (str or bytes or None)
|
(str or None) if True else (str or bytes or None)
|
||||||
str or None if (1 if True else 2) else str or bytes or None
|
str or None if (1 if True else 2) else str or bytes or None
|
||||||
(str or None) if (1 if True else 2) else (str or bytes or None)
|
(str or None) if (1 if True else 2) else (str or bytes or None)
|
||||||
-(
|
(
|
||||||
- (super_long_variable_name or None)
|
- (super_long_variable_name or None)
|
||||||
- if (1 if super_long_test_name else 2)
|
- if (1 if super_long_test_name else 2)
|
||||||
- else (str or bytes or None)
|
- else (str or bytes or None)
|
||||||
-)
|
+ (super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
||||||
|
)
|
||||||
-{"2.7": dead, "3.7": (long_live or die_hard)}
|
-{"2.7": dead, "3.7": (long_live or die_hard)}
|
||||||
-{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}}
|
-{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}}
|
||||||
+(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
|
||||||
+{'2.7': dead, '3.7': (long_live or die_hard)}
|
+{'2.7': dead, '3.7': (long_live or die_hard)}
|
||||||
+{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
+{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
||||||
{**a, **b, **c}
|
{**a, **b, **c}
|
||||||
|
@ -338,7 +328,7 @@ last_call()
|
||||||
- 2,
|
- 2,
|
||||||
- 3,
|
- 3,
|
||||||
-]
|
-]
|
||||||
+[1, 2, 3,]
|
+[1, 2, 3]
|
||||||
[*a]
|
[*a]
|
||||||
[*range(10)]
|
[*range(10)]
|
||||||
-[
|
-[
|
||||||
|
@ -351,15 +341,14 @@ last_call()
|
||||||
- *a,
|
- *a,
|
||||||
- 5,
|
- 5,
|
||||||
-]
|
-]
|
||||||
-[
|
+[*a, 4, 5]
|
||||||
- this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
+[4, *a, 5]
|
||||||
- element,
|
[
|
||||||
- another,
|
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||||
- *more,
|
element,
|
||||||
-]
|
@@ -87,54 +71,44 @@
|
||||||
+[*a, 4, 5,]
|
*more,
|
||||||
+[4, *a, 5,]
|
]
|
||||||
+[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
|
||||||
{i for i in (1, 2, 3)}
|
{i for i in (1, 2, 3)}
|
||||||
-{(i**2) for i in (1, 2, 3)}
|
-{(i**2) for i in (1, 2, 3)}
|
||||||
-{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))}
|
-{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))}
|
||||||
|
@ -417,18 +406,21 @@ last_call()
|
||||||
dict[str, int]
|
dict[str, int]
|
||||||
tuple[str, ...]
|
tuple[str, ...]
|
||||||
-tuple[str, int, float, dict[str, int]]
|
-tuple[str, int, float, dict[str, int]]
|
||||||
tuple[
|
-tuple[
|
||||||
- str,
|
- str,
|
||||||
- int,
|
- int,
|
||||||
- float,
|
- float,
|
||||||
- dict[str, int],
|
- dict[str, int],
|
||||||
|
+(
|
||||||
|
+ tuple[
|
||||||
+ str, int, float, dict[str, int]
|
+ str, int, float, dict[str, int]
|
||||||
]
|
]
|
||||||
|
+)
|
||||||
+tuple[str, int, float, dict[str, int],]
|
+tuple[str, int, float, dict[str, int],]
|
||||||
very_long_variable_name_filters: t.List[
|
very_long_variable_name_filters: t.List[
|
||||||
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
||||||
]
|
]
|
||||||
@@ -144,9 +109,9 @@
|
@@ -144,9 +118,9 @@
|
||||||
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
|
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
|
||||||
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
||||||
)
|
)
|
||||||
|
@ -441,7 +433,7 @@ last_call()
|
||||||
slice[0]
|
slice[0]
|
||||||
slice[0:1]
|
slice[0:1]
|
||||||
slice[0:1:2]
|
slice[0:1:2]
|
||||||
@@ -174,57 +139,29 @@
|
@@ -174,57 +148,29 @@
|
||||||
numpy[:, ::-1]
|
numpy[:, ::-1]
|
||||||
numpy[np.newaxis, :]
|
numpy[np.newaxis, :]
|
||||||
(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
|
(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
|
||||||
|
@ -450,9 +442,8 @@ last_call()
|
||||||
+{'2.7': dead, '3.7': long_live or die_hard}
|
+{'2.7': dead, '3.7': long_live or die_hard}
|
||||||
+{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
+{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||||
-(SomeName)
|
(SomeName)
|
||||||
SomeName
|
SomeName
|
||||||
+SomeName
|
|
||||||
(Good, Bad, Ugly)
|
(Good, Bad, Ugly)
|
||||||
(i for i in (1, 2, 3))
|
(i for i in (1, 2, 3))
|
||||||
-((i**2) for i in (1, 2, 3))
|
-((i**2) for i in (1, 2, 3))
|
||||||
|
@ -511,7 +502,7 @@ last_call()
|
||||||
Ø = set()
|
Ø = set()
|
||||||
authors.łukasz.say_thanks()
|
authors.łukasz.say_thanks()
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -237,134 +174,86 @@
|
@@ -237,114 +183,80 @@
|
||||||
|
|
||||||
def gen():
|
def gen():
|
||||||
yield from outside_of_generator
|
yield from outside_of_generator
|
||||||
|
@ -655,30 +646,7 @@ last_call()
|
||||||
+ ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
+ ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
-(
|
(
|
||||||
- aaaaaaaaaaaaaaaa
|
|
||||||
- + aaaaaaaaaaaaaaaa
|
|
||||||
- - aaaaaaaaaaaaaaaa
|
|
||||||
- * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
- / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaa * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa) / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
|
||||||
-(
|
|
||||||
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
bbbb >> bbbb * bbbb
|
|
||||||
-(
|
|
||||||
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- ^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- ^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
last_call()
|
|
||||||
# standalone comment at ENDMARKER
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
@ -709,8 +677,8 @@ Name1 or Name2 and Name3 or Name4
|
||||||
v1 << 2
|
v1 << 2
|
||||||
1 >> v2
|
1 >> v2
|
||||||
1 % finished
|
1 % finished
|
||||||
1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8
|
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
||||||
((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)
|
((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
||||||
not great
|
not great
|
||||||
~great
|
~great
|
||||||
+value
|
+value
|
||||||
|
@ -731,7 +699,9 @@ str or None if True else str or bytes or None
|
||||||
(str or None) if True else (str or bytes or None)
|
(str or None) if True else (str or bytes or None)
|
||||||
str or None if (1 if True else 2) else str or bytes or None
|
str or None if (1 if True else 2) else str or bytes or None
|
||||||
(str or None) if (1 if True else 2) else (str or bytes or None)
|
(str or None) if (1 if True else 2) else (str or bytes or None)
|
||||||
(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
(
|
||||||
|
(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
||||||
|
)
|
||||||
{'2.7': dead, '3.7': (long_live or die_hard)}
|
{'2.7': dead, '3.7': (long_live or die_hard)}
|
||||||
{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
||||||
{**a, **b, **c}
|
{**a, **b, **c}
|
||||||
|
@ -743,12 +713,17 @@ str or None if (1 if True else 2) else str or bytes or None
|
||||||
(1, 2, 3)
|
(1, 2, 3)
|
||||||
[]
|
[]
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||||
[1, 2, 3,]
|
[1, 2, 3]
|
||||||
[*a]
|
[*a]
|
||||||
[*range(10)]
|
[*range(10)]
|
||||||
[*a, 4, 5,]
|
[*a, 4, 5]
|
||||||
[4, *a, 5,]
|
[4, *a, 5]
|
||||||
[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
[
|
||||||
|
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||||
|
element,
|
||||||
|
another,
|
||||||
|
*more,
|
||||||
|
]
|
||||||
{i for i in (1, 2, 3)}
|
{i for i in (1, 2, 3)}
|
||||||
{(i ** 2) for i in (1, 2, 3)}
|
{(i ** 2) for i in (1, 2, 3)}
|
||||||
{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
|
{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
|
||||||
|
@ -782,9 +757,11 @@ call.me(maybe)
|
||||||
list[str]
|
list[str]
|
||||||
dict[str, int]
|
dict[str, int]
|
||||||
tuple[str, ...]
|
tuple[str, ...]
|
||||||
tuple[
|
(
|
||||||
|
tuple[
|
||||||
str, int, float, dict[str, int]
|
str, int, float, dict[str, int]
|
||||||
]
|
]
|
||||||
|
)
|
||||||
tuple[str, int, float, dict[str, int],]
|
tuple[str, int, float, dict[str, int],]
|
||||||
very_long_variable_name_filters: t.List[
|
very_long_variable_name_filters: t.List[
|
||||||
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
||||||
|
@ -828,7 +805,7 @@ numpy[np.newaxis, :]
|
||||||
{'2.7': dead, '3.7': long_live or die_hard}
|
{'2.7': dead, '3.7': long_live or die_hard}
|
||||||
{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||||
SomeName
|
(SomeName)
|
||||||
SomeName
|
SomeName
|
||||||
(Good, Bad, Ugly)
|
(Good, Bad, Ugly)
|
||||||
(i for i in (1, 2, 3))
|
(i for i in (1, 2, 3))
|
||||||
|
@ -936,11 +913,25 @@ if (
|
||||||
~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaa * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa) / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
(
|
||||||
|
aaaaaaaaaaaaaaaa
|
||||||
|
+ aaaaaaaaaaaaaaaa
|
||||||
|
- aaaaaaaaaaaaaaaa
|
||||||
|
* (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
||||||
|
/ (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
||||||
|
)
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
<< aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
)
|
||||||
bbbb >> bbbb * bbbb
|
bbbb >> bbbb * bbbb
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
)
|
||||||
last_call()
|
last_call()
|
||||||
# standalone comment at ENDMARKER
|
# standalone comment at ENDMARKER
|
||||||
```
|
```
|
||||||
|
|
|
@ -76,16 +76,51 @@ x[
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -56,4 +56,8 @@
|
@@ -31,14 +31,17 @@
|
||||||
|
ham[lower + offset : upper + offset]
|
||||||
|
|
||||||
|
slice[::, ::]
|
||||||
|
-slice[
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
:
|
||||||
|
# B
|
||||||
|
:
|
||||||
|
# C
|
||||||
|
]
|
||||||
|
-slice[
|
||||||
|
+)
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
1:
|
||||||
|
# B
|
||||||
|
@@ -46,8 +49,10 @@
|
||||||
|
# C
|
||||||
|
3
|
||||||
|
]
|
||||||
|
+)
|
||||||
|
|
||||||
|
-slice[
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
1
|
||||||
|
+ 2 :
|
||||||
|
@@ -56,4 +61,11 @@
|
||||||
# C
|
# C
|
||||||
4
|
4
|
||||||
]
|
]
|
||||||
-x[1:2:3] # A # B # C
|
-x[1:2:3] # A # B # C
|
||||||
+x[
|
+)
|
||||||
|
+(
|
||||||
|
+ x[
|
||||||
+ 1: # A
|
+ 1: # A
|
||||||
+ 2: # B
|
+ 2: # B
|
||||||
+ 3 # C
|
+ 3 # C
|
||||||
+]
|
+]
|
||||||
|
+)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
@ -124,14 +159,17 @@ ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
|
||||||
ham[lower + offset : upper + offset]
|
ham[lower + offset : upper + offset]
|
||||||
|
|
||||||
slice[::, ::]
|
slice[::, ::]
|
||||||
slice[
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
:
|
:
|
||||||
# B
|
# B
|
||||||
:
|
:
|
||||||
# C
|
# C
|
||||||
]
|
]
|
||||||
slice[
|
)
|
||||||
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
1:
|
1:
|
||||||
# B
|
# B
|
||||||
|
@ -139,8 +177,10 @@ slice[
|
||||||
# C
|
# C
|
||||||
3
|
3
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
slice[
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
1
|
1
|
||||||
+ 2 :
|
+ 2 :
|
||||||
|
@ -149,11 +189,14 @@ slice[
|
||||||
# C
|
# C
|
||||||
4
|
4
|
||||||
]
|
]
|
||||||
x[
|
)
|
||||||
|
(
|
||||||
|
x[
|
||||||
1: # A
|
1: # A
|
||||||
2: # B
|
2: # B
|
||||||
3 # C
|
3 # C
|
||||||
]
|
]
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Black Output
|
## Black Output
|
||||||
|
|
|
@ -42,14 +42,13 @@ assert (
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -1,58 +1,33 @@
|
@@ -2,18 +2,13 @@
|
||||||
importA
|
(
|
||||||
-(
|
()
|
||||||
- ()
|
<< 0
|
||||||
- << 0
|
|
||||||
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
-) #
|
+ **101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
+() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
) #
|
||||||
|
|
||||||
assert sort_by_dependency(
|
assert sort_by_dependency(
|
||||||
{
|
{
|
||||||
|
@ -65,12 +64,7 @@ assert (
|
||||||
}
|
}
|
||||||
) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
|
) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
|
||||||
|
|
||||||
importA
|
@@ -25,34 +20,18 @@
|
||||||
0
|
|
||||||
-0 ^ 0 #
|
|
||||||
+0^0 #
|
|
||||||
|
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def foo(self):
|
def foo(self):
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
|
@ -120,7 +114,11 @@ assert (
|
||||||
|
|
||||||
```py
|
```py
|
||||||
importA
|
importA
|
||||||
() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
(
|
||||||
|
()
|
||||||
|
<< 0
|
||||||
|
**101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
|
) #
|
||||||
|
|
||||||
assert sort_by_dependency(
|
assert sort_by_dependency(
|
||||||
{
|
{
|
||||||
|
@ -131,7 +129,7 @@ assert sort_by_dependency(
|
||||||
|
|
||||||
importA
|
importA
|
||||||
0
|
0
|
||||||
0^0 #
|
0 ^ 0 #
|
||||||
|
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
(aaaaaaaa
|
||||||
|
+ # trailing operator comment
|
||||||
|
b # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
aaaaaaaaaaaaaa + caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
aaaaaaaaaaaaaa + [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee]
|
||||||
|
aaaaaaaaaaaaaa + (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
aaaaaaaaaaaaaa + { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
aaaaaaaaaaaaaa + (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eee
|
||||||
|
] # comment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
[a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c ]
|
||||||
|
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content + b
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
(
|
||||||
|
aaaaaaaa
|
||||||
|
+ b # trailing operator comment # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(
|
||||||
|
aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
)
|
||||||
|
aaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eeeeeee,
|
||||||
|
]
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
)
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
+ [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eee]
|
||||||
|
) # comment
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
(
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
)
|
||||||
|
[
|
||||||
|
a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c,
|
||||||
|
]
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content
|
||||||
|
+ b
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use rustpython_parser::ast::StmtExpr;
|
use rustpython_parser::ast::StmtExpr;
|
||||||
|
@ -9,6 +10,6 @@ impl FormatNodeRule<StmtExpr> for FormatStmtExpr {
|
||||||
fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let StmtExpr { value, .. } = item;
|
let StmtExpr { value, .. } = item;
|
||||||
|
|
||||||
value.format().fmt(f)
|
value.format().with_options(Parenthesize::Optional).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments};
|
use crate::comments::{leading_alternate_branch_comments, trailing_comments};
|
||||||
use crate::expression::maybe_parenthesize::maybe_parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::write;
|
||||||
|
@ -33,7 +33,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
[
|
[
|
||||||
text("while"),
|
text("while"),
|
||||||
space(),
|
space(),
|
||||||
maybe_parenthesize(test),
|
test.format().with_options(Parenthesize::IfBreaks),
|
||||||
text(":"),
|
text(":"),
|
||||||
trailing_comments(trailing_condition_comments),
|
trailing_comments(trailing_condition_comments),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
|
|
|
@ -10,8 +10,8 @@ use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||||
///
|
///
|
||||||
/// Returns `None` if the range is empty or only contains trivia (whitespace or comments).
|
/// Returns `None` if the range is empty or only contains trivia (whitespace or comments).
|
||||||
pub(crate) fn find_first_non_trivia_character_in_range(
|
pub(crate) fn find_first_non_trivia_character_in_range(
|
||||||
code: &str,
|
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
|
code: &str,
|
||||||
) -> Option<(TextSize, char)> {
|
) -> Option<(TextSize, char)> {
|
||||||
let rest = &code[range];
|
let rest = &code[range];
|
||||||
let mut char_iter = rest.chars();
|
let mut char_iter = rest.chars();
|
||||||
|
@ -40,8 +40,63 @@ pub(crate) fn find_first_non_trivia_character_in_range(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_first_non_trivia_character_after(
|
||||||
|
offset: TextSize,
|
||||||
|
code: &str,
|
||||||
|
) -> Option<(TextSize, char)> {
|
||||||
|
find_first_non_trivia_character_in_range(TextRange::new(offset, code.text_len()), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_first_non_trivia_character_before(
|
||||||
|
offset: TextSize,
|
||||||
|
code: &str,
|
||||||
|
) -> Option<(TextSize, char)> {
|
||||||
|
let head = &code[TextRange::up_to(offset)];
|
||||||
|
let mut char_iter = head.chars();
|
||||||
|
|
||||||
|
while let Some(c) = char_iter.next_back() {
|
||||||
|
match c {
|
||||||
|
c if is_python_whitespace(c) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty comment
|
||||||
|
'#' => continue,
|
||||||
|
|
||||||
|
non_trivia_character => {
|
||||||
|
// Non trivia character but we don't know if it is a comment or not. Consume all characters
|
||||||
|
// until the start of the line and track if the last non-whitespace character was a `#`.
|
||||||
|
let mut is_comment = false;
|
||||||
|
|
||||||
|
let first_non_trivia_offset = char_iter.as_str().text_len();
|
||||||
|
|
||||||
|
while let Some(c) = char_iter.next_back() {
|
||||||
|
match c {
|
||||||
|
'#' => {
|
||||||
|
is_comment = true;
|
||||||
|
}
|
||||||
|
'\n' | '\r' => {
|
||||||
|
if !is_comment {
|
||||||
|
return Some((first_non_trivia_offset, non_trivia_character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c => {
|
||||||
|
if !is_python_whitespace(c) {
|
||||||
|
is_comment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of newlines between `offset` and the first non whitespace character in the source code.
|
/// Returns the number of newlines between `offset` and the first non whitespace character in the source code.
|
||||||
pub(crate) fn lines_before(code: &str, offset: TextSize) -> u32 {
|
pub(crate) fn lines_before(offset: TextSize, code: &str) -> u32 {
|
||||||
let head = &code[TextRange::up_to(offset)];
|
let head = &code[TextRange::up_to(offset)];
|
||||||
let mut newlines = 0u32;
|
let mut newlines = 0u32;
|
||||||
|
|
||||||
|
@ -68,7 +123,7 @@ pub(crate) fn lines_before(code: &str, offset: TextSize) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counts the empty lines between `offset` and the first non-whitespace character.
|
/// Counts the empty lines between `offset` and the first non-whitespace character.
|
||||||
pub(crate) fn lines_after(code: &str, offset: TextSize) -> u32 {
|
pub(crate) fn lines_after(offset: TextSize, code: &str) -> u32 {
|
||||||
let rest = &code[usize::from(offset)..];
|
let rest = &code[usize::from(offset)..];
|
||||||
let mut newlines = 0;
|
let mut newlines = 0;
|
||||||
|
|
||||||
|
@ -98,33 +153,33 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_empty_string() {
|
fn lines_before_empty_string() {
|
||||||
assert_eq!(lines_before("", TextSize::new(0)), 0);
|
assert_eq!(lines_before(TextSize::new(0), ""), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_in_the_middle_of_a_line() {
|
fn lines_before_in_the_middle_of_a_line() {
|
||||||
assert_eq!(lines_before("a = 20", TextSize::new(4)), 0);
|
assert_eq!(lines_before(TextSize::new(4), "a = 20"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_on_a_new_line() {
|
fn lines_before_on_a_new_line() {
|
||||||
assert_eq!(lines_before("a = 20\nb = 10", TextSize::new(7)), 1);
|
assert_eq!(lines_before(TextSize::new(7), "a = 20\nb = 10"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_multiple_leading_newlines() {
|
fn lines_before_multiple_leading_newlines() {
|
||||||
assert_eq!(lines_before("a = 20\n\r\nb = 10", TextSize::new(9)), 2);
|
assert_eq!(lines_before(TextSize::new(9), "a = 20\n\r\nb = 10"), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_comment_offset() {
|
fn lines_before_with_comment_offset() {
|
||||||
assert_eq!(lines_before("a = 20\n# a comment", TextSize::new(8)), 0);
|
assert_eq!(lines_before(TextSize::new(8), "a = 20\n# a comment"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_trailing_comment() {
|
fn lines_before_with_trailing_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_before("a = 20 # some comment\nb = 10", TextSize::new(22)),
|
lines_before(TextSize::new(22), "a = 20 # some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -132,40 +187,40 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_comment_only_line() {
|
fn lines_before_with_comment_only_line() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_before("a = 20\n# some comment\nb = 10", TextSize::new(22)),
|
lines_before(TextSize::new(22), "a = 20\n# some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_empty_string() {
|
fn lines_after_empty_string() {
|
||||||
assert_eq!(lines_after("", TextSize::new(0)), 0);
|
assert_eq!(lines_after(TextSize::new(0), ""), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_in_the_middle_of_a_line() {
|
fn lines_after_in_the_middle_of_a_line() {
|
||||||
assert_eq!(lines_after("a = 20", TextSize::new(4)), 0);
|
assert_eq!(lines_after(TextSize::new(4), "a = 20"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_before_a_new_line() {
|
fn lines_after_before_a_new_line() {
|
||||||
assert_eq!(lines_after("a = 20\nb = 10", TextSize::new(6)), 1);
|
assert_eq!(lines_after(TextSize::new(6), "a = 20\nb = 10"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_multiple_newlines() {
|
fn lines_after_multiple_newlines() {
|
||||||
assert_eq!(lines_after("a = 20\n\r\nb = 10", TextSize::new(6)), 2);
|
assert_eq!(lines_after(TextSize::new(6), "a = 20\n\r\nb = 10"), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_before_comment_offset() {
|
fn lines_after_before_comment_offset() {
|
||||||
assert_eq!(lines_after("a = 20 # a comment\n", TextSize::new(7)), 0);
|
assert_eq!(lines_after(TextSize::new(7), "a = 20 # a comment\n"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_with_comment_only_line() {
|
fn lines_after_with_comment_only_line() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_after("a = 20\n# some comment\nb = 10", TextSize::new(6)),
|
lines_after(TextSize::new(6), "a = 20\n# some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue