diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py new file mode 100644 index 0000000000..7406108226 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/slice.py @@ -0,0 +1,82 @@ +# Handle comments both when lower and upper exist and when they don't +a1 = "a"[ + # a + 1 # b + : # c + 2 # d +] +a2 = "a"[ + # a + # b + : # c + # d +] + +# Check all places where comments can exist +b1 = "b"[ # a + # b + 1 # c + # d + : # e + # f + 2 # g + # h + : # i + # j + 3 # k + # l +] + +# Handle the spacing from the colon correctly with upper leading comments +c1 = "c"[ + 1 + : # e + # f + 2 +] +c2 = "c"[ + 1 + : # e + 2 +] +c3 = "c"[ + 1 + : + # f + 2 +] +c4 = "c"[ + 1 + : # f + 2 +] + +# End of line comments +d1 = "d"[ # comment + : +] +d2 = "d"[ # comment + 1: +] +d3 = "d"[ + 1 # comment + : +] + +# Spacing around the colon(s) +def a(): + ... + +e00 = "e"[:] +e01 = "e"[:1] +e02 = "e"[: a()] +e10 = "e"[1:] +e11 = "e"[1:1] +e12 = "e"[1 : a()] +e20 = "e"[a() :] +e21 = "e"[a() : 1] +e22 = "e"[a() : a()] +e200 = "e"[a() :: ] +e201 = "e"[a() :: 1] +e202 = "e"[a() :: a()] +e210 = "e"[a() : 1 :] diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index a1f9fa2cae..fa6fa5baaa 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -1,16 +1,14 @@ -use std::cmp::Ordering; - -use ruff_text_size::{TextRange, TextSize}; -use rustpython_parser::ast::Ranged; - -use ruff_python_ast::node::AnyNodeRef; +use crate::comments::visitor::{CommentPlacement, DecoratedComment}; +use crate::comments::CommentLinePosition; +use crate::expression::expr_slice::{assign_comment_in_slice, ExprSliceCommentSection}; +use crate::trivia::{first_non_trivia_token_rev, SimpleTokenizer, Token, TokenKind}; +use ruff_python_ast::node::{AnyNodeRef, AstNode}; use ruff_python_ast::source_code::Locator; use ruff_python_ast::whitespace; use ruff_python_whitespace::{PythonWhitespace, UniversalNewlines}; - -use crate::comments::visitor::{CommentPlacement, DecoratedComment}; -use crate::comments::CommentLinePosition; -use crate::trivia::{SimpleTokenizer, Token, TokenKind}; +use ruff_text_size::{TextRange, TextSize}; +use rustpython_parser::ast::{Expr, ExprSlice, Ranged}; +use std::cmp::Ordering; /// Implements the custom comment placement logic. pub(super) fn place_comment<'a>( @@ -30,6 +28,7 @@ pub(super) fn place_comment<'a>( handle_trailing_binary_expression_left_or_operator_comment, handle_leading_function_with_decorators_comment, handle_dict_unpacking_comment, + handle_slice_comments, ]; for handler in HANDLERS { comment = match handler(comment, locator) { @@ -837,6 +836,87 @@ fn handle_module_level_own_line_comment_before_class_or_function_comment<'a>( } } +/// Handles the attaching comments left or right of the colon in a slice as trailing comment of the +/// preceding node or leading comment of the following node respectively. +/// ```python +/// a = "input"[ +/// 1 # c +/// # d +/// :2 +/// ] +/// ``` +fn handle_slice_comments<'a>( + comment: DecoratedComment<'a>, + locator: &Locator, +) -> CommentPlacement<'a> { + let expr_slice = match comment.enclosing_node() { + AnyNodeRef::ExprSlice(expr_slice) => expr_slice, + AnyNodeRef::ExprSubscript(expr_subscript) => { + if expr_subscript.value.end() < expr_subscript.slice.start() { + if let Expr::Slice(expr_slice) = expr_subscript.slice.as_ref() { + expr_slice + } else { + return CommentPlacement::Default(comment); + } + } else { + return CommentPlacement::Default(comment); + } + } + _ => return CommentPlacement::Default(comment), + }; + + let ExprSlice { + range: _, + lower, + upper, + step, + } = expr_slice; + + // Check for `foo[ # comment`, but only if they are on the same line + let after_lbracket = matches!( + first_non_trivia_token_rev(comment.slice().start(), locator.contents()), + Some(Token { + kind: TokenKind::LBracket, + .. + }) + ); + if comment.line_position().is_end_of_line() && after_lbracket { + // Keep comments after the opening bracket there by formatting them outside the + // soft block indent + // ```python + // "a"[ # comment + // 1: + // ] + // ``` + debug_assert!( + matches!(comment.enclosing_node(), AnyNodeRef::ExprSubscript(_)), + "{:?}", + comment.enclosing_node() + ); + return CommentPlacement::dangling(comment.enclosing_node(), comment); + } + + let assignment = + assign_comment_in_slice(comment.slice().range(), locator.contents(), expr_slice); + let node = match assignment { + ExprSliceCommentSection::Lower => lower, + ExprSliceCommentSection::Upper => upper, + ExprSliceCommentSection::Step => step, + }; + + if let Some(node) = node { + if comment.slice().start() < node.start() { + CommentPlacement::leading(node.as_ref().into(), comment) + } else { + // If a trailing comment is an end of line comment that's fine because we have a node + // ahead of it + CommentPlacement::trailing(node.as_ref().into(), comment) + } + } else { + CommentPlacement::dangling(expr_slice.as_any_node_ref(), comment) + } +} + /// Finds the offset of the `/` that separates the positional only and arguments from the other arguments. /// Returns `None` if the positional only separator `/` isn't present in the specified range. fn find_pos_only_slash_offset( diff --git a/crates/ruff_python_formatter/src/expression/expr_slice.rs b/crates/ruff_python_formatter/src/expression/expr_slice.rs index 529ef4679b..2edbcc3146 100644 --- a/crates/ruff_python_formatter/src/expression/expr_slice.rs +++ b/crates/ruff_python_formatter/src/expression/expr_slice.rs @@ -1,26 +1,264 @@ +use crate::comments::{dangling_comments, Comments, SourceComment}; use crate::expression::parentheses::{ default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, }; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; - -use crate::comments::Comments; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::trivia::Token; +use crate::trivia::{first_non_trivia_token, TokenKind}; +use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use ruff_formatter::prelude::{hard_line_break, line_suffix_boundary, space, text}; +use ruff_formatter::{write, Buffer, Format, FormatError, FormatResult}; +use ruff_python_ast::node::AstNode; +use ruff_python_ast::prelude::{Expr, Ranged}; +use ruff_text_size::TextRange; use rustpython_parser::ast::ExprSlice; #[derive(Default)] pub struct FormatExprSlice; impl FormatNodeRule for FormatExprSlice { - fn fmt_fields(&self, _item: &ExprSlice, f: &mut PyFormatter) -> FormatResult<()> { - write!( - f, - [not_yet_implemented_custom_text( - "NOT_IMPLEMENTED_start:NOT_IMPLEMENTED_end" - )] - ) + /// This implementation deviates from black in that comments are attached to the section of the + /// slice they originate in + fn fmt_fields(&self, item: &ExprSlice, f: &mut PyFormatter) -> FormatResult<()> { + // `[lower:upper:step]` + let ExprSlice { + range, + lower, + upper, + step, + } = item; + + let (first_colon, second_colon) = + find_colons(f.context().contents(), *range, lower, upper)?; + + // Handle comment placement + // In placements.rs, we marked comment for None nodes a dangling and associated all others + // as leading or dangling wrt to a node. That means we either format a node and only have + // to handle newlines and spacing, or the node is None and we insert the corresponding + // slice of dangling comments + let comments = f.context().comments().clone(); + let slice_dangling_comments = comments.dangling_comments(item.as_any_node_ref()); + // Put the dangling comments (where the nodes are missing) into buckets + let first_colon_partition_index = slice_dangling_comments + .partition_point(|x| x.slice().start() < first_colon.range.start()); + let (dangling_lower_comments, dangling_upper_step_comments) = + slice_dangling_comments.split_at(first_colon_partition_index); + let (dangling_upper_comments, dangling_step_comments) = + if let Some(second_colon) = &second_colon { + let second_colon_partition_index = dangling_upper_step_comments + .partition_point(|x| x.slice().start() < second_colon.range.start()); + dangling_upper_step_comments.split_at(second_colon_partition_index) + } else { + // Without a second colon they remaining dangling comments belong between the first + // colon and the closing parentheses + (dangling_upper_step_comments, [].as_slice()) + }; + + // Ensure there a no dangling comments for a node if the node is present + debug_assert!(lower.is_none() || dangling_lower_comments.is_empty()); + debug_assert!(upper.is_none() || dangling_upper_comments.is_empty()); + debug_assert!(step.is_none() || dangling_step_comments.is_empty()); + + // Handle spacing around the colon(s) + // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices + let lower_simple = lower.as_ref().map_or(true, |expr| is_simple_expr(expr)); + let upper_simple = upper.as_ref().map_or(true, |expr| is_simple_expr(expr)); + let step_simple = step.as_ref().map_or(true, |expr| is_simple_expr(expr)); + let all_simple = lower_simple && upper_simple && step_simple; + + // lower + if let Some(lower) = lower { + write!(f, [lower.format(), line_suffix_boundary()])?; + } else { + dangling_comments(dangling_lower_comments).fmt(f)?; + } + + // First colon + // The spacing after the colon depends on both the lhs and the rhs: + // ``` + // e00 = x[:] + // e01 = x[:1] + // e02 = x[: a()] + // e10 = x[1:] + // e11 = x[1:1] + // e12 = x[1 : a()] + // e20 = x[a() :] + // e21 = x[a() : 1] + // e22 = x[a() : a()] + // e200 = "e"[a() : :] + // e201 = "e"[a() :: 1] + // e202 = "e"[a() :: a()] + // ``` + if !all_simple { + space().fmt(f)?; + } + text(":").fmt(f)?; + // No upper node, no need for a space, e.g. `x[a() :]` + if !all_simple && upper.is_some() { + space().fmt(f)?; + } + + // Upper + if let Some(upper) = upper { + let upper_leading_comments = comments.leading_comments(upper.as_ref()); + leading_comments_spacing(f, upper_leading_comments)?; + write!(f, [upper.format(), line_suffix_boundary()])?; + } else { + if let Some(first) = dangling_upper_comments.first() { + // Here the spacing for end-of-line comments works but own line comments need + // explicit spacing + if first.line_position().is_own_line() { + hard_line_break().fmt(f)?; + } + } + dangling_comments(dangling_upper_comments).fmt(f)?; + } + + // (optionally) step + if second_colon.is_some() { + // Same spacing rules as for the first colon, except for the strange case when the + // second colon exists, but neither upper nor step + // ``` + // e200 = "e"[a() : :] + // e201 = "e"[a() :: 1] + // e202 = "e"[a() :: a()] + // ``` + if !all_simple && (upper.is_some() || step.is_none()) { + space().fmt(f)?; + } + text(":").fmt(f)?; + // No step node, no need for a space + if !all_simple && step.is_some() { + space().fmt(f)?; + } + if let Some(step) = step { + let step_leading_comments = comments.leading_comments(step.as_ref()); + leading_comments_spacing(f, step_leading_comments)?; + step.format().fmt(f)?; + } else { + if !dangling_step_comments.is_empty() { + // Put the colon and comments on their own lines + write!( + f, + [hard_line_break(), dangling_comments(dangling_step_comments)] + )?; + } + } + } else { + debug_assert!(step.is_none(), "step can't exist without a second colon"); + } + Ok(()) } } +/// We're in a slice, so we know there's a first colon, but with have to look into the source +/// to find out whether there is a second one, too, e.g. `[1:2]` and `[1:10:2]`. +/// +/// Returns the first and optionally the second colon. +pub(crate) fn find_colons( + contents: &str, + range: TextRange, + lower: &Option>, + upper: &Option>, +) -> FormatResult<(Token, Option)> { + let after_lower = lower + .as_ref() + .map_or(range.start(), |lower| lower.range().end()); + let first_colon = + first_non_trivia_token(after_lower, contents).ok_or(FormatError::SyntaxError)?; + if first_colon.kind != TokenKind::Colon { + return Err(FormatError::SyntaxError); + } + + let after_upper = upper + .as_ref() + .map_or(first_colon.end(), |upper| upper.range().end()); + // At least the closing bracket must exist, so there must be a token there + let next_token = + first_non_trivia_token(after_upper, contents).ok_or(FormatError::SyntaxError)?; + let second_colon = if next_token.kind == TokenKind::Colon { + debug_assert!( + next_token.range.start() < range.end(), + "The next token in a slice must either be a colon or the closing bracket" + ); + Some(next_token) + } else { + None + }; + Ok((first_colon, second_colon)) +} + +/// Determines whether this expression needs a space around the colon +/// +fn is_simple_expr(expr: &Expr) -> bool { + matches!(expr, Expr::Constant(_) | Expr::Name(_)) +} + +pub(crate) enum ExprSliceCommentSection { + Lower, + Upper, + Step, +} + +/// Assigns a comment to lower/upper/step in `[lower:upper:step]`. +/// +/// ```python +/// "sliceable"[ +/// # lower comment +/// : +/// # upper comment +/// : +/// # step comment +/// ] +/// ``` +pub(crate) fn assign_comment_in_slice( + comment: TextRange, + contents: &str, + expr_slice: &ExprSlice, +) -> ExprSliceCommentSection { + let ExprSlice { + range, + lower, + upper, + step: _, + } = expr_slice; + + let (first_colon, second_colon) = find_colons(contents, *range, lower, upper) + .expect("SyntaxError when trying to parse slice"); + + if comment.start() < first_colon.range.start() { + ExprSliceCommentSection::Lower + } else { + // We are to the right of the first colon + if let Some(second_colon) = second_colon { + if comment.start() < second_colon.range.start() { + ExprSliceCommentSection::Upper + } else { + ExprSliceCommentSection::Step + } + } else { + // No second colon means there is no step + ExprSliceCommentSection::Upper + } + } +} + +/// Manual spacing for the leading comments of upper and step +fn leading_comments_spacing( + f: &mut PyFormatter, + leading_comments: &[SourceComment], +) -> FormatResult<()> { + if let Some(first) = leading_comments.first() { + if first.line_position().is_own_line() { + // Insert a newline after the colon so the comment ends up on its own line + hard_line_break().fmt(f)?; + } else { + // Insert the two spaces between the colon and the end-of-line comment after the colon + write!(f, [space(), space()])?; + } + } + Ok(()) +} + impl NeedsParentheses for ExprSlice { fn needs_parentheses( &self, diff --git a/crates/ruff_python_formatter/src/expression/expr_subscript.rs b/crates/ruff_python_formatter/src/expression/expr_subscript.rs index 6874b1415a..55f8d22490 100644 --- a/crates/ruff_python_formatter/src/expression/expr_subscript.rs +++ b/crates/ruff_python_formatter/src/expression/expr_subscript.rs @@ -1,24 +1,52 @@ +use crate::comments::{trailing_comments, Comments}; use crate::expression::parentheses::{ default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, }; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; - -use crate::comments::Comments; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use ruff_formatter::prelude::{group, soft_block_indent, text}; +use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use ruff_python_ast::node::AstNode; use rustpython_parser::ast::ExprSubscript; #[derive(Default)] pub struct FormatExprSubscript; impl FormatNodeRule for FormatExprSubscript { - fn fmt_fields(&self, _item: &ExprSubscript, f: &mut PyFormatter) -> FormatResult<()> { + fn fmt_fields(&self, item: &ExprSubscript, f: &mut PyFormatter) -> FormatResult<()> { + let ExprSubscript { + range: _, + value, + slice, + ctx: _, + } = item; + + let comments = f.context().comments().clone(); + let dangling_comments = comments.dangling_comments(item.as_any_node_ref()); + debug_assert!( + dangling_comments.len() <= 1, + "The subscript expression must have at most a single comment, the one after the bracket" + ); + write!( f, - [not_yet_implemented_custom_text( - "NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]" - )] + [group(&format_args![ + value.format(), + text("["), + trailing_comments(dangling_comments), + soft_block_indent(&slice.format()), + text("]") + ])] ) } + + fn fmt_dangling_comments( + &self, + _node: &ExprSubscript, + _f: &mut PyFormatter, + ) -> FormatResult<()> { + // Handled inside of `fmt_fields` + Ok(()) + } } impl NeedsParentheses for ExprSubscript { @@ -28,6 +56,9 @@ impl NeedsParentheses for ExprSubscript { source: &str, comments: &Comments, ) -> Parentheses { - default_expression_needs_parentheses(self.into(), parenthesize, source, comments) + match default_expression_needs_parentheses(self.into(), parenthesize, source, comments) { + Parentheses::Optional => Parentheses::Never, + parentheses => parentheses, + } } } diff --git a/crates/ruff_python_formatter/src/expression/expr_unary_op.rs b/crates/ruff_python_formatter/src/expression/expr_unary_op.rs index 04151760fa..e55a2db81a 100644 --- a/crates/ruff_python_formatter/src/expression/expr_unary_op.rs +++ b/crates/ruff_python_formatter/src/expression/expr_unary_op.rs @@ -2,10 +2,10 @@ use crate::comments::{trailing_comments, Comments}; use crate::expression::parentheses::{ default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, }; -use crate::prelude::*; use crate::trivia::{SimpleTokenizer, TokenKind}; -use crate::FormatNodeRule; -use ruff_formatter::FormatContext; +use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use ruff_formatter::prelude::{hard_line_break, space, text}; +use ruff_formatter::{Format, FormatContext, FormatResult}; use ruff_python_ast::prelude::UnaryOp; use ruff_text_size::{TextLen, TextRange}; use rustpython_parser::ast::{ExprUnaryOp, Ranged}; diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 3f09fd51d6..ec332985a3 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -273,36 +273,7 @@ if True: let formatted_code = printed.as_code(); - let reformatted = match format_module(formatted_code) { - Ok(reformatted) => reformatted, - Err(err) => { - panic!( - "Expected formatted code to be valid syntax: {err}:\ - \n---\n{formatted_code}---\n", - ); - } - }; - - if reformatted.as_code() != formatted_code { - let diff = TextDiff::from_lines(formatted_code, reformatted.as_code()) - .unified_diff() - .header("Formatted once", "Formatted twice") - .to_string(); - panic!( - r#"Reformatting the formatted code a second time resulted in formatting changes. ---- -{diff}--- - -Formatted once: ---- -{formatted_code}--- - -Formatted twice: ---- -{}---"#, - reformatted.as_code() - ); - } + ensure_stability_when_formatting_twice(formatted_code); if formatted_code == expected_output { // Black and Ruff formatting matches. Delete any existing snapshot files because the Black output @@ -374,6 +345,8 @@ Formatted twice: let reformatted = 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}")); + ensure_stability_when_formatting_twice(formatted_code); + if reformatted.as_code() != formatted_code { let diff = TextDiff::from_lines(formatted_code, reformatted.as_code()) .unified_diff() @@ -406,6 +379,40 @@ Formatted twice: Ok(()) } + /// Format another time and make sure that there are no changes anymore + fn ensure_stability_when_formatting_twice(formatted_code: &str) { + let reformatted = match format_module(formatted_code) { + Ok(reformatted) => reformatted, + Err(err) => { + panic!( + "Expected formatted code to be valid syntax: {err}:\ + \n---\n{formatted_code}---\n", + ); + } + }; + + if reformatted.as_code() != formatted_code { + let diff = TextDiff::from_lines(formatted_code, reformatted.as_code()) + .unified_diff() + .header("Formatted once", "Formatted twice") + .to_string(); + panic!( + r#"Reformatting the formatted code a second time resulted in formatting changes. +--- +{diff}--- + +Formatted once: +--- +{formatted_code}--- + +Formatted twice: +--- +{}---"#, + reformatted.as_code() + ); + } + } + /// Use this test to debug the formatting of some snipped #[ignore] #[test] diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap index c736be6242..da19ad12f5 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap @@ -73,9 +73,8 @@ y = 100(no) +if 10 .NOT_IMPLEMENTED_attr: ... --y = 100[no] + y = 100[no] -y = 100(no) -+y = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +y = NOT_IMPLEMENTED_call() ``` @@ -102,7 +101,7 @@ x = -100.0000J if 10 .NOT_IMPLEMENTED_attr: ... -y = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +y = 100[no] y = NOT_IMPLEMENTED_call() ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap index 3cd87b7335..db67f3dc39 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap @@ -264,32 +264,30 @@ instruction()#comment with bad spacing if typedargslist: - parameters.children = [children[0], body, children[-1]] # (1 # )1 - parameters.children = [ -- children[0], + parameters.NOT_IMPLEMENTED_attr = [ -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (1 - body, -- children[-1], # type: ignore -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )1 ++ children[0], # (1 ++ body, ++ children[-1], # )1 + ] + parameters.NOT_IMPLEMENTED_attr = [ -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ body, -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: ignore + children[0], + body, + children[-1], # type: ignore ] else: - parameters.children = [ - parameters.children[0], # (2 what if this was actually long + parameters.NOT_IMPLEMENTED_attr = [ -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (2 what if this was actually long ++ parameters.NOT_IMPLEMENTED_attr[0], # (2 what if this was actually long body, - parameters.children[-1], # )2 -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )2 ++ parameters.NOT_IMPLEMENTED_attr[-1], # )2 ] - parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore + parameters.NOT_IMPLEMENTED_attr = [ -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], ++ parameters.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr[0], + body, -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], ++ parameters.NOT_IMPLEMENTED_attr[-1], + ] # type: ignore if ( - self._proc is not None @@ -460,25 +458,25 @@ else: def inline_comments_in_brackets_ruin_everything(): if typedargslist: parameters.NOT_IMPLEMENTED_attr = [ - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (1 + children[0], # (1 body, - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )1 + children[-1], # )1 ] parameters.NOT_IMPLEMENTED_attr = [ - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], + children[0], body, - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: ignore + children[-1], # type: ignore ] else: parameters.NOT_IMPLEMENTED_attr = [ - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # (2 what if this was actually long + parameters.NOT_IMPLEMENTED_attr[0], # (2 what if this was actually long body, - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # )2 + parameters.NOT_IMPLEMENTED_attr[-1], # )2 ] parameters.NOT_IMPLEMENTED_attr = [ - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], + parameters.NOT_IMPLEMENTED_attr.NOT_IMPLEMENTED_attr[0], body, - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], + parameters.NOT_IMPLEMENTED_attr[-1], ] # type: ignore if ( NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap index 2f25cc478b..10302ecac7 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap @@ -153,12 +153,9 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite tup = ( another_element, -@@ -84,35 +85,22 @@ - - +@@ -86,33 +87,20 @@ def func( -- a=some_list[0], # type: int -+ a=NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: int + a=some_list[0], # type: int ): # type: () -> int - c = call( - 0.0123, @@ -293,7 +290,7 @@ def f( def func( - a=NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], # type: int + a=some_list[0], # type: int ): # type: () -> int c = NOT_IMPLEMENTED_call() diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap index 1bc96c10d0..772e6104cb 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap @@ -276,7 +276,7 @@ last_call() Name None True -@@ -30,203 +31,178 @@ +@@ -30,134 +31,120 @@ -1 ~int and not v1 ^ 123 + v2 | True (~int) and (not ((v1 ^ (123 + v2)) | True)) @@ -297,13 +297,6 @@ 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) --) --{"2.7": dead, "3.7": (long_live or die_hard)} --{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} ++really ** -confusing ** ~operator**-precedence +flags & ~select.NOT_IMPLEMENTED_attr and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right +lambda x: True @@ -328,9 +321,7 @@ last_call() + "NOT_YET_IMPLEMENTED_STRING": (long_live or die_hard), + **{"NOT_YET_IMPLEMENTED_STRING": verygood}, +} - {**a, **b, **c} --{"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} --({"a": "b"}, (True or False), (+value), "string", b"bytes") or None ++{**a, **b, **c} +{ + "NOT_YET_IMPLEMENTED_STRING", + "NOT_YET_IMPLEMENTED_STRING", @@ -339,7 +330,16 @@ last_call() + "NOT_YET_IMPLEMENTED_STRING", + (NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false), +} -+( + ( +- (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} +-{"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} +-({"a": "b"}, (True or False), (+value), "string", b"bytes") or None + {"NOT_YET_IMPLEMENTED_STRING": "NOT_YET_IMPLEMENTED_STRING"}, + (True or False), + (+value), @@ -352,7 +352,12 @@ last_call() (1, 2, 3) [] [1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)] --[ ++[1, 2, 3] ++[NOT_YET_IMPLEMENTED_ExprStarred] ++[NOT_YET_IMPLEMENTED_ExprStarred] ++[NOT_YET_IMPLEMENTED_ExprStarred, 4, 5] ++[4, NOT_YET_IMPLEMENTED_ExprStarred, 5] + [ - 1, - 2, - 3, @@ -369,12 +374,7 @@ last_call() - *a, - 5, -] -+[1, 2, 3] -+[NOT_YET_IMPLEMENTED_ExprStarred] -+[NOT_YET_IMPLEMENTED_ExprStarred] -+[NOT_YET_IMPLEMENTED_ExprStarred, 4, 5] -+[4, NOT_YET_IMPLEMENTED_ExprStarred, 5] - [ +-[ this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, @@ -393,6 +393,33 @@ last_call() -{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} -{a: b * 2 for a, b in dictionary.items()} -{a: b * -2 for a, b in dictionary.items()} +-{ +- k: v +- for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension +-} +-Python3 > Python2 > COBOL +-Life is Life +-call() +-call(arg) +-call(kwarg="hey") +-call(arg, kwarg="hey") +-call(arg, another, kwarg="hey", **kwargs) +-call( +- this_is_a_very_long_variable_which_will_force_a_delimiter_split, +- arg, +- another, +- kwarg="hey", +- **kwargs, +-) # note: no trailing comma pre-3.6 +-call(*gidgets[:2]) +-call(a, *gidgets[:2]) +-call(**self.screen_kwargs) +-call(b, **self.screen_kwargs) +-lukasz.langa.pl +-call.me(maybe) +-(1).real +-(1.0).real +-....__class__ +NOT_YET_IMPLEMENTED_ExprSetComp +NOT_YET_IMPLEMENTED_ExprSetComp +NOT_YET_IMPLEMENTED_ExprSetComp @@ -423,81 +450,22 @@ last_call() +1 .NOT_IMPLEMENTED_attr +1.0 .NOT_IMPLEMENTED_attr +....NOT_IMPLEMENTED_attr -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_YET_IMPLEMENTED_StmtAnnAssign -+NOT_YET_IMPLEMENTED_StmtAnnAssign -+NOT_YET_IMPLEMENTED_StmtAnnAssign -+NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false - { -- k: v -- for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension -+ "NOT_YET_IMPLEMENTED_STRING": dead, -+ "NOT_YET_IMPLEMENTED_STRING": long_live or die_hard, - } --Python3 > Python2 > COBOL --Life is Life --call() --call(arg) --call(kwarg="hey") --call(arg, kwarg="hey") --call(arg, another, kwarg="hey", **kwargs) --call( -- this_is_a_very_long_variable_which_will_force_a_delimiter_split, -- arg, -- another, -- kwarg="hey", -- **kwargs, --) # note: no trailing comma pre-3.6 --call(*gidgets[:2]) --call(a, *gidgets[:2]) --call(**self.screen_kwargs) --call(b, **self.screen_kwargs) --lukasz.langa.pl --call.me(maybe) --(1).real --(1.0).real --....__class__ --list[str] --dict[str, int] --tuple[str, ...] --tuple[str, int, float, dict[str, int]] --tuple[ + list[str] + dict[str, int] + tuple[str, ...] + tuple[str, int, float, dict[str, int]] + tuple[ - str, - int, - float, - dict[str, int], --] ++ ( ++ str, ++ int, ++ float, ++ dict[str, int], ++ ) + ] -very_long_variable_name_filters: t.List[ - t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], -] @@ -510,35 +478,43 @@ last_call() -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -) # type: ignore --slice[0] --slice[0:1] --slice[0:1:2] --slice[:] ++NOT_YET_IMPLEMENTED_StmtAnnAssign ++NOT_YET_IMPLEMENTED_StmtAnnAssign ++NOT_YET_IMPLEMENTED_StmtAnnAssign ++NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore + slice[0] + slice[0:1] + slice[0:1:2] + slice[:] -slice[:-1] --slice[1:] ++slice[ : -1] + slice[1:] -slice[::-1] --slice[d :: d + 1] --slice[:c, c - 1] --numpy[:, 0:1] ++slice[ :: -1] + slice[d :: d + 1] + slice[:c, c - 1] + numpy[:, 0:1] -numpy[:, :-1] --numpy[0, :] --numpy[:, i] --numpy[0, :2] --numpy[:N, 0] --numpy[:2, :4] --numpy[2:4, 1:5] --numpy[4:, 2:] --numpy[:, (0, 1, 2, 5)] --numpy[0, [0]] --numpy[:, [i]] --numpy[1 : c + 1, c] --numpy[-(c + 1) :, d] --numpy[:, l[-2]] ++numpy[:, : -1] + numpy[0, :] + numpy[:, i] + numpy[0, :2] +@@ -171,62 +158,58 @@ + numpy[1 : c + 1, c] + numpy[-(c + 1) :, d] + numpy[:, l[-2]] -numpy[:, ::-1] -numpy[np.newaxis, :] -(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) -{"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"} ++numpy[:, :: -1] ++numpy[np.NOT_IMPLEMENTED_attr, :] ++NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false ++{ ++ "NOT_YET_IMPLEMENTED_STRING": dead, ++ "NOT_YET_IMPLEMENTED_STRING": long_live or die_hard, ++} +{ + "NOT_YET_IMPLEMENTED_STRING", + "NOT_YET_IMPLEMENTED_STRING", @@ -585,7 +561,13 @@ last_call() -g = 1, *"ten" -what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set( - vars_to_remove --) ++e = NOT_IMPLEMENTED_call() ++f = 1, NOT_YET_IMPLEMENTED_ExprStarred ++g = 1, NOT_YET_IMPLEMENTED_ExprStarred ++what_is_up_with_those_new_coord_names = ( ++ (coord_names + NOT_IMPLEMENTED_call()) ++ + NOT_IMPLEMENTED_call() + ) -what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( - vars_to_remove -) @@ -596,13 +578,7 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() -+e = NOT_IMPLEMENTED_call() -+f = 1, NOT_YET_IMPLEMENTED_ExprStarred -+g = 1, NOT_YET_IMPLEMENTED_ExprStarred -+what_is_up_with_those_new_coord_names = ( -+ (coord_names + NOT_IMPLEMENTED_call()) -+ + NOT_IMPLEMENTED_call() - ) +-) -result = ( - session.query(models.Customer.id) - .filter( @@ -625,7 +601,7 @@ last_call() mapping = { A: 0.25 * (10.0 / 12), B: 0.1 * (10.0 / 12), -@@ -236,64 +212,38 @@ +@@ -236,64 +219,38 @@ def gen(): @@ -714,7 +690,7 @@ last_call() ): return True if ( -@@ -327,24 +277,44 @@ +@@ -327,24 +284,44 @@ ): return True if ( @@ -771,7 +747,7 @@ last_call() ): return True ( -@@ -363,8 +333,9 @@ +@@ -363,8 +340,9 @@ bbbb >> bbbb * bbbb ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -908,41 +884,48 @@ NOT_IMPLEMENTED_call() 1 .NOT_IMPLEMENTED_attr 1.0 .NOT_IMPLEMENTED_attr ....NOT_IMPLEMENTED_attr -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +list[str] +dict[str, int] +tuple[str, ...] +tuple[str, int, float, dict[str, int]] +tuple[ + ( + str, + int, + float, + dict[str, int], + ) +] NOT_YET_IMPLEMENTED_StmtAnnAssign NOT_YET_IMPLEMENTED_StmtAnnAssign NOT_YET_IMPLEMENTED_StmtAnnAssign NOT_YET_IMPLEMENTED_StmtAnnAssign # type: ignore -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +slice[0] +slice[0:1] +slice[0:1:2] +slice[:] +slice[ : -1] +slice[1:] +slice[ :: -1] +slice[d :: d + 1] +slice[:c, c - 1] +numpy[:, 0:1] +numpy[:, : -1] +numpy[0, :] +numpy[:, i] +numpy[0, :2] +numpy[:N, 0] +numpy[:2, :4] +numpy[2:4, 1:5] +numpy[4:, 2:] +numpy[:, (0, 1, 2, 5)] +numpy[0, [0]] +numpy[:, [i]] +numpy[1 : c + 1, c] +numpy[-(c + 1) :, d] +numpy[:, l[-2]] +numpy[:, :: -1] +numpy[np.NOT_IMPLEMENTED_attr, :] NOT_IMPLEMENTED_true if NOT_IMPLEMENTED_cond else NOT_IMPLEMENTED_false { "NOT_YET_IMPLEMENTED_STRING": dead, diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap index dc725ba267..b1786e90a8 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap @@ -273,7 +273,7 @@ d={'a':1, + debug: bool = False, + **kwargs, +) -> str: -+ return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ return text[number : -1] + + # fmt: on @@ -296,7 +296,7 @@ d={'a':1, def spaces_types( -@@ -51,76 +70,61 @@ +@@ -51,76 +70,71 @@ d: dict = {}, e: bool = True, f: int = -1, @@ -323,15 +323,22 @@ d={'a':1, def subscriptlist(): -- atom[ -- # fmt: off + atom[ + # fmt: off - 'some big and', - 'complex subscript', - # fmt: on - goes + here, - andhere, -- ] -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ ( ++ "NOT_YET_IMPLEMENTED_STRING", ++ "NOT_YET_IMPLEMENTED_STRING", ++ # fmt: on ++ goes ++ + here, ++ andhere, ++ ) + ] def import_as_names(): @@ -390,7 +397,7 @@ d={'a':1, # fmt: off # hey, that won't work -@@ -130,13 +134,15 @@ +@@ -130,13 +144,15 @@ def on_and_off_broken(): @@ -411,7 +418,7 @@ d={'a':1, # fmt: on # fmt: off # ...but comments still get reformatted even though they should not be -@@ -145,80 +151,21 @@ +@@ -145,80 +161,21 @@ def long_lines(): if True: @@ -551,7 +558,7 @@ def function_signature_stress_test( debug: bool = False, **kwargs, ) -> str: - return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + return text[number : -1] # fmt: on @@ -595,7 +602,17 @@ something = { def subscriptlist(): - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + atom[ + # fmt: off + ( + "NOT_YET_IMPLEMENTED_STRING", + "NOT_YET_IMPLEMENTED_STRING", + # fmt: on + goes + + here, + andhere, + ) + ] def import_as_names(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap index a601ceda63..25404fa93b 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap @@ -167,7 +167,7 @@ def __await__(): return (yield) **kwargs, ) -> str: - return text[number:-1] -+ return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ return text[number : -1] -def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): @@ -330,7 +330,7 @@ def function_signature_stress_test( debug: bool = False, **kwargs, ) -> str: - return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + return text[number : -1] def spaces( diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap index 89e72cdcc0..f0fb15c471 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap @@ -94,7 +94,7 @@ some_module.some_function( } tup = ( 1, -@@ -24,45 +24,18 @@ +@@ -24,45 +24,23 @@ def f( a: int = 1, ): @@ -106,7 +106,9 @@ some_module.some_function( - call2( - arg=[1, 2, 3], - ) -- x = { ++ NOT_IMPLEMENTED_call() ++ NOT_IMPLEMENTED_call() + x = { - "a": 1, - "b": 2, - }["a"] @@ -123,9 +125,11 @@ some_module.some_function( - "h": 8, - }["a"] - ): -+ NOT_IMPLEMENTED_call() -+ NOT_IMPLEMENTED_call() -+ x = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ "NOT_YET_IMPLEMENTED_STRING": 1, ++ "NOT_YET_IMPLEMENTED_STRING": 2, ++ }[ ++ "NOT_YET_IMPLEMENTED_STRING" ++ ] + if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: pass @@ -133,7 +137,7 @@ some_module.some_function( -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] -): -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: ++def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set["NOT_YET_IMPLEMENTED_STRING"]: json = { - "k": { - "k2": { @@ -148,7 +152,7 @@ some_module.some_function( } -@@ -80,35 +53,16 @@ +@@ -80,35 +58,16 @@ pass @@ -221,12 +225,17 @@ def f( ): NOT_IMPLEMENTED_call() NOT_IMPLEMENTED_call() - x = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + x = { + "NOT_YET_IMPLEMENTED_STRING": 1, + "NOT_YET_IMPLEMENTED_STRING": 2, + }[ + "NOT_YET_IMPLEMENTED_STRING" + ] if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: pass -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set["NOT_YET_IMPLEMENTED_STRING"]: json = { "NOT_YET_IMPLEMENTED_STRING": { "NOT_YET_IMPLEMENTED_STRING": {"NOT_YET_IMPLEMENTED_STRING": [1]}, diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap index b52d1de815..66e37c3bad 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap @@ -25,25 +25,28 @@ list_of_types = [tuple[int,],] ```diff --- Black +++ Ruff -@@ -1,22 +1,12 @@ +@@ -1,22 +1,17 @@ # We should not treat the trailing comma # in a single-element subscript. -a: tuple[int,] -b = tuple[int,] +NOT_YET_IMPLEMENTED_StmtAnnAssign -+b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++b = tuple[(int,)] # The magic comma still applies to multi-element subscripts. -c: tuple[ - int, - int, -] --d = tuple[ -- int, -- int, --] +NOT_YET_IMPLEMENTED_StmtAnnAssign -+d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + d = tuple[ +- int, +- int, ++ ( ++ int, ++ int, ++ ) + ] # Magic commas still work as expected for non-subscripts. -small_list = [ @@ -53,7 +56,7 @@ list_of_types = [tuple[int,],] - tuple[int,], -] +small_list = [1] -+list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]] ++list_of_types = [tuple[(int,)]] ``` ## Ruff Output @@ -62,15 +65,20 @@ list_of_types = [tuple[int,],] # We should not treat the trailing comma # in a single-element subscript. NOT_YET_IMPLEMENTED_StmtAnnAssign -b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +b = tuple[(int,)] # The magic comma still applies to multi-element subscripts. NOT_YET_IMPLEMENTED_StmtAnnAssign -d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +d = tuple[ + ( + int, + int, + ) +] # Magic commas still work as expected for non-subscripts. small_list = [1] -list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]] +list_of_types = [tuple[(int,)]] ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap index e3b6cde88b..3ccecffed8 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap @@ -92,7 +92,7 @@ return np.divide( -j = super().name ** 5 -k = [(2**idx, value) for idx, value in pairs] -l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) -+d = 5 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++d = 5 ** f["NOT_YET_IMPLEMENTED_STRING"] +e = NOT_IMPLEMENTED_call() +f = NOT_IMPLEMENTED_call() ** 5 +g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr @@ -125,7 +125,7 @@ return np.divide( -j = super().name ** 5.0 -k = [(2.0**idx, value) for idx, value in pairs] -l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) -+d = 5.0 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++d = 5.0 ** f["NOT_YET_IMPLEMENTED_STRING"] +e = NOT_IMPLEMENTED_call() +f = NOT_IMPLEMENTED_call() ** 5.0 +g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr @@ -181,7 +181,7 @@ def function_dont_replace_spaces(): a = 5**~4 b = 5 ** NOT_IMPLEMENTED_call() c = -(5**2) -d = 5 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +d = 5 ** f["NOT_YET_IMPLEMENTED_STRING"] e = NOT_IMPLEMENTED_call() f = NOT_IMPLEMENTED_call() ** 5 g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr @@ -200,7 +200,7 @@ r = x**y a = 5.0**~4.0 b = 5.0 ** NOT_IMPLEMENTED_call() c = -(5.0**2.0) -d = 5.0 ** NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +d = 5.0 ** f["NOT_YET_IMPLEMENTED_STRING"] e = NOT_IMPLEMENTED_call() f = NOT_IMPLEMENTED_call() ** 5.0 g = a.NOT_IMPLEMENTED_attr**c.NOT_IMPLEMENTED_attr diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap index bdc722edb5..89478d9b1f 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap @@ -40,7 +40,7 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx -xxxxxxxxx_yyy_zzzzzzzz[ - xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) -] = 1 -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] = 1 ++xxxxxxxxx_yyy_zzzzzzzz[NOT_IMPLEMENTED_call(), NOT_IMPLEMENTED_call()] = 1 ``` ## Ruff Output @@ -61,7 +61,7 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx # Make when when the left side of assignment plus the opening paren "... = (" is # exactly line length limit + 1, it won't be split like that. -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] = 1 +xxxxxxxxx_yyy_zzzzzzzz[NOT_IMPLEMENTED_call(), NOT_IMPLEMENTED_call()] = 1 ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap index 404098c431..db50309690 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap @@ -123,35 +123,36 @@ def foo() -> tuple[int, int, int,]: return 2 -@@ -95,26 +99,14 @@ - - - # Return type with commas --def foo() -> tuple[int, int, int]: -+def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: +@@ -99,22 +103,22 @@ return 2 -def foo() -> ( - tuple[ -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, ++def foo() -> tuple[ ++ ( + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - ] -): -+def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: ++ ) ++]: return 2 # Magic trailing comma example -def foo() -> ( - tuple[ -- int, -- int, -- int, ++def foo() -> tuple[ ++ ( + int, + int, + int, - ] -): -+def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: ++ ) ++]: return 2 ``` @@ -259,16 +260,28 @@ def foo() -> ( # Return type with commas -def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: +def foo() -> tuple[int, int, int]: return 2 -def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: +def foo() -> tuple[ + ( + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + ) +]: return 2 # Magic trailing comma example -def foo() -> NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]: +def foo() -> tuple[ + ( + int, + int, + int, + ) +]: return 2 ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap index 0ca6ef0a1f..5a86ffbc95 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap @@ -60,26 +60,31 @@ func( ```diff --- Black +++ Ruff -@@ -1,25 +1,30 @@ +@@ -1,25 +1,35 @@ # We should not remove the trailing comma in a single-element subscript. -a: tuple[int,] -b = tuple[int,] +NOT_YET_IMPLEMENTED_StmtAnnAssign -+b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++b = tuple[(int,)] # But commas in multiple element subscripts should be removed. -c: tuple[int, int] -d = tuple[int, int] +NOT_YET_IMPLEMENTED_StmtAnnAssign -+d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++d = tuple[ ++ ( ++ int, ++ int, ++ ) ++] # Remove commas for non-subscripts. small_list = [1] -list_of_types = [tuple[int,]] -+list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]] ++list_of_types = [tuple[(int,)]] small_set = {1} -set_of_types = {tuple[int,]} -+set_of_types = {NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]} ++set_of_types = {tuple[(int,)]} # Except single element tuples small_tuple = (1,) @@ -108,17 +113,22 @@ func( ```py # We should not remove the trailing comma in a single-element subscript. NOT_YET_IMPLEMENTED_StmtAnnAssign -b = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +b = tuple[(int,)] # But commas in multiple element subscripts should be removed. NOT_YET_IMPLEMENTED_StmtAnnAssign -d = NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +d = tuple[ + ( + int, + int, + ) +] # Remove commas for non-subscripts. small_list = [1] -list_of_types = [NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]] +list_of_types = [tuple[(int,)]] small_set = {1} -set_of_types = {NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key]} +set_of_types = {tuple[(int,)]} # Except single element tuples small_tuple = (1,) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap index 1d2e046b96..808ecffe6b 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap @@ -76,158 +76,137 @@ x[ ```diff --- Black +++ Ruff -@@ -1,59 +1,48 @@ +@@ -1,33 +1,33 @@ -slice[a.b : c.d] --slice[d :: d + 1] --slice[d + 1 :: d] --slice[d::d] --slice[0] --slice[-1] ++slice[a.NOT_IMPLEMENTED_attr : c.NOT_IMPLEMENTED_attr] + slice[d :: d + 1] + slice[d + 1 :: d] + slice[d::d] + slice[0] + slice[-1] -slice[:-1] -slice[::-1] --slice[:c, c - 1] --slice[c, c + 1, d::] --slice[ham[c::d] :: 1] --slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] ++slice[ : -1] ++slice[ :: -1] + slice[:c, c - 1] + slice[c, c + 1, d::] + slice[ham[c::d] :: 1] + slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] -slice[:-1:] -slice[lambda: None : lambda: None] -slice[lambda x, y, *args, really=2, **kwargs: None :, None::] --slice[1 or 2 : True and False] ++slice[ : -1 :] ++slice[lambda x: True : lambda x: True] ++slice[lambda x: True :, None::] + slice[1 or 2 : True and False] -slice[not so_simple : 1 < val <= 10] -slice[(1 for i in range(42)) : x] -slice[:: [i for i in range(42)]] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++slice[not so_simple : NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right] ++slice[(i for i in []) : x] ++slice[ :: [i for i in []]] async def f(): - slice[await x : [i async for i in arange(42)] : 42] -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ slice[await x : [i for i in []] : 42] # These are from PEP-8: --ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] --ham[lower:upper], ham[lower:upper:], ham[lower::step] -+( -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+) -+( -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+ NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -+) + ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] + ham[lower:upper], ham[lower:upper:], ham[lower::step] # ham[lower+offset : upper+offset] -ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] --ham[lower + offset : upper + offset] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ham[ : NOT_IMPLEMENTED_call() : NOT_IMPLEMENTED_call()], ham[ :: NOT_IMPLEMENTED_call()] + ham[lower + offset : upper + offset] --slice[::, ::] --slice[ -- # A -- : -- # B -- : -- # C --] --slice[ -- # A -- 1: -- # B -- 2: -- # C -- 3 --] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] - --slice[ -- # A -- 1 + slice[::, ::] +@@ -50,10 +50,14 @@ + slice[ + # A + 1 - + 2 : -- # B ++ + 2 : + # B - 3 : -- # C -- 4 --] ++ 3 : + # C + 4 + ] -x[1:2:3] # A # B # C -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -+NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++x[ ++ 1: # A ++ 2: # B ++ 3 # C ++] ``` ## Ruff Output ```py -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +slice[a.NOT_IMPLEMENTED_attr : c.NOT_IMPLEMENTED_attr] +slice[d :: d + 1] +slice[d + 1 :: d] +slice[d::d] +slice[0] +slice[-1] +slice[ : -1] +slice[ :: -1] +slice[:c, c - 1] +slice[c, c + 1, d::] +slice[ham[c::d] :: 1] +slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] +slice[ : -1 :] +slice[lambda x: True : lambda x: True] +slice[lambda x: True :, None::] +slice[1 or 2 : True and False] +slice[not so_simple : NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right] +slice[(i for i in []) : x] +slice[ :: [i for i in []]] async def f(): - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + slice[await x : [i for i in []] : 42] # These are from PEP-8: -( - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -) -( - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], - NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], -) +ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] +ham[lower:upper], ham[lower:upper:], ham[lower::step] # ham[lower+offset : upper+offset] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key], NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +ham[ : NOT_IMPLEMENTED_call() : NOT_IMPLEMENTED_call()], ham[ :: NOT_IMPLEMENTED_call()] +ham[lower + offset : upper + offset] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +slice[::, ::] +slice[ + # A + : + # B + : + # C +] +slice[ + # A + 1: + # B + 2: + # C + 3 +] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] -NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] +slice[ + # A + 1 + + 2 : + # B + 3 : + # C + 4 +] +x[ + 1: # A + 2: # B + 3 # C +] ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap index fbbed48d6e..e2b670003f 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap @@ -46,7 +46,7 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( ```diff --- Black +++ Ruff -@@ -1,50 +1,26 @@ +@@ -1,50 +1,30 @@ -zero( - one, -).two( @@ -86,7 +86,11 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( - }, - api_key=api_key, - )["extensions"]["sdk"]["token"] -+ return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] ++ return NOT_IMPLEMENTED_call()["NOT_YET_IMPLEMENTED_STRING"][ ++ "NOT_YET_IMPLEMENTED_STRING" ++ ][ ++ "NOT_YET_IMPLEMENTED_STRING" ++ ] # Edge case where a bug in a working-in-progress version of @@ -126,7 +130,11 @@ NOT_IMPLEMENTED_call() # Example from https://github.com/psf/black/issues/3229 def refresh_token(self, device_family, refresh_token, api_key): - return NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] + return NOT_IMPLEMENTED_call()["NOT_YET_IMPLEMENTED_STRING"][ + "NOT_YET_IMPLEMENTED_STRING" + ][ + "NOT_YET_IMPLEMENTED_STRING" + ] # Edge case where a bug in a working-in-progress version of diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__expression__slice_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__expression__slice_py.snap new file mode 100644 index 0000000000..4fa305f243 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__expression__slice_py.snap @@ -0,0 +1,177 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +--- +## Input +```py +# Handle comments both when lower and upper exist and when they don't +a1 = "a"[ + # a + 1 # b + : # c + 2 # d +] +a2 = "a"[ + # a + # b + : # c + # d +] + +# Check all places where comments can exist +b1 = "b"[ # a + # b + 1 # c + # d + : # e + # f + 2 # g + # h + : # i + # j + 3 # k + # l +] + +# Handle the spacing from the colon correctly with upper leading comments +c1 = "c"[ + 1 + : # e + # f + 2 +] +c2 = "c"[ + 1 + : # e + 2 +] +c3 = "c"[ + 1 + : + # f + 2 +] +c4 = "c"[ + 1 + : # f + 2 +] + +# End of line comments +d1 = "d"[ # comment + : +] +d2 = "d"[ # comment + 1: +] +d3 = "d"[ + 1 # comment + : +] + +# Spacing around the colon(s) +def a(): + ... + +e00 = "e"[:] +e01 = "e"[:1] +e02 = "e"[: a()] +e10 = "e"[1:] +e11 = "e"[1:1] +e12 = "e"[1 : a()] +e20 = "e"[a() :] +e21 = "e"[a() : 1] +e22 = "e"[a() : a()] +e200 = "e"[a() :: ] +e201 = "e"[a() :: 1] +e202 = "e"[a() :: a()] +e210 = "e"[a() : 1 :] +``` + + + +## Output +```py +# Handle comments both when lower and upper exist and when they don't +a1 = "NOT_YET_IMPLEMENTED_STRING"[ + # a + 1 # b + : # c + 2 # d +] +a2 = "NOT_YET_IMPLEMENTED_STRING"[ + # a + # b + : # c + # d +] + +# Check all places where comments can exist +b1 = "NOT_YET_IMPLEMENTED_STRING"[ # a + # b + 1 # c + # d + : # e + # f + 2 # g + # h + : # i + # j + 3 # k + # l +] + +# Handle the spacing from the colon correctly with upper leading comments +c1 = "NOT_YET_IMPLEMENTED_STRING"[ + 1: # e + # f + 2 +] +c2 = "NOT_YET_IMPLEMENTED_STRING"[ + 1: # e + 2 +] +c3 = "NOT_YET_IMPLEMENTED_STRING"[ + 1: + # f + 2 +] +c4 = "NOT_YET_IMPLEMENTED_STRING"[ + 1: # f + 2 +] + +# End of line comments +d1 = "NOT_YET_IMPLEMENTED_STRING"[ # comment + : +] +d2 = "NOT_YET_IMPLEMENTED_STRING"[ # comment + 1: +] +d3 = "NOT_YET_IMPLEMENTED_STRING"[ + 1 # comment + : +] + + +# Spacing around the colon(s) +def a(): + ... + + +e00 = "NOT_YET_IMPLEMENTED_STRING"[:] +e01 = "NOT_YET_IMPLEMENTED_STRING"[:1] +e02 = "NOT_YET_IMPLEMENTED_STRING"[ : NOT_IMPLEMENTED_call()] +e10 = "NOT_YET_IMPLEMENTED_STRING"[1:] +e11 = "NOT_YET_IMPLEMENTED_STRING"[1:1] +e12 = "NOT_YET_IMPLEMENTED_STRING"[1 : NOT_IMPLEMENTED_call()] +e20 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() :] +e21 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() : 1] +e22 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() : NOT_IMPLEMENTED_call()] +e200 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() : :] +e201 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() :: 1] +e202 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() :: NOT_IMPLEMENTED_call()] +e210 = "NOT_YET_IMPLEMENTED_STRING"[NOT_IMPLEMENTED_call() : 1 :] +``` + + diff --git a/crates/ruff_python_formatter/src/trivia.rs b/crates/ruff_python_formatter/src/trivia.rs index 04a06dd404..955f77c93f 100644 --- a/crates/ruff_python_formatter/src/trivia.rs +++ b/crates/ruff_python_formatter/src/trivia.rs @@ -112,9 +112,8 @@ impl Token { self.range.start() } - #[allow(unused)] pub(crate) const fn end(&self) -> TextSize { - self.range.start() + self.range.end() } }