mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 13:05:06 +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
|
@ -12,7 +12,7 @@ pub(crate) trait PyFormatterExtensions<'ast, 'buf> {
|
|||
///
|
||||
/// * [`NodeLevel::Module`]: Up to two empty lines
|
||||
/// * [`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>;
|
||||
}
|
||||
|
||||
|
@ -48,18 +48,18 @@ impl<'fmt, 'ast, 'buf> JoinNodesBuilder<'fmt, 'ast, 'buf> {
|
|||
{
|
||||
let node_level = self.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),
|
||||
2 => empty_line().fmt(f),
|
||||
_ => write!(f, [empty_line(), empty_line()]),
|
||||
},
|
||||
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),
|
||||
_ => empty_line().fmt(f),
|
||||
}
|
||||
}
|
||||
NodeLevel::Parenthesized => hard_line_break().fmt(f),
|
||||
NodeLevel::Expression => hard_line_break().fmt(f),
|
||||
});
|
||||
|
||||
self.entry_with_separator(&separator, content);
|
||||
|
@ -200,7 +200,7 @@ no_leading_newline = 30"#
|
|||
// Removes all empty lines
|
||||
#[test]
|
||||
fn ranged_builder_parenthesized_level() {
|
||||
let printed = format_ranged(NodeLevel::Parenthesized);
|
||||
let printed = format_ranged(NodeLevel::Expression);
|
||||
|
||||
assert_eq!(
|
||||
&printed,
|
||||
|
|
|
@ -39,7 +39,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingComments<'_> {
|
|||
for comment in leading_comments {
|
||||
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!(
|
||||
f,
|
||||
[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() {
|
||||
// Leading comments only preserves the lines after the comment but not before.
|
||||
// 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()])?;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {
|
|||
} else if let Some(last_preceding) = self.last_node {
|
||||
// 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.
|
||||
if lines_after(f.context().contents(), last_preceding.end()) > 1 {
|
||||
if lines_after(last_preceding.end(), f.context().contents()) > 1 {
|
||||
write!(f, [empty_line()])?;
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
|||
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
||||
|
||||
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
|
||||
// ```python
|
||||
|
@ -175,20 +175,26 @@ pub(crate) fn dangling_node_comments<T>(node: &T) -> FormatDanglingComments
|
|||
where
|
||||
T: AstNode,
|
||||
{
|
||||
FormatDanglingComments {
|
||||
node: node.as_any_node_ref(),
|
||||
}
|
||||
FormatDanglingComments::Node(node.as_any_node_ref())
|
||||
}
|
||||
|
||||
pub(crate) struct FormatDanglingComments<'a> {
|
||||
node: AnyNodeRef<'a>,
|
||||
pub(crate) fn dangling_comments(comments: &[SourceComment]) -> FormatDanglingComments {
|
||||
FormatDanglingComments::Comments(comments)
|
||||
}
|
||||
|
||||
pub(crate) enum FormatDanglingComments<'a> {
|
||||
Node(AnyNodeRef<'a>),
|
||||
Comments(&'a [SourceComment]),
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
|
||||
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;
|
||||
for comment in dangling_comments {
|
||||
|
@ -200,7 +206,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
|||
f,
|
||||
[
|
||||
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
|
||||
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::visitor::CommentsVisitor;
|
||||
pub(crate) use format::{
|
||||
dangling_node_comments, leading_alternate_branch_comments, leading_node_comments,
|
||||
trailing_comments, trailing_node_comments,
|
||||
dangling_comments, dangling_node_comments, leading_alternate_branch_comments,
|
||||
leading_node_comments, trailing_comments, trailing_node_comments,
|
||||
};
|
||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||
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) {
|
||||
let mut start = preceding.end();
|
||||
while let Some((offset, c)) = find_first_non_trivia_character_in_range(
|
||||
locator.contents(),
|
||||
TextRange::new(start, following.start()),
|
||||
locator.contents(),
|
||||
) {
|
||||
match c {
|
||||
':' => {
|
||||
|
@ -655,7 +655,7 @@ fn handle_trailing_binary_expression_left_or_operator_comment<'a>(
|
|||
);
|
||||
|
||||
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
|
||||
Some((offset, ')')) => {
|
||||
between_operands_range =
|
||||
|
@ -733,17 +733,17 @@ fn find_pos_only_slash_offset(
|
|||
locator: &Locator,
|
||||
) -> Option<TextSize> {
|
||||
// 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)| {
|
||||
debug_assert_eq!(comma, ',');
|
||||
|
||||
// Then find the position of the `/` operator
|
||||
find_first_non_trivia_character_in_range(
|
||||
locator.contents(),
|
||||
TextRange::new(
|
||||
comma_offset + TextSize::new(1),
|
||||
between_arguments_range.end(),
|
||||
),
|
||||
locator.contents(),
|
||||
)
|
||||
.map(|(offset, c)| {
|
||||
debug_assert_eq!(c, '/');
|
||||
|
|
|
@ -82,5 +82,5 @@ pub(crate) enum NodeLevel {
|
|||
CompoundStatement,
|
||||
|
||||
/// 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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprAttribute;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprAwait;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAwait> for FormatExprAwait {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprBinOp;
|
||||
use crate::comments::trailing_comments;
|
||||
use crate::expression::parentheses::{
|
||||
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)]
|
||||
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 {
|
||||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprBoolOp;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprCall;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprCompare;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprCompare> for FormatExprCompare {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprConstant;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprConstant> for FormatExprConstant {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprDict;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDict> for FormatExprDict {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprDictComp;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDictComp> for FormatExprDictComp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprFormattedValue;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprFormattedValue> for FormatExprFormattedValue {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprGeneratorExp;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprGeneratorExp> for FormatExprGeneratorExp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprIfExp;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprIfExp> for FormatExprIfExp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprJoinedStr;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprJoinedStr> for FormatExprJoinedStr {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprLambda;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use crate::comments::dangling_comments;
|
||||
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;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -7,6 +12,55 @@ pub struct FormatExprList;
|
|||
|
||||
impl FormatNodeRule<ExprList> for FormatExprList {
|
||||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprListComp;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprListComp> for FormatExprListComp {
|
|||
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::FormatNodeRule;
|
||||
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)]
|
||||
mod tests {
|
||||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprNamedExpr;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprSet;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSet> for FormatExprSet {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprSetComp;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSetComp> for FormatExprSetComp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprSlice;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSlice> for FormatExprSlice {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprStarred;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprStarred> for FormatExprStarred {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprSubscript;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprTuple;
|
||||
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprUnaryOp;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprYield;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYield> for FormatExprYield {
|
|||
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 ruff_formatter::{write, Buffer, FormatResult};
|
||||
use rustpython_parser::ast::ExprYieldFrom;
|
||||
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYieldFrom> for FormatExprYieldFrom {
|
|||
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 ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule};
|
||||
use ruff_formatter::{
|
||||
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
||||
};
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
pub(crate) mod expr_attribute;
|
||||
|
@ -29,17 +33,30 @@ pub(crate) mod expr_tuple;
|
|||
pub(crate) mod expr_unary_op;
|
||||
pub(crate) mod expr_yield;
|
||||
pub(crate) mod expr_yield_from;
|
||||
pub(crate) mod maybe_parenthesize;
|
||||
pub(crate) mod parentheses;
|
||||
|
||||
#[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 {
|
||||
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::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::Lambda(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::Tuple(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_dangling_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.
|
||||
|
@ -225,8 +225,11 @@ if True:
|
|||
|
||||
let formatted_code = printed.as_code();
|
||||
|
||||
let reformatted =
|
||||
format_module(formatted_code).expect("Expected formatted code to be valid syntax");
|
||||
let reformatted = format_module(formatted_code).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Formatted code resulted introduced a syntax error {err:#?}. Code:\n{formatted_code}"
|
||||
)
|
||||
});
|
||||
|
||||
if reformatted.as_code() != formatted_code {
|
||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||
|
@ -314,7 +317,7 @@ Formatted twice:
|
|||
let formatted_code = printed.as_code();
|
||||
|
||||
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 {
|
||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||
|
@ -378,7 +381,9 @@ other
|
|||
// Uncomment the `dbg` to print the IR.
|
||||
// Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR
|
||||
// inside of a `Format` implementation
|
||||
// dbg!(formatted.document());
|
||||
// dbg!(formatted
|
||||
// .document()
|
||||
// .display(formatted.context().source_code()));
|
||||
|
||||
let printed = formatted.print().unwrap();
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ if True:
|
|||
- 2,
|
||||
- 3,
|
||||
-]
|
||||
+[1, 2, 3,]
|
||||
+[1, 2, 3]
|
||||
|
||||
-division_result_tuple = (6 / 2,)
|
||||
+division_result_tuple = (6/2,)
|
||||
|
@ -250,7 +250,7 @@ for x in (1,):
|
|||
for (x,) in (1,), (2,), (3,):
|
||||
pass
|
||||
|
||||
[1, 2, 3,]
|
||||
[1, 2, 3]
|
||||
|
||||
division_result_tuple = (6/2,)
|
||||
print("foo %r", (foo.bar,))
|
||||
|
|
|
@ -109,7 +109,21 @@ async def wat():
|
|||
```diff
|
||||
--- Black
|
||||
+++ 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
|
||||
except ImportError:
|
||||
import slow as fast
|
||||
|
@ -117,16 +131,9 @@ async def wat():
|
|||
-
|
||||
-# Some comment before a function.
|
||||
y = 1
|
||||
-(
|
||||
- # some strings
|
||||
- y # type: ignore
|
||||
-)
|
||||
+# some strings
|
||||
+y # type: ignore
|
||||
|
||||
|
||||
def function(default=None):
|
||||
@@ -93,4 +88,4 @@
|
||||
(
|
||||
# some strings
|
||||
@@ -93,4 +92,4 @@
|
||||
|
||||
# Some closing comments.
|
||||
# Maybe Vim or Emacs directives for formatting.
|
||||
|
@ -144,10 +151,12 @@ async def wat():
|
|||
#
|
||||
# Has many lines. Many, many lines.
|
||||
# Many, many, many lines.
|
||||
"""Module docstring.
|
||||
(
|
||||
"""Module docstring.
|
||||
|
||||
Possibly also many, many lines.
|
||||
"""
|
||||
)
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
@ -160,8 +169,10 @@ try:
|
|||
except ImportError:
|
||||
import slow as fast
|
||||
y = 1
|
||||
# some strings
|
||||
y # type: ignore
|
||||
(
|
||||
# some strings
|
||||
y # type: ignore
|
||||
)
|
||||
|
||||
|
||||
def function(default=None):
|
||||
|
|
|
@ -276,17 +276,7 @@ last_call()
|
|||
Name
|
||||
None
|
||||
True
|
||||
@@ -22,119 +23,83 @@
|
||||
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
|
||||
@@ -30,56 +31,39 @@
|
||||
-1
|
||||
~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 (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), **{"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), **{'3.6': verygood}}
|
||||
{**a, **b, **c}
|
||||
|
@ -338,7 +328,7 @@ last_call()
|
|||
- 2,
|
||||
- 3,
|
||||
-]
|
||||
+[1, 2, 3,]
|
||||
+[1, 2, 3]
|
||||
[*a]
|
||||
[*range(10)]
|
||||
-[
|
||||
|
@ -351,15 +341,14 @@ last_call()
|
|||
- *a,
|
||||
- 5,
|
||||
-]
|
||||
-[
|
||||
- this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||
- element,
|
||||
- another,
|
||||
- *more,
|
||||
-]
|
||||
+[*a, 4, 5,]
|
||||
+[4, *a, 5,]
|
||||
+[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
||||
+[*a, 4, 5]
|
||||
+[4, *a, 5]
|
||||
[
|
||||
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||
element,
|
||||
@@ -87,54 +71,44 @@
|
||||
*more,
|
||||
]
|
||||
{i 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"))}
|
||||
|
@ -417,18 +406,21 @@ last_call()
|
|||
dict[str, int]
|
||||
tuple[str, ...]
|
||||
-tuple[str, int, float, dict[str, int]]
|
||||
tuple[
|
||||
-tuple[
|
||||
- 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[
|
||||
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
|
||||
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
||||
)
|
||||
|
@ -441,7 +433,7 @@ last_call()
|
|||
slice[0]
|
||||
slice[0:1]
|
||||
slice[0:1:2]
|
||||
@@ -174,57 +139,29 @@
|
||||
@@ -174,57 +148,29 @@
|
||||
numpy[:, ::-1]
|
||||
numpy[np.newaxis, :]
|
||||
(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', '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]
|
||||
-(SomeName)
|
||||
(SomeName)
|
||||
SomeName
|
||||
+SomeName
|
||||
(Good, Bad, Ugly)
|
||||
(i for i in (1, 2, 3))
|
||||
-((i**2) for i in (1, 2, 3))
|
||||
|
@ -511,7 +502,7 @@ last_call()
|
|||
Ø = set()
|
||||
authors.łukasz.say_thanks()
|
||||
mapping = {
|
||||
@@ -237,134 +174,86 @@
|
||||
@@ -237,114 +183,80 @@
|
||||
|
||||
def gen():
|
||||
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
|
||||
):
|
||||
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
|
||||
|
@ -709,8 +677,8 @@ Name1 or Name2 and Name3 or Name4
|
|||
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
|
||||
|
@ -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 (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), **{'3.6': verygood}}
|
||||
{**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, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||
[1, 2, 3,]
|
||||
[1, 2, 3]
|
||||
[*a]
|
||||
[*range(10)]
|
||||
[*a, 4, 5,]
|
||||
[4, *a, 5,]
|
||||
[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
||||
[*a, 4, 5]
|
||||
[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 ** 2) for i in (1, 2, 3)}
|
||||
{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
|
||||
|
@ -782,9 +757,11 @@ call.me(maybe)
|
|||
list[str]
|
||||
dict[str, int]
|
||||
tuple[str, ...]
|
||||
tuple[
|
||||
(
|
||||
tuple[
|
||||
str, int, float, dict[str, int]
|
||||
]
|
||||
)
|
||||
tuple[str, int, float, dict[str, int],]
|
||||
very_long_variable_name_filters: t.List[
|
||||
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', '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]
|
||||
SomeName
|
||||
(SomeName)
|
||||
SomeName
|
||||
(Good, Bad, Ugly)
|
||||
(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
|
||||
):
|
||||
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
|
||||
```
|
||||
|
|
|
@ -76,16 +76,51 @@ x[
|
|||
```diff
|
||||
--- Black
|
||||
+++ 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
|
||||
4
|
||||
]
|
||||
-x[1:2:3] # A # B # C
|
||||
+x[
|
||||
+)
|
||||
+(
|
||||
+ x[
|
||||
+ 1: # A
|
||||
+ 2: # B
|
||||
+ 3 # C
|
||||
+]
|
||||
+)
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
@ -124,14 +159,17 @@ ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
|
|||
ham[lower + offset : upper + offset]
|
||||
|
||||
slice[::, ::]
|
||||
slice[
|
||||
(
|
||||
slice[
|
||||
# A
|
||||
:
|
||||
# B
|
||||
:
|
||||
# C
|
||||
]
|
||||
slice[
|
||||
)
|
||||
(
|
||||
slice[
|
||||
# A
|
||||
1:
|
||||
# B
|
||||
|
@ -139,8 +177,10 @@ slice[
|
|||
# C
|
||||
3
|
||||
]
|
||||
)
|
||||
|
||||
slice[
|
||||
(
|
||||
slice[
|
||||
# A
|
||||
1
|
||||
+ 2 :
|
||||
|
@ -149,11 +189,14 @@ slice[
|
|||
# C
|
||||
4
|
||||
]
|
||||
x[
|
||||
)
|
||||
(
|
||||
x[
|
||||
1: # A
|
||||
2: # B
|
||||
3 # C
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
|
|
@ -42,14 +42,13 @@ assert (
|
|||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,58 +1,33 @@
|
||||
importA
|
||||
-(
|
||||
- ()
|
||||
- << 0
|
||||
@@ -2,18 +2,13 @@
|
||||
(
|
||||
()
|
||||
<< 0
|
||||
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||
-) #
|
||||
+() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
||||
+ **101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||
) #
|
||||
|
||||
assert sort_by_dependency(
|
||||
{
|
||||
|
@ -65,12 +64,7 @@ assert (
|
|||
}
|
||||
) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
|
||||
|
||||
importA
|
||||
0
|
||||
-0 ^ 0 #
|
||||
+0^0 #
|
||||
|
||||
|
||||
@@ -25,34 +20,18 @@
|
||||
class A:
|
||||
def foo(self):
|
||||
for _ in range(10):
|
||||
|
@ -120,7 +114,11 @@ assert (
|
|||
|
||||
```py
|
||||
importA
|
||||
() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
||||
(
|
||||
()
|
||||
<< 0
|
||||
**101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||
) #
|
||||
|
||||
assert sort_by_dependency(
|
||||
{
|
||||
|
@ -131,7 +129,7 @@ assert sort_by_dependency(
|
|||
|
||||
importA
|
||||
0
|
||||
0^0 #
|
||||
0 ^ 0 #
|
||||
|
||||
|
||||
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::FormatNodeRule;
|
||||
use rustpython_parser::ast::StmtExpr;
|
||||
|
@ -9,6 +10,6 @@ impl FormatNodeRule<StmtExpr> for FormatStmtExpr {
|
|||
fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
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::expression::maybe_parenthesize::maybe_parenthesize;
|
||||
use crate::expression::parentheses::Parenthesize;
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_formatter::write;
|
||||
|
@ -33,7 +33,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
|||
[
|
||||
text("while"),
|
||||
space(),
|
||||
maybe_parenthesize(test),
|
||||
test.format().with_options(Parenthesize::IfBreaks),
|
||||
text(":"),
|
||||
trailing_comments(trailing_condition_comments),
|
||||
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).
|
||||
pub(crate) fn find_first_non_trivia_character_in_range(
|
||||
code: &str,
|
||||
range: TextRange,
|
||||
code: &str,
|
||||
) -> Option<(TextSize, char)> {
|
||||
let rest = &code[range];
|
||||
let mut char_iter = rest.chars();
|
||||
|
@ -40,8 +40,63 @@ pub(crate) fn find_first_non_trivia_character_in_range(
|
|||
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.
|
||||
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 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.
|
||||
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 mut newlines = 0;
|
||||
|
||||
|
@ -98,33 +153,33 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn lines_before_empty_string() {
|
||||
assert_eq!(lines_before("", TextSize::new(0)), 0);
|
||||
assert_eq!(lines_before(TextSize::new(0), ""), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
fn lines_before_with_trailing_comment() {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
@ -132,40 +187,40 @@ mod tests {
|
|||
#[test]
|
||||
fn lines_before_with_comment_only_line() {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines_after_empty_string() {
|
||||
assert_eq!(lines_after("", TextSize::new(0)), 0);
|
||||
assert_eq!(lines_after(TextSize::new(0), ""), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
fn lines_after_with_comment_only_line() {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue